複数シート、データのマージ、欠損値

理解を深めるために、データ数20レコードにおいて欠損値Non Avairable value)を含む例を取り上げて解説する。 その都度、データセットの内容を確認して作業することを強く勧める。

複数のシートデータを扱う

Excel等の表計算ソフトウエアで作成したデータでは、複数のシートに記載されたデータが1つのファイルとなっていることがある(たしかに関連データの散逸を防ぐなど便利なことがある)。 パッケージ gregmisc の関数 read.xls( ) を使うとExcelのシートを指定して直接データを読み込むことができる。 しかし、日本語データの読取に問題が発生することがある。 確実なのは、各シートごとにCSV形式でシートデータを保存してRに読み込むことである。

問題 sheet-na-1: 成績データが2つのシートに分割されたファイル sheets-data.xls をダウンロードして、各シートデータをCSV形式で保存しよう。 英語(Eng)、国語(Lang)、数学(Math)の基本科目データを basic.csv、歴史(Hist)、物理(Phys)、生物(Bio)の選択科目データを selective.csv として保存せよ。
問題 sheet-na-2: 基本科目データ basic.csv を Basic として、選択科目データ selective.csv をSelect としてRに読み込め。

[ヒント]: それぞれのシートデータを観察して、列ラベル名からの行データをデータフレームとして読み込む。

> Basic <- read.csv(file = "basic.csv", header = TRUE, skip = 5)
> Select <- read.csv(file = "selective.csv", header = TRUE, skip = 3)

データセットの結合

Rでは、入力されていないフィールドを示すために欠損値(Non Avairable)として NA と記載される。 読み込んだデータセット Basic と Select の内容を観察してみよう。

基本科目データ Basic では20人分全員の「完全な」レコードがあるが、選択科目データ Select では学生番号 a129 のレコードは記載されていないことに注意。 実際、次のようにBasic は20行、Select は19行のデータであることが確かめられる。

> Basic
   Stnum Sex Eng Lang Math
1   a111   M  68   76   71
2   a112   F  38   62   33
3   a113   M  NA   62   88
4   a114   F  62   77   52
5   a115   F  82   69   93
6   a116   F  44   72   57
7   a117   F  48   72   66
8   a118   M  47   64   NA
9   a119   M  77   77   55
10  a120   M  89   NA   75
11  a121   M  51   76   57
12  a122   F  61   65   45
13  a123   M  73   81   58
14  a124   F  48   84   56
15  a125   F  NA   70   67
16  a126   M  74   76   51
17  a127   M  57   53   25
18  a128   M  47   74   81
19  a129   F  84   78   85
20  a130   M  68   82   74
> Select
   Stnum Hist Phys Bio
1   a111   90   NA  85
2   a112   NA   78  94
3   a113   78   80  NA
4   a114   85   NA  75
5   a115   NA   85  80
6   a116   68   84  85
7   a117   74   NA  75
8   a118   82   84  NA
9   a119   NA   95  85
10  a120   58   NA  60
11  a121   NA   68  95
12  a122   70   84  90
13  a123   75   NA  80
14  a124   NA   60  75
15  a125   59   92  NA
16  a126   85   80  NA
17  a127   NA   76  95
18  a128   90   NA  75
19  a130   94   NA  NA

データフレーム Basic と Select は共通の識別ラベルである Stnum(学生番号)をキーとしてを使い、次のようにして合併することができる。

合併データ Exam0 を表示してわかるように、どちらかのデータフレームで欠けているレコードがあれば、合併後のデータフレームでも存在しないことに注意。 つまり、学生番号 a129 の行データ(レコード)は Exam0 には存在しない。

> Exam0 <- merge(Basic, Select, by = "Stnum")
> Exam0
   Stnum Sex Eng Lang Math Hist Phys Bio
1   a111   M  68   76   71   90   NA  85
2   a112   F  38   62   33   NA   78  94
3   a113   M  NA   62   88   78   80  NA
4   a114   F  62   77   52   85   NA  75
5   a115   F  82   69   93   NA   85  80
6   a116   F  44   72   57   68   84  85
7   a117   F  48   72   66   74   NA  75
8   a118   M  47   64   NA   82   84  NA
9   a119   M  77   77   55   NA   95  85
10  a120   M  89   NA   75   58   NA  60
11  a121   M  51   76   57   NA   68  95
12  a122   F  61   65   45   70   84  90
13  a123   M  73   81   58   75   NA  80
14  a124   F  48   84   56   NA   60  75
15  a125   F  NA   70   67   59   92  NA
16  a126   M  74   76   51   85   80  NA
17  a127   M  57   53   25   NA   76  95
18  a128   M  47   74   81   90   NA  75
19  a130   M  68   82   74   94   NA  NA

合併時における欠損レコードの扱い

上の例のように、マージされる前のいずれかのデータセットで欠けているレコードが、マージされたときも存在しないのは、 関数 marge( )オプションがデフォルトで all が FALSE がセットされているためである。

オプション all = TRUE を指定して次のようにすれば、いずれかのデータセットでレコードが存在していれば、マージ後の欠損箇所が NA で埋められて結合される。

> Exam <- merge(Basic, Select, by = "Stnum", all = TRUE)
> Exam
   Stnum Sex Eng Lang Math Hist Phys Bio
1   a111   M  68   76   71   90   NA  85
2   a112   F  38   62   33   NA   78  94
3   a113   M  NA   62   88   78   80  NA
4   a114   F  62   77   52   85   NA  75
5   a115   F  82   69   93   NA   85  80
6   a116   F  44   72   57   68   84  85
7   a117   F  48   72   66   74   NA  75
8   a118   M  47   64   NA   82   84  NA
9   a119   M  77   77   55   NA   95  85
10  a120   M  89   NA   75   58   NA  60
11  a121   M  51   76   57   NA   68  95
12  a122   F  61   65   45   70   84  90
13  a123   M  73   81   58   75   NA  80
14  a124   F  48   84   56   NA   60  75
15  a125   F  NA   70   67   59   92  NA
16  a126   M  74   76   51   85   80  NA
17  a127   M  57   53   25   NA   76  95
18  a128   M  47   74   81   90   NA  75
19  a129   F  84   78   85   NA   NA  NA <-- 欠損行があっても、NAで埋められて結合される
20  a130   M  68   82   74   94   NA  NA

欠損値のある平均

列データの取得で紹介したように、 結合データフレーム Exam の英語列データは、記号 $ を使って、Exam$Eng で取り出すことができる。 今の例の場合、そこに欠損値 NA を2個含んでいる。

> Exam$Eng
 [1] 68 38 NA 62 82 44 48 47 77 89 51 61 73 48 NA 74 57 47 84 68

欠損値を含んでいるデータ並び(ベクトル)の平均値を関数 mean( ) で次のように単純に求めようとすると、うまくいかない(NAが返る)。

> mean(Exam$Eng)
[1] NA

ただし、欠損値 NA がなかったことにして平均値や合計を求めることはできる。 次のような簡単な例を試してみよう。

ベクトル(データの並び)として、2つの欠損値 NA を含む6つのデータ(値があるのは1,2,3,5の4つのデータ)を x とする。 この x の平均を mean( ) で平均値 mean(x) を求めようとすると NA が返って失敗する。 しかし、次のようにオプション na.rm=TRUE を付けて関数 mean( ) を使うと、平均値(1+2+3+5)/4=2.75 を計算できることがわかる。 na.rm は Non Avairable値を除外する(reMove)の意である。

> x <- c(1, 2, 3, NA, 5, NA)
> x
[1]  1  2  3 NA  5 NA
> mean(x)
[1] NA
> mean(x, na.rm = TRUE)
[1] 2.75
問題 sheet-na-3: 関数 mean( ) でオプション na.rm = TRUE を使って、 英語(Eng)、国語(Lang)、数学(Math)、歴史(Hist)、物理(Phys)、生物(Bio)のクラス平均値を求めよ。

[ヒント]:

> mean(Exam$Eng, na.rm = TRUE)
[1] 62.11111
問題 sheet-na-4: 行列データの各列の平均値を一気に返す関数 colMeans( ) でもオプション na.rm=TRUE が使える。 関数 colMeans( ) を使って、 英語(Eng)、国語(Lang)、数学(Math)、歴史(Hist)、物理(Phys)、生物(Bio)のクラス平均値を求めよ。

[ヒント]:

> colMeans(Exam[,3:8], na.rm = TRUE)

欠損値の判定 is.na( )

Rでは、欠損値があっても、それをを除外して平均や合計などを計算することができるので便利である。 しかし、計算結果が得られたとしても、除外した欠損値の個数がどの程度あるのかについては常に注意をはらう必要がある。 ほとんどのデータがNSの場合に、平均値を求めても意味があるのかどうかは一考に値するからだ。

データが欠損値(NA)であるかどうかを判定する関数が is.na( ) だ。 NA のとき TRUE、NA でないとき FALSE を返す。 否定演算子 !を付けると、結果は反転する。

> is.na(Exam$Eng)
 [1] FALSE FALSE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[13] FALSE FALSE  TRUE FALSE FALSE FALSE FALSE FALSE
> !is.na(Exam$Eng)
 [1]  TRUE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
[13]  TRUE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE

関数 table( ) は値に応じた分布表を返す。 次にようにすれば、クラスの英語データ Exam$Eng のNAの数は2個あることが分かる。

> table(!is.na(Exam$Eng))

FALSE  TRUE 
    2    18 

colMeans( )と rowMeans( )

2次元データセットに対して、関数 colMeans( ) は各列の平均値を、関数 rowMeans( ) は各行の平均値を計算する。

データフレーム Exam の3列から8列までの連続する列からなる2次元データの各列の NA値を除いた平均値は次で求めることができる。

> colMeans(Exam[ ,3:8], na.rm = TRUE)
     Eng     Lang     Math     Hist     Phys      Bio 
62.11111 72.10526 62.57895 77.53846 80.50000 82.07143

データフレーム Exam の3列から8列までの連続する列からなる2次元データの各行の NA値を除いた平均値は次のように求めることができる。

Exam$Ave <- ... において、個人の科目平均値をデータフレーム Exam に新しくラベル Ave を定義し、Ave列に個人平均をセットしていることに注意する。

 
> rowMeans(Exam[ ,3:8], na.rm = TRUE)
 [1] 78.00000 61.00000 77.00000 70.20000 81.80000 68.33333 67.00000 69.25000
 [9] 77.80000 70.50000 69.40000 69.16667 73.40000 64.60000 72.00000 73.20000
[17] 61.20000 73.40000 82.33333 79.50000
> Exam$Ave <- rowMeans(Exam[ ,3:8], na.rm = TRUE)
> Exam
   Stnum Sex Eng Lang Math Hist Phys Bio      Ave
1   a111   M  68   76   71   90   NA  85 78.00000
2   a112   F  38   62   33   NA   78  94 61.00000
3   a113   M  NA   62   88   78   80  NA 77.00000
4   a114   F  62   77   52   85   NA  75 70.20000
5   a115   F  82   69   93   NA   85  80 81.80000
6   a116   F  44   72   57   68   84  85 68.33333
7   a117   F  48   72   66   74   NA  75 67.00000
8   a118   M  47   64   NA   82   84  NA 69.25000
9   a119   M  77   77   55   NA   95  85 77.80000
10  a120   M  89   NA   75   58   NA  60 70.50000
11  a121   M  51   76   57   NA   68  95 69.40000
12  a122   F  61   65   45   70   84  90 69.16667
13  a123   M  73   81   58   75   NA  80 73.40000
14  a124   F  48   84   56   NA   60  75 64.60000
15  a125   F  NA   70   67   59   92  NA 72.00000
16  a126   M  74   76   51   85   80  NA 73.20000
17  a127   M  57   53   25   NA   76  95 61.20000
18  a128   M  47   74   81   90   NA  75 73.40000
19  a129   F  84   78   85   NA   NA  NA 82.33333
20  a130   M  68   82   74   94   NA  NA 79.50000

データのソート

関数 sort( ) はセットしたデータ並び(ベクトル)を並べ替えた結果を返す。 デフォルトは昇順で並べ替えた結果を返すが、オプション decreasing = TRUE とすると降順に並べ替えた結果を返す。

> x <- c(6, 3, 4, 1, 5, 2)
> sort(x)
[1] 1 2 3 4 5 6
> sort(x, decreasing = TRUE)
[1] 6 5 4 3 2 1

一方、関数 order( ) は、セットしたデータ並び(ベクトル)を並べ替え(ソート)したとき、元のベクトルにおけるデータ位置を返す関数である。 デフォルトは昇順ソートでの元データの位置を返すが、オプション decreasing = TRUE とすると降順ソートにおける元データの位置を返す。

次の例で、order(x) が返すデータ並び「4 6 2 3 5 1」の最初の値 4 の意味は、(昇順で x を並べ替えたときの)元データの場所が 4 であることを示す。 したがって、並べ替えたときの最初の値は x[4] つまり 1 である。

> x <- c(6, 3, 4, 1, 5, 2)
> order(x)
[1] 4 6 2 3 5 1
> order(x, decreasing = TRUE)
[1] 1 5 3 2 6 4

次の例は、データフレームの Ave 列のデータ並び(ベクトル)を Exam$Ave で取り出したとき、降順で並べ替えたときの Ave 値の元位置は order(Exam$Ave, decreasing = TRUE) であることを示している。 Exam[AveOrder, ] の出力結果に現れる行番号の並びに注意する。 その並びは、まさに order(Exam$Ave, decreasing = TRUE) に一致している。

>  AveOrder <- order(Exam$Ave, decreasing = TRUE)
> AveOrder
 [1] 19  5 20  1  9  3 13 18 16 15 10  4 11  8 12  6  7 14 17  2
> Exam[AveOrder, ]
   Stnum Sex Eng Lang Math Hist Phys Bio      Ave
19  a129   F  84   78   85   NA   NA  NA 82.33333
5   a115   F  82   69   93   NA   85  80 81.80000
20  a130   M  68   82   74   94   NA  NA 79.50000
1   a111   M  68   76   71   90   NA  85 78.00000
9   a119   M  77   77   55   NA   95  85 77.80000
3   a113   M  NA   62   88   78   80  NA 77.00000
13  a123   M  73   81   58   75   NA  80 73.40000
18  a128   M  47   74   81   90   NA  75 73.40000
16  a126   M  74   76   51   85   80  NA 73.20000
15  a125   F  NA   70   67   59   92  NA 72.00000
10  a120   M  89   NA   75   58   NA  60 70.50000
4   a114   F  62   77   52   85   NA  75 70.20000
11  a121   M  51   76   57   NA   68  95 69.40000
8   a118   M  47   64   NA   82   84  NA 69.25000
12  a122   F  61   65   45   70   84  90 69.16667
6   a116   F  44   72   57   68   84  85 68.33333
7   a117   F  48   72   66   74   NA  75 67.00000
14  a124   F  48   84   56   NA   60  75 64.60000
17  a127   M  57   53   25   NA   76  95 61.20000
2   a112   F  38   62   33   NA   78  94 61.00000
問題 sheet-na-4: 個人科目平均値を、男女別に降順で表示するにはどうすればよいか。

男女別平均

データフレーム Exam から性別 Exam$Sex を取り出して、男性または女性であるときに TRUE であるデータ並び(ベクトル)をそれぞれ Men および Women とする。

> Men <- Exam$Sex == "M"
> Men
 [1]  TRUE FALSE  TRUE FALSE FALSE FALSE FALSE  TRUE  TRUE  TRUE  TRUE FALSE
[13]  TRUE FALSE FALSE  TRUE  TRUE  TRUE FALSE  TRUE
> Women <- Exam$Sex == "F"
> Women
 [1] FALSE  TRUE FALSE  TRUE  TRUE  TRUE  TRUE FALSE FALSE FALSE FALSE  TRUE
[13] FALSE  TRUE  TRUE FALSE FALSE FALSE  TRUE FALSE

データフレーム Exam から Sex が男性または女性である行はそれぞれ Exam[Men, ] または Exam[Women, ] である。

> Exam[Men, ]
   Stnum Sex Eng Lang Math Hist Phys Bio   Ave
1   a111   M  68   76   71   90   NA  85 78.00
3   a113   M  NA   62   88   78   80  NA 77.00
8   a118   M  47   64   NA   82   84  NA 69.25
9   a119   M  77   77   55   NA   95  85 77.80
10  a120   M  89   NA   75   58   NA  60 70.50
11  a121   M  51   76   57   NA   68  95 69.40
13  a123   M  73   81   58   75   NA  80 73.40
16  a126   M  74   76   51   85   80  NA 73.20
17  a127   M  57   53   25   NA   76  95 61.20
18  a128   M  47   74   81   90   NA  75 73.40
20  a130   M  68   82   74   94   NA  NA 79.50
> Exam[Women, ]
   Stnum Sex Eng Lang Math Hist Phys Bio      Ave
2   a112   F  38   62   33   NA   78  94 61.00000
4   a114   F  62   77   52   85   NA  75 70.20000
5   a115   F  82   69   93   NA   85  80 81.80000
6   a116   F  44   72   57   68   84  85 68.33333
7   a117   F  48   72   66   74   NA  75 67.00000
12  a122   F  61   65   45   70   84  90 69.16667
14  a124   F  48   84   56   NA   60  75 64.60000
15  a125   F  NA   70   67   59   92  NA 72.00000
19  a129   F  84   78   85   NA   NA  NA 82.33333

上のcolMeans( )と rowMeans( )の方法を使って、欠損値 NA を除外して、3列から8列までのクラスの男女別科目平均が求められる。

問題 sheet-na-5: 関数 colMeans( ) を使って各科目の男女別クラス平均値を求めよ。

sapply( ) と tapply( )

問題 sheet-na-7: 次のように、関数 sapply( ) を使って、クラスの男女別科目平均が求められることを説明せよ。
> sapply(Exam[Men,3:8], FUN = mean, na.rm = TRUE)
     Eng     Lang     Math     Hist     Phys      Bio 
65.10000 72.10000 63.50000 81.50000 80.50000 82.14286 
> sapply(Exam[Women,3:8], FUN = mean, na.rm = TRUE)
     Eng     Lang     Math     Hist     Phys      Bio 
58.37500 72.11111 61.55556 71.20000 80.50000 82.00000 
問題 sheet-na-8: データフレーム Exam のEng列の Sex列のラベル値ごとのNA値を除外した平均値は、次のように関数 tapply( ) を使った計算できることを説明せよ。
> tapply(Exam$Eng, Basic$Sex, mean, na.rm = TRUE)
     F      M 
58.375 65.100