タグ別アーカイブ: データ分析

LME/GLMMで変量効果の相関を外すときのアレコレ

はじめに

線形混合モデル(LME)・一般化線形混合モデル(GLMM)で,変量効果をデザイン上で最も複雑なものにする最大モデルを作って,それでだめなら切片と傾きの間の相関パラメータを除いたモデルにトライする,という提案(Bates et al., 2015)をやろうとするとき,相関を除くのは”||”(double-bar syntax)なんですが,単に”|”を”||”に変えるだけじゃうまくいかないのどうすんだろうなと思っていたら解決策が見つかったぽいというお話です。

ちなみに,この方法はdata-drivenで探索的な側面があり,best practiceというわけではありません。

問題とは

具体的なコードとかは参考のところにも挙げたReduction of Complexity of Linear Mixed Models with Double-Bar Syntaxを見てください。

要するに,

m1 <- lmer (res ~ factorA + factorB + (1+factorA | ID), data =dat)

みたいになってるとして,factorAが因子型だとするじゃないですか。そのときに,「相関を外そう」ということで

m2 <- lmer (res ~ factorA + factorB + (1+factorA || ID), data =dat)

としてもうまくいかないよ,というお話です。これ,私もなんかうまくいかないなと思っていたんですが,解決策わからないので放置してました。「うまくいかない」というのはどういうことかというと,ただ単に”|”を”||”で置き換えるだけだと,切片とfactorAの第一水準(どういうコーディングしたかにもよりますけど)との相関パラメータは排除されるけれども,第一水準と第二水準の相関パラメータは残っちゃいますってことなんですね。でも,本当は全部の相関パラメータをなくしたいわけですね。

解決策

単純で,相関ありでfitさせたモデルの入った変数から,model.matrix()関数を使って変数名を直に取ってくる,ということです。m1という変数に相関ありのモデルが入ってるとすると,

model.matrix(m1)

とやってみます。そうすると,モデルの中に入ってる変数の列がガーッと出てきます。その中で,自分が使いたい列名をモデル式にいれる,ということですね。例えば,factorAが事前,事後,遅延テストという3つの水準からなっているとすると,

pre_post <- model.matrix(m1)[,2] #列番号は適当です
pre_del <- model.matrix(m1)[,3] #列番号は適当です

みたいな感じで一旦変数に代入した上で,

m2 <- lmer (res ~ factorA + factorB + (1+ pre_post + pre_del || ID), data =dat)

のようにさきほど代入した変数を使うと,”||”の指定によって相関パラメータがすべて除外されたモデルが作れるよ,ということのようです。

おわりに

詳しくは下記の参考記事を御覧ください。lme4長いこと使ってますが,知らないことまだまだありますね…。

なにをゆう たむらゆう。

おしまい。

参考記事

The Correlation Parameter in the Random Effects of Mixed Effects Models

Reduction of Complexity of Linear Mixed Models with Double-Bar Syntax

[R] 1列だけの選択ならselect関数よりpull関数を使う

はじめに

dplyr使いの方で,select関数を使って特定の1列に対して何かしらの処理をするということをしたいときにエラーが出る人はpull関数を使いましょうという話です。ちなみに,$使えば問題は解決です。今回は,それ以外の方法で,というお話。

具体的になにをしようとしたか

私がどんな処理をしようとしたかについて少し触れておきます。ここはスキップしてもらっても構いません。

ある列の単語(1単語または2単語の英単語が入っている)の文字数をカウントするということをやろうとしていました。ピリオドが含まれていたり(e.g., “dogs.”),2語の場合は間に空白もあるので(e.g., “beautiful lake”,それらを除去して文字数を数える必要があります。そのためには,str_extract,str_replace, str_lengthなどの文字列処理関数を使う必要があります。しかし,これらの関数はベクトルを受け取って処理をするので,データフレームを渡してもエラーが出てしまうという問題に直面しました。

select関数の挙動

データフレームからある特定の列を引っ張ってくるというとき,もちろん$を使って,dat$wordのようにするのが一番簡単なのでそれでもいいのですけども,dplyr使いの方はselect関数を使っている方も多いのではと思います。ただし,select関数はデータフレームの中から特定の列を引っ張ってくると,それがたとえ1列であってもdata.frame型になります。

よって,処理に使う関数がベクトル型を要求するものだとエラーが発生することになるわけです。しかもやっかいなのは,「じゃあas.vector()関数」を噛ませればいいやんとやってみてもそれでは解決しないからです。理由はよくわかりません。

  dat %>% 
  select(word) %>% #wordという列に文字数カウントしたい文字列があるとする
  as.vector() %>%
  is.vector() #このコードではFALSEとなります

pull関数がすること

pull関数は,要するにdat$x1のように$を使って,またはdat[, x1]のように[,]を使ってデータフレームの中の特定の列を指定するのと同じ挙動をするということです(参考)。よって,pull関数の出力はベクトル型になります。

  dat %>% 
  pull(word) %>%
  is.vector() #このコードではTRUEとなります

というわけで,1列だけ持ってくるときに関数を使う場合はpull()関数を使いましょうというのが結論です。私は結局こういう感じで文字数カウントをしました。

  dat %>% 
  pull(word) %>%
  str_extract(.,"[^\\.]+") %>% #ピリオドを除去
  str_replace(.,pattern = "\\s",replacement = "") %>% #半角スペースを除去
  str_length()->rt_dat$length #文字数をカウント

$を使うと次のようになります。

  str_extract(dat$word,"[^\\.]+") %>% #ピリオドを除去
  str_replace(.,pattern = "\\s",replacement = "") %>% #半角スペースを除去
  str_length()-> dat$length #文字数をカウントしてlengthという列に入れる

$を使ったほうが行数が少なくなるので,そっちのほうがいいような気もしますが,工程の見やすさでいうとpull関数を使うほうが上かなという感じがします。

おわりに

もともと文字数はカウントしてあったのですが,ちょっと査読者の指摘でフィラー項目も入れて分析をやり直さないといけなくなってこういった処理が必要になりました。データ型って基本といえば基本なのですが,以外に気づかずにエラーで困ることもあるので,select関数の挙動について勉強になるいい機会でした。

ということで,pull関数とselect関数,場合によって使い分けましょう。

なにをゆう たむらゆう。

おしまい。

[R] 初心者向け pivot_longer関数で縦型変換

はじめに

以前(というかかなり昔),tidyrやdplyrについての記事を書きました。

どちらの記事を書いたときからもだいぶ月日が経っていて,dplyrのアップデート等もあって色々とやり方が変わっているのですが,自分自身が研究で分析をする際にアップデートしていたことをブログ記事には反映させられていないので,上の2本の記事のアップデート版のようなことを書いておこうと思います。

まずはサンプルデータ

#事前・事後・遅延事後でCAFのデータを取ってるというデザイン
pre.fluency <-rnorm(50,mean=10,sd=2)
pre.accuracy<-rnorm(50,mean=7,sd=2)
pre.complexity<-rnorm(50,mean=4,sd=2)
post.fluency<-rnorm(50,mean=14,sd=4)
post.accuracy<-rnorm(50,mean=10,sd=3)
post.complexity<-rnorm(50,mean=7,sd=2)
delayed.fluency<-rnorm(50,mean=12,sd=2)
delayed.accuracy<-rnorm(50,mean=8,sd=1)
delayed.complexity<-rnorm(50,mean=6,sd=1)

#それぞれの列を横にくっつける
dat<-cbind(pre.fluency,pre.accuracy,pre.complexity,post.fluency,post.accuracy,post.complexity,delayed.fluency,delayed.accuracy,delayed.complexity)
dat<-as.data.frame(dat) #データフレーム型に変換
dat$subject<-rep(1:50) #実験参与者のID列をつける

とりあえずこんな感じでいまデータを持ってるとする

head(dat)
##   pre.fluency pre.accuracy pre.complexity post.fluency post.accuracy
## 1    9.344978     3.921626      4.5778491    13.932376      2.241629
## 2    9.126920     7.307178      3.2309289    13.624622     12.813440
## 3   13.433524     6.048270      5.0082008     7.932601     11.832851
## 4   11.937755     5.859142      0.6414096    11.289177      7.009059
## 5    7.913083     5.748651      2.6099159    17.933721     10.765706
## 6    8.683515     4.363661      5.4608116    16.550468     16.483160
##   post.complexity delayed.fluency delayed.accuracy delayed.complexity subject
## 1        9.381369       10.628923         6.929672           7.923673       1
## 2        4.485729       14.456886         9.048825           5.306266       2
## 3        7.534226       13.031400         7.144670           7.781451       3
## 4        8.076248       12.390157         9.247418           6.244443       4
## 5        8.702016        9.604130         7.530200           5.927892       5
## 6        8.973912        8.060271         8.188526           6.458383       6

縦横変換

昔の縦横変換は,gatherでやっていました。現在は,pivot_関数を使います。その名の通り,縦長(long型)にするのがpivot_longer関数で,横長にするのがpivot_wider関数です。

library(dplyr)
dat %>%
  tidyr::pivot_longer(cols = 1:9)
## # A tibble: 450 × 3
##    subject name               value
##      <int> <chr>              <dbl>
##  1       1 pre.fluency         9.34
##  2       1 pre.accuracy        3.92
##  3       1 pre.complexity      4.58
##  4       1 post.fluency       13.9 
##  5       1 post.accuracy       2.24
##  6       1 post.complexity     9.38
##  7       1 delayed.fluency    10.6 
##  8       1 delayed.accuracy    6.93
##  9       1 delayed.complexity  7.92
## 10       2 pre.fluency         9.13
## # … with 440 more rows

colsのところでどの列をまとめるかという指定をします。ここの指定は,上のやり方だと数字で列指定(1列目から9列目)としています。この部分はc()関数を使って文字列で指定してもいいですし,列指定のときに使えるstarts_with()やcontains() なんかもできます。例えば,まあこれはあくまで偶然そうなだけですけど,ここではまとめたい1列目から9列目はすべて”y”で終わっているので,以下のようにすることもできます。

dat %>% 
  tidyr::pivot_longer(cols = ends_with("y"))
## # A tibble: 450 × 3
##    subject name               value
##      <int> <chr>              <dbl>
##  1       1 pre.fluency         9.34
##  2       1 pre.accuracy        3.92
##  3       1 pre.complexity      4.58
##  4       1 post.fluency       13.9 
##  5       1 post.accuracy       2.24
##  6       1 post.complexity     9.38
##  7       1 delayed.fluency    10.6 
##  8       1 delayed.accuracy    6.93
##  9       1 delayed.complexity  7.92
## 10       2 pre.fluency         9.13
## # … with 440 more rows

また,まとめたくない列を”!“で指定することもできるので,今回のようにまとめないでほしい列が少ないという場合については,次のようにも出来ます。

dat %>% 
  tidyr::pivot_longer(!subject)
## # A tibble: 450 × 3
##    subject name               value
##      <int> <chr>              <dbl>
##  1       1 pre.fluency         9.34
##  2       1 pre.accuracy        3.92
##  3       1 pre.complexity      4.58
##  4       1 post.fluency       13.9 
##  5       1 post.accuracy       2.24
##  6       1 post.complexity     9.38
##  7       1 delayed.fluency    10.6 
##  8       1 delayed.accuracy    6.93
##  9       1 delayed.complexity  7.92
## 10       2 pre.fluency         9.13
## # … with 440 more rows

「まとめる」という感覚がいまいちよくわからないなぁとか,どの列を「まとめ」て,どの列は「まとめ」なくていいのかというのがピンと来ない場合には,次のように考えてください。

分析の際に従属変数(応答変数)となる列をまとめる

さて,今の段階ではまとめた際の列名が”name”になっていて,数値の部分が”value”という列名になっていますよね。ここも次のように指定できます。

dat %>% 
  tidyr::pivot_longer(!subject, names_to = "variable", values_to = "score")
## # A tibble: 450 × 3
##    subject variable           score
##      <int> <chr>              <dbl>
##  1       1 pre.fluency         9.34
##  2       1 pre.accuracy        3.92
##  3       1 pre.complexity      4.58
##  4       1 post.fluency       13.9 
##  5       1 post.accuracy       2.24
##  6       1 post.complexity     9.38
##  7       1 delayed.fluency    10.6 
##  8       1 delayed.accuracy    6.93
##  9       1 delayed.complexity  7.92
## 10       2 pre.fluency         9.13
## # … with 440 more rows

ただ,今回のケースではまとめた列に”pre”, “post”, “delayed”というテスト実施時期(test)という要因と,“complexity”, “accuracy”, “fluency”という測定値の要因が混在していますね。よって,あまり”name”の列名を変えることは意味がありません。むしろ,この列を分割してそれぞれの列に名前をつける必要があります。この列分割をseparate関数で行うという点は私が以前ブログ記事を書いた際と同じです。というわけで,次のようにします。

dat %>% 
  tidyr::pivot_longer(!subject) %>% 
  tidyr::separate(name, c("test","measure"), sep = "\\.")
## # A tibble: 450 × 4
##    subject test    measure    value
##      <int> <chr>   <chr>      <dbl>
##  1       1 pre     fluency     9.34
##  2       1 pre     accuracy    3.92
##  3       1 pre     complexity  4.58
##  4       1 post    fluency    13.9 
##  5       1 post    accuracy    2.24
##  6       1 post    complexity  9.38
##  7       1 delayed fluency    10.6 
##  8       1 delayed accuracy    6.93
##  9       1 delayed complexity  7.92
## 10       2 pre     fluency     9.13
## # … with 440 more rows

これで,完璧ですね。と思いきや…!実はpivot_関数には”name_sep”という便利な引数があります。これはどういう時に使うかと言うと,まとめた際に一つの列に複数の要因が混在してしまうときに,それを指定した区切り文字によって分割するために使います。まさに上で起こった問題ですよね。テスト実施時期と測定値が一緒の列になっていたのをseparate関数で分割したわけですが,なんとseparateを使わなくても縦型に変換する段階で分割までできてしまいます。

dat %>% 
  tidyr::pivot_longer(!subject, names_to = c("test", "measure"), names_sep = "\\.", values_to = "score")
## # A tibble: 450 × 4
##    subject test    measure    score
##      <int> <chr>   <chr>      <dbl>
##  1       1 pre     fluency     9.34
##  2       1 pre     accuracy    3.92
##  3       1 pre     complexity  4.58
##  4       1 post    fluency    13.9 
##  5       1 post    accuracy    2.24
##  6       1 post    complexity  9.38
##  7       1 delayed fluency    10.6 
##  8       1 delayed accuracy    6.93
##  9       1 delayed complexity  7.92
## 10       2 pre     fluency     9.13
## # … with 440 more rows

このときのポイントは2点あります。1つは”names_to”で列名を2つ指定すること。縦型変換の際に列の分割もするので,ここでのnames_toの指定は分割後の列名とします。もう一つは,区切り文字は正規表現を受け付けるということ。区切り文字がドット(.)なので,ここでnames_sep= “.”としてしまうと,正規表現におけるドットだと認識されてしまいます。これでは任意の1文字ですので,列名がうまく分割されずに以下のようになってしまいます。

dat %>% 
  tidyr::pivot_longer(!subject, names_to = c("test", "measure"), names_sep = ".", values_to = "score")
## Warning: Expected 2 pieces. Additional pieces discarded in 9 rows [1, 2, 3, 4,
## 5, 6, 7, 8, 9].
## # A tibble: 450 × 4
##    subject test  measure score
##      <int> <chr> <chr>   <dbl>
##  1       1 ""    ""       9.34
##  2       1 ""    ""       3.92
##  3       1 ""    ""       4.58
##  4       1 ""    ""      13.9 
##  5       1 ""    ""       2.24
##  6       1 ""    ""       9.38
##  7       1 ""    ""      10.6 
##  8       1 ""    ""       6.93
##  9       1 ""    ""       7.92
## 10       2 ""    ""       9.13
## # … with 440 more rows

したがって,正規表現ではありませんよということを追記する必要があります。このことをエスケープするなんて言いますが,Rにおけるエスケープは “\\”です。一般的にはエスケープは”\“ですが,R上では2つ重ねないといけないことに注意が必要です。

おわりに

とりあえず,long型に変換する作業をこの記事では説明しました。少し長くなってしまったので,記述統計を出すという話はまた別の記事にしたいと思います。

なにをゆう たむらゆう。

おしまい。

追記(2022.03.16)

従属変数を3列待ちにしたい(横長にしたい)

よく考えたら,分析するときはcomplexity, accuracy, fluencyの3つの列があったほうが便利ですよね。ということで,その形に変形させましょう。ここで,横長に変換するためのpivot_wider関数を使います。“names_from”の引数で「分解」したい列を指定します。ここでは,accuracy, complexity, fluencyの入っている“measure”の列を「分解」して横長にするので,“measure”を指定します。あとは,横長にしたときに持ってくる数値がどこに入っているかを“values_from”の引数で指定します。これは“score”に入ってますから,これを指定すればいいですね。というわけで,次のようになります。

dat %>% 
  tidyr::pivot_longer(!subject, names_to = c("test", "measure"), names_sep = "\\.", values_to = "score") %>% 
  tidyr::pivot_wider(names_from = "measure", values_from="score")
## # A tibble: 150 × 5
##    subject test    fluency accuracy complexity
##      <int> <chr>     <dbl>    <dbl>      <dbl>
##  1       1 pre       10.1     10.0        3.97
##  2       1 post      15.8      8.50       9.81
##  3       1 delayed   15.5      8.59       4.67
##  4       2 pre       12.0      6.40       1.03
##  5       2 post       6.90    10.1        7.30
##  6       2 delayed   15.0      8.54       7.70
##  7       3 pre        9.07     8.49       2.78
##  8       3 post       7.11    10.2        8.55
##  9       3 delayed   10.7      7.97       5.54
## 10       4 pre       12.5      6.03       2.26
## # … with 140 more rows

もしも,fluency, accuracy, complexityなどの順番が気になる場合は,select関数で指定してください。

dat %>% 
  tidyr::pivot_longer(!subject, names_to = c("test", "measure"), names_sep = "\\.", values_to = "score") %>% 
  tidyr::pivot_wider(names_from = "measure", values_from="score") %>%
  dplyr::select(subject, test, complexity, accuracy, fluency)
## # A tibble: 150 × 5
##    subject test    complexity accuracy fluency
##      <int> <chr>        <dbl>    <dbl>   <dbl>
##  1       1 pre           3.97    10.0    10.1 
##  2       1 post          9.81     8.50   15.8 
##  3       1 delayed       4.67     8.59   15.5 
##  4       2 pre           1.03     6.40   12.0 
##  5       2 post          7.30    10.1     6.90
##  6       2 delayed       7.70     8.54   15.0 
##  7       3 pre           2.78     8.49    9.07
##  8       3 post          8.55    10.2     7.11
##  9       3 delayed       5.54     7.97   10.7 
## 10       4 pre           2.26     6.03   12.5 
## # … with 140 more rows

この変換後のデータを別の変数に保存するのを忘れずに!

dat2 <- dat %>% 
  tidyr::pivot_longer(!subject, names_to = c("test", "measure"), names_sep = "\\.", values_to = "score") %>% 
  tidyr::pivot_wider(names_from = "measure", values_from="score") %>%
  dplyr::select(subject, test, complexity, accuracy, fluency)

head(dat2)
## # A tibble: 6 × 5
##   subject test    complexity accuracy fluency
##     <int> <chr>        <dbl>    <dbl>   <dbl>
## 1       1 pre           3.97    10.0    10.1 
## 2       1 post          9.81     8.50   15.8 
## 3       1 delayed       4.67     8.59   15.5 
## 4       2 pre           1.03     6.40   12.0 
## 5       2 post          7.30    10.1     6.90
## 6       2 delayed       7.70     8.54   15.0

[R] mutateとacrossでデータの下処理を少しだけエレガントに

まえおき

私はdplyrは5年前くらいから使っていて,自分が扱うようなデータについて自分がやりたいことを(その表現方法のエレガントさは別として)表現することはできていました。ただ,近年dplyrはアップデートを重ねていました。昔覚えたやり方でやろうとしても,その関数は使えませんとか,その表記方法は違いますとか言われることが増えました。分析の下処理でやりたいことは基本的に研究が変わらないので同じです。よって,過去に自分が書いたスクリプトのコピペをすることが多いわけです。それができなくなっていたと。

特に,最近の更新でacross()という関数が導入されたことが変更として大きいなと思います。まだまだこのacross()に関する記事も少なかったので,自分が使うにあたって覚えたことをメモ代わりに書いておきます。ここでは,mutate関数と一緒に使うケースです。つまり,ある特定の列について,ある処理を施して,その処理を施した列をデータフレームに追加するという作業です。単純に列に対して処理を施すだけというのは結構記事があったんですが,列を追加することについては全部列挙するみたいな方法しか見つかりませんでした。そこでacross関数の出番というわけですね。

やりたいこと

例えば,今やってる研究のデータでは,データフレームの中に頻度のデータが入っています。これをログ変換したいとします。すると,これまでは以下のように書いていました。

log(dat$ColFreq)->dat$ColFreq_log #コロケーション頻度
log(dat$AdjFreq)->dat$AdjFreq_log #コロケーション内の形容詞の頻度
log(dat$NounFreq)->dat$NounFreq_log #コロケーション内の名詞の頻度
log(dat$MIScore)->dat$MIScore_log #Mutual Information Score

別にコピペ&書き換えみたいなことをしながらやればいいし,これでだめだってことはないんですけど,複数の列について

  1. 同じ関数を適用
  2. 列を追加する

という同じ動作をしているわけなので,これは一気にできたほうが応用可能性があがります。私は手作業でやるの無理みたいな列数のデータを扱うことはないんですが,もし仮にそういうデータを扱う場合には何十行も使うのは好ましくないし無駄な作業だといえます。そこで列に対して処理を施して追加するという機能があるmutate関数と,それを複数列に適用する際に便利なacross関数を組み合わせます。

やりかた

ちなみに,なんだかんだでdplyrのパッケージのPDFが一番わかりやすかったです(pp. 3-6のacross関数のセクションとpp. 43-46のmutate関数のセクション)。across関数の引数は,列(.cols),関数(.fns),追加する列の名前(.names)という3つの引数があります。よって,今回のケースで言えば列のところで頻度情報が入ってる列を選択し,関数はlogを選べばOKです。ただ,.namesがないと情報を上書きしてしまいます。.namesのところは手書きで全部列名指定してやらなかんのかと思いましたが,そんなことはありません。”{.col}”を使えば,もとの列名を使えます。これにあとは自分で好きなタグのようなものをつけてあげればいいですね。”{.fn}”というのも使えて,これは使った関数名が入ります。

ということで,以下のようにすれば頻度情報にログ変換して列追加という作業ができます。

dat%>%
  mutate(across(c(ColFreq, AdjFreq, NounFreq, MIScore),log,.names = "{.col}_log"))->dat

.namesの部分は”{.col}_log”としていますが,”{.col}_{.fn}”でも同じです。dat$ColFreq_log, dat$AdjFreq_log,dat$NounFreq_log,dat$MIScore_logという4つの列が追加されます。ちなみに,列指定の部分は列の数値(e.g., 1, 2)でも可能です。頻度の情報が5~8列目にあるなら,次のように書くこともできます。ある特定の文字列が含まれる列を選ぶcontains()関数starts_with()関数とかも使えるはずです(こういうのは調べれば結構例があります)。

dat%>%
  mutate(across(5:8,log,.names = "{.col}_log"))->dat

ちょっと応用

さて,mutate関数とacross関数でたいぶすっきりしたコードを書くことができました。そこでふと,私がもう一ついつも下処理で複数列に適用する作業を思い出しました。それは,変数の標準化です。いつもなら次のようにしてました。

dat$z.oqpt <-scale(dat$oqpt)[,1] #Oxford Placement Testの点数の中心化
dat$z.rating <-scale(dat$rating)[,1] #評定値の中心化

これも別に2行だけなので大したことないんですが,やってることは先ほどのログ変換と同じですので,これもmutate関数を使って書き直してみましょう。次のようになります。

dat%>%
  mutate(across(c(oqpt,rating),scale,.names = "z.{.col}"))->dat

これでうまくいっているようにも見えますが,実はscale関数って出力された結果がベクトルではありません(データ型を調べるとmatrix型なのがわかります)。よって,大抵の場合は分析に問題はありませんが,あとで(私の場合だと分析結果の図示とか)ベクトル形式が求められる関数に渡した際に問題が発生することがよくあります。データフレームをただ眺めるだけではそのことはわからないので,次のように工夫してあげる必要があります。

dat%>%
  mutate(across(c(oqpt,rating),~scale(.x)[,1],.names = "z.{.col}"))->dat

さきほどと関数部分の書き方が変わっているのがわかると思います。このように”~”をつける書記法はpurrr-styleと呼ばれるそうですが,これは一般的には関数内の引数を指定する場合によく用いられます。例えば,~mean(.x, na.rm=T) のように使います。”na.rm=T”は欠損値は外して関数を適用するという設定のようなものです。今回は引数の設定ではなく,~scale(.x)[,1]としています。”[,1]”とすることで,行列の1つ目の要素(つまりこれは標準化された数値のベクトル)だけを出力してくれます。ちなみに,この方法で.namesに{.fn}をつかって次のようにすると,出力される列名はoqpt_1, rating_1のようになりました(理由は不明)。

dat%>%
  mutate(across(c(oqpt,rating),~scale(.x)[,1],.names = "{.col}_{.fn}"))->dat

おわりに

というわけで,改良されているんだろうけれども前のやり方に慣れてるこっちからしたらアップデートたびにコードを書き換えるのまじで面倒…って思っていたのですが,調べてみるとやっぱり便利でしたというお話でした。

またこういう系のことで新しく覚えたことがあれば記事に書こうと思います。

なにをゆう たむらゆう。

おしまい。

データを取る前に考えよう

ストレスが溜まっているのかなんなのか帰り道にセブン-イレブンでチーズとワインを買って飲むとかいう珍しいことをしているという言い訳を最初に書いておこうと思います。それから,自戒もたっぷり込めていることも申し添えておきたいと思います。

データ分析の方法の相談についてです。色んな所で起こっていることだと思うし色んな人がきっと同じようなことを考えているだろうと思います。一言でいうと,データを取る前に相談してみましょうということです。データを取ったあとに,「このデータってどうやって分析すればいいですか?」っていう質問は結構つらいところがあったりします(恐れ多くもなぜだかこういう質問をされる立場に私もなってしまいました)。なぜなら,本当にその質問者の方が調べたいこと,明らかにしたいことが,そのデータ収集方法で明らかにすることができるのかちょっと怪しかったりすることがあるからです。

そういう状況にならないために重要なことは,研究課題を見つけて,それを具体的な実験計画なりに落としこむ際に,その先のことまでちょっと考えてからデータ収集をしてみることだと思います。つまり,

こういう方法を使うと,こういうデータを収集することができる。それをこういう分析にかければ,こういう結果が得られるはず。もしこういう結果が得られた場合,それはこういうことを意味していると解釈できるだろう。

みたいなことを考えてから実験を組んで,データを取っていただきたいなということです。そうすれば,データを取ってからどうしたらいいかという質問が来ることはなくなるからです。上述の事でイマイチ自分がうまく説明できないとなったら,その時点で相談をすればよいと思います。そうすれば,実はその方法で得られたデータで,質問者の方の主張をするのはちょっと違うので,こういうデータ収集をしないといけませんよ,なんてアドバイスができたりするかもしれないからです。これって仮説検証型の研究でも探索的な研究でも同じだと思います。たとえ探索的な研究だったとしても,得られた結果に対してなんらかの解釈を加える必要はあるはずですから。研究課題がwh疑問文で始まる形であったとしても,結果に対しては,きっとこういう理由でこうだったのだろうということを言えなければいけないわけですし。

データをとって,分析をしてみて,これであってるのか不安だから相談してみようということもあるのかもしれませんが,それって別にデータ取る前にもできることなんですよね。

こういう研究課題をもっていて,これを明らかにするためにこういう分析をしてこういう結果が得られたらこういう解釈ができると思うのですが,これで合ってますか?

ってデータ取る前に相談すればすむと思うのです。なのにどうして,「データを取ったあとにこれってどうやって分析すればいいんですか?」ってなってしまうのでしょう。

これはあくまで持論なのですが,研究って,研究のデザインとか実験計画ですべて決まると私は思っています。面白いなと思う研究はやっぱりデザインがしっかりしていて,分析の結果から得られたことの解釈がとってもシンプル(注1)なんです(そういう研究が良い研究だと思う私からすると,discussionを長々と書いている論文やそれが良しとされる風潮,また「discussionが短い」みたいなコメントってなんだかなと思ったりします)。それって,デザインを練る段階で色んなことを考えているからなんですよね。研究の計画を立てる段階で,データの分析方法まで明確にイメージできない場合は,データ収集に入るべきではないと私は考えています。私の場合は基本的に,ある研究で行われることを考えることは,R上で分析を行う際のデータセットの形をイメージすることとほぼ同義です。変数のデータフレームがイメージできていて,それをどうやって分析すれば何をどうしていることになるのか,こういう結果になったらそれはどう解釈するのか,また別の結果がでたらそれをどう解釈するのかまで考えられて初めて研究のデータ収集ができるのではないでしょうか。

こういった考えが当たり前になれば「統計屋さん」(私は違いますけど)とか言われる人たちもずいぶん楽になるのではないかなと思っています。

なにをゆう たむらゆう

おしまい。

*注1: だからといって,t検定一発で明らかになることってそんなにはないとは思います。少なくとも今なら昔はt検定一発だったこともGLMMするでしょう。例えば正答率の平均値をt検定というのはもはや時代遅れになりつつあるわけですし。