はじめに

 解析前のデータ処理は、量的研究・統計解析を行う上で必須である。しかし統計解析についての本は多々あるが、解析前のデータ処理を扱う講義や本は意外と少ないように感じる。そのためデータがあって想定している解析もあるのに、なかなか解析に手がつかない、という状況は非常によくある(自分はそうだった)。そういう反省も踏まえて、Rによる解析前のデータ処理の内容を網羅的にまとめてみた。

1. 疑似データの作成

set.seed(218)
dt <- data.frame(ID=1:5, #対象者番号
                 age=round(rnorm(5, 30, 10)), #年齢
                 gender=c("男","男","女","女",NA), #性別
                 blood_type=c("A","B","AB","O","A"), #血液型
                 test=c("不可","可","良","優","可")) #テストの成績
knitr::kable(dt)
ID age gender blood_type test
1 42 A 不可
2 14 B
3 28 AB
4 41 O
5 13 NA A

2. データフレームの変数の指定

2-1 変数(列)の指定

 1つの変数(1列)を指定する方法は3つある。方法③はデータフレームとして指定するため、表示形式が異なる

方法① 列ベクトルとしての指定

dt$age
dt[,1]

方法② データフレームとしての指定

dt[1]  #1列だけのデータフレームとして表示
##   ID
## 1  1
## 2  2
## 3  3
## 4  4
## 5  5

2-2 繰り返し処理(複数列の指定)

 for文と組み合わせることで、複数列に対する処理を行うことができる。またsapply関数により、全ての変数に対する処理も実行できる。

for(i in 1:2){table(dt[,i])}
sapply(dt, table)
## $ID
## 
## 1 2 3 4 5 
## 1 1 1 1 1 
## 
## $age
## 
## 13 14 28 41 42 
##  1  1  1  1  1 
## 
## $gender
## 
## 女 男 
##  2  2 
## 
## $blood_type
## 
##  A AB  B  O 
##  2  1  1  1 
## 
## $test
## 
##   可 不可   優   良 
##    2    1    1    1

3. データチェック

 データの分布、欠測の数を把握する場合、まずはtable関数で確認する。これも指定方法によりいくつかの方法があるが、いずれも同じ結果を示す。

table(dt$gender, exclude=NULL)  #exclude=NULLで欠測をカウント
table(dt[,2], exclude=NULL)
table(dt[2], exclude=NULL)
## 
## 13 14 28 41 42 
##  1  1  1  1  1

全ての変数のデータチェック

sapply(dt, table, exclude=NULL)
## $ID
## 
## 1 2 3 4 5 
## 1 1 1 1 1 
## 
## $age
## 
## 13 14 28 41 42 
##  1  1  1  1  1 
## 
## $gender
## 
##   女   男 <NA> 
##    2    2    1 
## 
## $blood_type
## 
##  A AB  B  O 
##  2  1  1  1 
## 
## $test
## 
##   可 不可   優   良 
##    2    1    1    1

4. カテゴリー変数の定義

 Rのデータ形式の一つであるデータフレームでは、カテゴリー変数を①文字型、②FACTOR型の2つで表現する。FACTOR型は数値として扱われるため、解析を行う場合には便利な形式である。

4-1 数値カテゴリーの定義

 カテゴリー変数は省略された文字で表現されることが多い。例えば、血液型は以下のようにA、B、AB、Oの文字だけで表現されることもある。このような文字だけでは分かりづらいこともあるため、FACTOR型のlabels属性を付与することでデータがより見やすくなる(指定しなくても良い)。またlevels属性は割り当てる水準(数値)を指定する。(FACTOR型はlabelsとlevelsの2つの属性をもつ)

dt$blood_typef <- factor(dt$blood_type,
                        levels=c("A","B","AB","O"), #水準数の定義
                        labels=c("A型","B型","O型","AB型")) #ラベル定義
dt_blood <- data.frame(dt$blood_type,dt$blood_typef)
knitr::kable(dt_blood)
dt.blood_type dt.blood_typef
A A型
B B型
AB O型
O AB型
A A型

4-2 順序カテゴリーの定義

 順序変数の定義にはfactor関数にordered=TRUEオプションを指定するだけでよい。これで指定順に値が大きくなることが表現される。これにより比較演算子による値の比較ができるようになる。

dt$test <- factor(dt$test, 
                  levels=c("不可","可","良","優"), 
                  ordered=TRUE) #順序の定義
dt$test>="可" #可、良、優以上の者
## [1] FALSE  TRUE  TRUE  TRUE  TRUE

4-3 カテゴリーの定義順序の変更

 FACTOR型を扱う場合、整数が辞書順に割り当てられる。例えば、男と女はそれぞれ一般的に1、2として割り振られる事が多い。しかしながらRでは、男を2、女を1として数値を割り当てることに注意しなければならない。これに気づかないとデータの解釈が男女で逆の結果となり、目が当てられないことになる。  

unclass(dt$gender) 
## [1]  2  2  1  1 NA
## attr(,"levels")
## [1] "女" "男"
# 変数genderに割り当てられた整数の確認
# 女=1、男=2となっていることを確認
dt$gender <- factor(dt$gender,levels=c("男","女"),labels=c("男性","女性"))
unclass(dt$gender)
## [1]  1  1  2  2 NA
## attr(,"levels")
## [1] "男性" "女性"

男=1、女=2となっていることを確認する。これは特に順序変数の順序変更に有用。

5. 連続変数のカテゴリー化

 統計解析では解釈のしやすさのために、連続変数をカテゴリー化することがよくある。そこでcut関数により、①カテゴリーとして分割する値、②ラベル名の指定、をすることでカテゴリー化できる。以下では順序変数に変換している。

dt$agec <- cut(dt$age, 
               breaks=c(-Inf,20,30,40,50,60,70,Inf), #分割点の定義(7カテゴリー)
               right=FALSE,  #区間の下限点が区間に含まれ、上限が含まれないように指定
               labels=c("20歳未満","20~30歳","30~40歳","40~50歳","50~60歳",
                        "60~70歳","70歳以上"),
               ordered_result = TRUE) 
dt_age <- data.frame(dt$age,dt$agec)
knitr::kable(dt_age)
dt.age dt.agec
42 40~50歳
14 20歳未満
28 20~30歳
41 40~50歳
13 20歳未満

6. カテゴリー変数の最カテゴリー化

細かくカテゴリー化された変数をより少ないカテゴリーに変換する。levels関数で元の水準を新たに定義する。

levels(dt$agec)
## [1] "20歳未満" "20~30歳"  "30~40歳"  "40~50歳"  "50~60歳"  "60~70歳"  "70歳以上"
x <- dt$agec
levels(x) <- c("30歳未満","30歳未満","30歳以上","30歳以上","30歳以上","30歳以上","30歳以上")
table(dt$agec,x) #カテゴリー化されているかの確認
##           x
##            30歳未満 30歳以上
##   20歳未満        2        0
##   20~30歳         1        0
##   30~40歳         0        0
##   40~50歳         0        2
##   50~60歳         0        0
##   60~70歳         0        0
##   70歳以上        0        0

7. 新しい変数の作成

 連続変数の数値を加工し新たな変数を作成する。以下ではmean()とvar()を使用し、ageを標準化したage_sdを作成する。

dt$age_sd <- (dt$age-mean(dt$age))/var(dt$age) #ageを標準化したage_sdを作成
dt_age <- data.frame(dt$age,dt$age_sd)
knitr::kable(dt_age)
dt.age dt.age_sd
42 0.0733571
14 -0.0692817
28 0.0020377
41 0.0682629
13 -0.0743760

 またtransform関数でも同様に変数を作成できる。

dt <- transform(dt, age_sd=(age-mean(age))/var(age))

8. 新しいデータフレーム(サブセット)の作成

 実際のデータ処理では、データ量を減らしたり目的の解析を行うため、サブセット(指定した条件を満たすデータ)を作成することが多い。特に研究では、欠測の除外、閾値を超える対象者の絞り込み、性別などのカテゴリ別のデータ作成、などサブセットを作成する過程が非常に多い。

8-1 指定した変数のサブセット作成

 特定の変数のみを含むサブセットを作成する。主にデータ量を減らしたり、データを見やすくする目的で行う。

dt1 <- dt[1,2]  #1,2列目変数を含むデータの作成
dt1 <- data.frame(dt$age,dt$gender,dt$blood_type,dt$test)
# age,gender,blood_type,testを含むデータの作成

8-2 条件を満たすサブセットの作成

 特定の連続変数に対して、比較演算子による条件を満たしたサブセットを作成する。

dt2 <- subset(dt,age>20) #20歳以上の者だけのデータ作成

8-3 欠測値の除外

 欠測の除外には以下の2つの方法がある。

dt3 <- subset(dt,complete.cases(dt))  #一つでも欠測のある者を除外
dt3 <- na.omit(dt)

9. データフレームの分割

 カテゴリー別の解析も研究ではあるある。男女別などカテゴリー別にデータを作成したい場合、split関数でデータフレームを分割できる。そこから男女別のデータを作成する。

dt_gender <- split(dt,dt$gender) #男女別のリストを含むデータの作成
dt_man <- dt_gender$男 #男のみのデータ作成
dt_woman <- dt_gender$女 #女のみのデータ作成

10. データフレームの結合

 データをいじっていると複数のデータを結合したくなる場合がある。追加の対象者データを元データに統合したい場合は縦結合、同じ対象者の異なる変数のデータを追加したい場合は横結合を行う。

10-1 縦結合

 結合するデータに同じ変数が含まれていれば、以下の記述で結合が可能。

dt_bind <- rbind(dt_man,dt_woman) #同じ変数(列数)をもつデータフレームを結合
knitr::kable(dt_bind)
ID age gender blood_type test blood_typef agec age_sd
1 42 男性 A 不可 A型 40~50歳 0.0733571
2 14 男性 B B型 20歳未満 -0.0692817
3 28 女性 AB O型 20~30歳 0.0020377
4 41 女性 O AB型 40~50歳 0.0682629

10-2 横結合

 結合するデータに同じ対象者(行数)があれば、以下の記述で結合が可能。

dt_df <- data.frame(dt_man,dt_woman) #同じデータ数(行)をもつデータフレームを結合
knitr::kable(dt_df)
ID age gender blood_type test blood_typef agec age_sd ID.1 age.1 gender.1 blood_type.1 test.1 blood_typef.1 agec.1 age_sd.1
1 42 男性 A 不可 A型 40~50歳 0.0733571 3 28 女性 AB O型 20~30歳 0.0020377
2 14 男性 B B型 20歳未満 -0.0692817 4 41 女性 O AB型 40~50歳 0.0682629

10-3 柔軟な縦結合

 データ間の対象者数が異なったり、異なる変数データを元データに結合したい場合、上記の方法では結合できない。そのような場合はmerge関数を使用する。デフォルトでは同じ列名を探し、縦に結合する。以下ではIDが3~5の対象者の血圧データを元のデータに追加してみる。

dt_add <- data.frame(ID=3:5,bp=c(140,151,132))
dt_merge <- merge(dt[1:2],dt_add,all=TRUE,by="ID") 
knitr::kable(dt_merge)
ID age bp
1 42 NA
2 14 NA
3 28 140
4 41 151
5 13 132

 all=TRUEを指定しないと、共通ID以外が結合されない。そのため以下では、元データと追加データの共通IDである、IDが3~5の対象者のみが結合されている。したがってよほどのことが無い限り、all=TRUEを指定すべき。

dt_merge1 <- merge(dt[1:2],dt_add,by="ID") 
knitr::kable(dt_merge1)
ID age bp
3 28 140
4 41 151
5 13 132
merge関数 内容
x,y データフレーム名
by=列名    結合のために参照する列名の指定
by.x(by.y)=列名 結合のために参照する列名の指定(xとyで異なる列名でも可)
all=T/F 共通ID以外も結合する(FALSEで共通ID以外結合されない)
all.x(all.y)=T/F どちらのデータを残すかの指定(all.x:左結合、all.y:右結合)

11. 並び替え

 ソートで有名なのはorder関数。指定した変数で昇順ソートし、行番号を返してくれる。その後得た行番号のオブジェクトをデータフレームの第1引数(行番号)に渡すことで、対象データをソートできる。

sort_test <- order(dt$test) #ソートしたときの行番号をオブジェクトに格納
dt_order <- dt[sort_test,]  #元データをテスト成績でソート(dt[行,列]による)
dt_order
##   ID age gender blood_type test blood_typef     agec       age_sd
## 1  1  42   男性          A 不可         A型  40~50歳  0.073357106
## 2  2  14   男性          B   可         B型 20歳未満 -0.069281712
## 5  5  13   <NA>          A   可         A型 20歳未満 -0.074375955
## 3  3  28   女性         AB   良         O型  20~30歳  0.002037697
## 4  4  41   女性          O   優        AB型  40~50歳  0.068262863

 因みにデータを見ると元々の行番号が表示されている。行番号を変更したい場合は以下のようにする。

rownames(dt_order) <- c(1:nrow(dt_order))
dt_order
##   ID age gender blood_type test blood_typef     agec       age_sd
## 1  1  42   男性          A 不可         A型  40~50歳  0.073357106
## 2  2  14   男性          B   可         B型 20歳未満 -0.069281712
## 3  5  13   <NA>          A   可         A型 20歳未満 -0.074375955
## 4  3  28   女性         AB   良         O型  20~30歳  0.002037697
## 5  4  41   女性          O   優        AB型  40~50歳  0.068262863

12. リストとデータフレームの相互変換

 分析方法によってはリストとデータフレームを相互に変換することが必要な場合がある。最も簡単なのはas.list関数とas.data.frame関数。

list <- as.list(dt)  #変数ごとのベクトルをもつリストの作成
dt_dataframe <- as.data.frame(list)  #リストからデータフレームに変換

 またstack関数によってリストからデータフレームに変換することも可能。

list1 <- list(Man=c(30,40,50),Woman=c(30,41,46))
list1
## $Man
## [1] 30 40 50
## 
## $Woman
## [1] 30 41 46
dt_gender <- stack(list1)
dt_gender
##   values   ind
## 1     30   Man
## 2     40   Man
## 3     50   Man
## 4     30 Woman
## 5     41 Woman
## 6     46 Woman

13. 対応のあるデータの転置

 経時測定データを扱う場合、各列でそれぞれの時点の測定値を示している場合がある。しかしこの形式では統計解析を行うことはできない。解析を行うためには、各測定時点の値を一つの変数にまとめる必要がある(データの転置)。

dt_time <- data.frame(ID=1:5,   #血圧の経時データの作成
                      week.1=round(rnorm(5,140,10)),
                      week.2=round(rnorm(5,135,10)),
                      week.3=round(rnorm(5,130,10)))
knitr::kable(dt_time)
ID week.1 week.2 week.3
1 130 131 142
2 127 137 126
3 133 147 148
4 120 134 146
5 134 146 130

 データの転置で便利なのはreshape関数。stack関数よりも柔軟なデータ編集が可能。以下ではlong形式のデータからwide形式のデータに変換している。各時点の変数名が“変数名.数値”でないと転置できないことに注意。

dt_time1 <- reshape(dt_time,idvar="ID",
                    varying = 2:4,direction="long",
                    v.names="Week")
knitr::kable(dt_time1)
ID time Week
1.1 1 1 130
2.1 2 1 127
3.1 3 1 133
4.1 4 1 120
5.1 5 1 134
1.2 1 2 131
2.2 2 2 137
3.2 3 2 147
4.2 4 2 134
5.2 5 2 146
1.3 1 3 142
2.3 2 3 126
3.3 3 3 148
4.3 4 3 146
5.3 5 3 130

 またlongからwideへ、逆の形式への変換も可能。timevar引数で時点を示す変数を指定する(week1~3を含むtime変数)。

dt_time2 <- reshape(dt_time1,idvar="ID",timevar="time",direction="wide")
knitr::kable(dt_time2)
ID Week.1 Week.2 Week.3
1.1 1 130 131 142
2.1 2 127 137 126
3.1 3 133 147 148
4.1 4 120 134 146
5.1 5 134 146 130
reshape関数 内容
data データフレーム名
idvar=識別変数 対象者やデータを識別するための変数の指定
varying=転置したい変数 転置する変数の指定(ID以外の各時点の変数:“列名.数値”)
direction=“long”“wide” 作成する形式の指定(long:各時点が1列、wide:各時点が複数列)
v.names=列名 widw形式からlong形式への変更で作成される変数名の指定
timevar=時点を示す変数 long→wideで転置する変数を指定(時点変数)

14. クロス集計表の作成と元データの復元

 カテゴリカルなアウトカムを含むデータの場合、クロス集計表の作成は必須。xtabs関数、table関数でクロス集計表を生成することができる。まずは単純な2変数の度数集計から。

ct1 <- xtabs(~gender+test, data = dt) #性別とテスト成績の度数集計
      # table(dt$gender,dt$test)も同じ結果
knitr::kable(ct1)
不可
男性 1 1 0 0
女性 0 0 1 1

 ~は等号=と同じであり、度数=性別+成績という方程式を表現している。因みに~の右辺に変数を加えると、3次元以上のクロス集計が可能(使うことはほとんどない)。

 またクロス集計から元データの復元には、data.frame関数でデータフレームに変換するのが分かりやすい。

df1 <- data.frame(ct1)
knitr::kable(df1)
gender test Freq
男性 不可 1
女性 不可 0
男性 1
女性 0
男性 0
女性 1
男性 0
女性 1

 しかしこれだけでは、Freqという変数が邪魔であるし、Freq=0の行も除きたいところ。そこでdata.frameとlapplyにより、邪魔なFreqの列とFewq=0の行を除外する。

data.frame(lapply(df1, function(i) rep(i,df1[, "Freq"]))) [-3]
##   gender test
## 1   男性 不可
## 2   男性   可
## 3   女性   良
## 4   女性   優
# function(i)で行数をカウント、rep関数でFreqの数値分対象行を繰り返す(Freq=0の除外)
# df1[, "Freq"]はFreqという名前の列にある数値を指定
# [-3]で3列目の変数を除外