以下では具体的な例を示すためにRに最初から入っているデモ用データのirisを用いる. irisデータはアイリス(菖蒲)のがく(Sepal)と花びら(Petal)の長さ(length)と幅(width)の実測データが3つのアイリスの種類(Setosa, vergicolor, virginica)ごとに50点ずつ格納されている. 内容を確認するために,summary()関数とhead()関数(データフレームの冒頭データを出力させる関数)を実行してみた.

data_iris <- iris #irisデータを呼び出してdというオブジェクトに格納
summary(data_iris)
##   Sepal.Length    Sepal.Width     Petal.Length    Petal.Width   
##  Min.   :4.300   Min.   :2.000   Min.   :1.000   Min.   :0.100  
##  1st Qu.:5.100   1st Qu.:2.800   1st Qu.:1.600   1st Qu.:0.300  
##  Median :5.800   Median :3.000   Median :4.350   Median :1.300  
##  Mean   :5.843   Mean   :3.057   Mean   :3.758   Mean   :1.199  
##  3rd Qu.:6.400   3rd Qu.:3.300   3rd Qu.:5.100   3rd Qu.:1.800  
##  Max.   :7.900   Max.   :4.400   Max.   :6.900   Max.   :2.500  
##        Species  
##  setosa    :50  
##  versicolor:50  
##  virginica :50  
##                 
##                 
## 
head(data_iris)
##   Sepal.Length Sepal.Width Petal.Length Petal.Width Species
## 1          5.1         3.5          1.4         0.2  setosa
## 2          4.9         3.0          1.4         0.2  setosa
## 3          4.7         3.2          1.3         0.2  setosa
## 4          4.6         3.1          1.5         0.2  setosa
## 5          5.0         3.6          1.4         0.2  setosa
## 6          5.4         3.9          1.7         0.4  setosa

列名が英語のままだと分かりにくいので,日本語に翻訳した名前に置き換える(->参考).以下はそのためのコード.

colnames(data_iris) <-  c("がく長","がく幅","花びら長","花びら幅","種類")
head(data_iris)
##   がく長 がく幅 花びら長 花びら幅   種類
## 1    5.1    3.5      1.4      0.2 setosa
## 2    4.9    3.0      1.4      0.2 setosa
## 3    4.7    3.2      1.3      0.2 setosa
## 4    4.6    3.1      1.5      0.2 setosa
## 5    5.0    3.6      1.4      0.2 setosa
## 6    5.4    3.9      1.7      0.4 setosa

1 抽出

グループ分け変数を持ったデータを処理するときに,ある特定のグループのデータだけを抜き出したいということはよくある. そうした「データの抽出」を行うための関数としてtidyverseパッケージにfilter()関数が設けられている.利用するためにはTools->install.packagesでtidyverseパッケージをインストールしたうえで,スクリプト上でlibrary(tidyverse)と入力してパッケージを読み込む必要がある

filter()関数の利用法は,第1引数に元のデータを与え,第2引数に検索条件を与える.検索条件の書式のパターンは以下の通り.

書式 動作
元データ$列名==Key 列名で指定した列の値がKeyと一致しているものを抽出.「=」を重ねる点がポイント.
元データ$列名>Key 列名で指定した列の値がKeyよりも大きいものを抽出
元データ$列名>=Key 列名で指定した列の値がKeyと等しいかKeyよりも大きいものを抽出
元データ$列名<Key 列名で指定した列の値がKeyよりも小さいものを抽出
元データ$列名<=Key 列名で指定した列の値がKeyと等しいかKeyよりも小さいものを抽出
library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.1.5
## ✔ forcats   1.0.0     ✔ stringr   1.5.1
## ✔ ggplot2   3.5.1     ✔ tibble    3.2.1
## ✔ lubridate 1.9.3     ✔ tidyr     1.3.1
## ✔ purrr     1.0.2     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
summary(data_iris)
##      がく長          がく幅         花びら長        花びら幅    
##  Min.   :4.300   Min.   :2.000   Min.   :1.000   Min.   :0.100  
##  1st Qu.:5.100   1st Qu.:2.800   1st Qu.:1.600   1st Qu.:0.300  
##  Median :5.800   Median :3.000   Median :4.350   Median :1.300  
##  Mean   :5.843   Mean   :3.057   Mean   :3.758   Mean   :1.199  
##  3rd Qu.:6.400   3rd Qu.:3.300   3rd Qu.:5.100   3rd Qu.:1.800  
##  Max.   :7.900   Max.   :4.400   Max.   :6.900   Max.   :2.500  
##          種類   
##  setosa    :50  
##  versicolor:50  
##  virginica :50  
##                 
##                 
## 
res<- filter(data_iris, 種類=="virginica")
summary(res)
##      がく長          がく幅         花びら長        花びら幅    
##  Min.   :4.900   Min.   :2.200   Min.   :4.500   Min.   :1.400  
##  1st Qu.:6.225   1st Qu.:2.800   1st Qu.:5.100   1st Qu.:1.800  
##  Median :6.500   Median :3.000   Median :5.550   Median :2.000  
##  Mean   :6.588   Mean   :2.974   Mean   :5.552   Mean   :2.026  
##  3rd Qu.:6.900   3rd Qu.:3.175   3rd Qu.:5.875   3rd Qu.:2.300  
##  Max.   :7.900   Max.   :3.800   Max.   :6.900   Max.   :2.500  
##          種類   
##  setosa    : 0  
##  versicolor: 0  
##  virginica :50  
##                 
##                 
## 
res <- filter(data_iris, がく長<=5)
summary(res)
##      がく長          がく幅         花びら長        花びら幅     
##  Min.   :4.300   Min.   :2.000   Min.   :1.000   Min.   :0.1000  
##  1st Qu.:4.600   1st Qu.:3.000   1st Qu.:1.375   1st Qu.:0.2000  
##  Median :4.850   Median :3.150   Median :1.400   Median :0.2000  
##  Mean   :4.787   Mean   :3.091   Mean   :1.703   Mean   :0.3406  
##  3rd Qu.:5.000   3rd Qu.:3.400   3rd Qu.:1.600   3rd Qu.:0.3000  
##  Max.   :5.000   Max.   :3.600   Max.   :4.500   Max.   :1.7000  
##          種類   
##  setosa    :28  
##  versicolor: 3  
##  virginica : 1  
##                 
##                 
## 
res <- filter(data_iris, がく長>=6)
summary(res)
##      がく長         がく幅         花びら長        花びら幅             種類   
##  Min.   :6.00   Min.   :2.200   Min.   :4.000   Min.   :1.000   setosa    : 0  
##  1st Qu.:6.30   1st Qu.:2.800   1st Qu.:4.700   1st Qu.:1.500   versicolor:24  
##  Median :6.50   Median :3.000   Median :5.200   Median :1.800   virginica :43  
##  Mean   :6.61   Mean   :2.966   Mean   :5.263   Mean   :1.816                  
##  3rd Qu.:6.85   3rd Qu.:3.150   3rd Qu.:5.700   3rd Qu.:2.100                  
##  Max.   :7.90   Max.   :3.800   Max.   :6.900   Max.   :2.500

条件は複数並べることもできる.|は複数の条件のいずれかにマッチすれば抽出される.&は複数条件の全てにマッチするものが抽出される. それぞれ以下に示す.

res <- filter(data_iris, がく長>=6 | 花びら長>1.5) # がく長が6以上「または」花びら長が1.5より大きいもの
summary(res)
##      がく長          がく幅        花びら長        花びら幅             種類   
##  Min.   :4.700   Min.   :2.00   Min.   :1.600   Min.   :0.200   setosa    :13  
##  1st Qu.:5.600   1st Qu.:2.70   1st Qu.:4.000   1st Qu.:1.300   versicolor:50  
##  Median :6.100   Median :3.00   Median :4.700   Median :1.500   virginica :50  
##  Mean   :6.125   Mean   :2.94   Mean   :4.535   Mean   :1.519                  
##  3rd Qu.:6.600   3rd Qu.:3.20   3rd Qu.:5.400   3rd Qu.:2.000                  
##  Max.   :7.900   Max.   :3.90   Max.   :6.900   Max.   :2.500
res <- filter(data_iris, がく長>=6 & 花びら長>1.5) # がく長が6以上「かつ」花びら長が1.5より大きいもの
summary(res)
##      がく長         がく幅         花びら長        花びら幅             種類   
##  Min.   :6.00   Min.   :2.200   Min.   :4.000   Min.   :1.000   setosa    : 0  
##  1st Qu.:6.30   1st Qu.:2.800   1st Qu.:4.700   1st Qu.:1.500   versicolor:24  
##  Median :6.50   Median :3.000   Median :5.200   Median :1.800   virginica :43  
##  Mean   :6.61   Mean   :2.966   Mean   :5.263   Mean   :1.816                  
##  3rd Qu.:6.85   3rd Qu.:3.150   3rd Qu.:5.700   3rd Qu.:2.100                  
##  Max.   :7.90   Max.   :3.800   Max.   :6.900   Max.   :2.500
res <- filter(data_iris, がく長>=5 & がく長<6.5) # 5<=がく長<6.5
summary(res)
##      がく長          がく幅         花びら長        花びら幅    
##  Min.   :5.000   Min.   :2.000   Min.   :1.200   Min.   :0.100  
##  1st Qu.:5.300   1st Qu.:2.700   1st Qu.:1.600   1st Qu.:0.400  
##  Median :5.700   Median :3.000   Median :4.100   Median :1.300  
##  Mean   :5.691   Mean   :3.047   Mean   :3.577   Mean   :1.133  
##  3rd Qu.:6.100   3rd Qu.:3.400   3rd Qu.:4.800   3rd Qu.:1.600  
##  Max.   :6.400   Max.   :4.400   Max.   :6.000   Max.   :2.500  
##          種類   
##  setosa    :30  
##  versicolor:40  
##  virginica :23  
##                 
##                 
## 
res <- filter(data_iris, がく長<5 | がく長>=6.5) # がく長<5, 6.5<=学長
summary(res)
##      がく長          がく幅         花びら長        花びら幅    
##  Min.   :4.300   Min.   :2.300   Min.   :1.000   Min.   :0.100  
##  1st Qu.:4.800   1st Qu.:3.000   1st Qu.:1.500   1st Qu.:0.200  
##  Median :6.600   Median :3.100   Median :4.800   Median :1.500  
##  Mean   :6.091   Mean   :3.074   Mean   :4.053   Mean   :1.307  
##  3rd Qu.:6.900   3rd Qu.:3.200   3rd Qu.:5.800   3rd Qu.:2.100  
##  Max.   :7.900   Max.   :3.800   Max.   :6.900   Max.   :2.500  
##          種類   
##  setosa    :20  
##  versicolor:10  
##  virginica :27  
##                 
##                 
## 

抽出したデータのある列だけが必要だという場合には,抽出したデータを収めたオブジェクトにに$[]をつけて指定すればよい.以下では内容を確認するため,head()関数を使って最初の数行だけを表示させている.

res <- filter(data_iris, がく長>=6 | 花びら長>1.5)
res<- res$花びら長
head(res)
## [1] 1.7 1.6 1.7 1.7 1.7 1.9
res <- filter(data_iris, がく長>=6 | 花びら長>1.5)
res<- res["花びら長"]
head(res)
##   花びら長
## 1      1.7
## 2      1.6
## 3      1.7
## 4      1.7
## 5      1.7
## 6      1.9
#次のように関数に対して直接[]や$を書くこともできる
res2 <- filter(data_iris, がく長>=6 | 花びら長>1.5)$花びら長
head(res2)
## [1] 1.7 1.6 1.7 1.7 1.7 1.9
res2 <- filter(data_iris, がく長>=6 | 花びら長>1.5)["花びら長"]
head(res2)
##   花びら長
## 1      1.7
## 2      1.6
## 3      1.7
## 4      1.7
## 5      1.7
## 6      1.9
#複数の列を取り出す場合には[c()]を使う
res3 <- filter(data_iris, がく長>=6 | 花びら長>1.5)[c("花びら長","花びら幅")]
head(res3)
##   花びら長 花びら幅
## 1      1.7      0.4
## 2      1.6      0.2
## 3      1.7      0.3
## 4      1.7      0.2
## 5      1.7      0.5
## 6      1.9      0.2

$[]とで出力が違うのはこちらで説明したように,$は指定した列をベクトルとして取り出すのに対して,[]は指定した列だけで構成されるデータフレームとして取り出されるためである.

2 数値の置換

数値の置換にはifelse()関数を応用する.ifelse()関数は,第1引数に条件を記述し(条件の記述方法は検索と同じ),第2引数には条件にマッチした場合に置き換える値,第3引数にはマッチしなかった場合に置き換える値を記載する(Excelのif()関数と同じ引数構造である).ifelse()関数の実行結果として最終的に返ってくるのは,そうした置き換えが行われたベクトルとなる.あくまでベクトルが返ってくるだけなので,実際に置き換えをデータに反映させるためには,元のデータの元の列に返ってきたベクトルを代入するなり,元のデータに新しい列を作ってベクトルを代入するなりする必要がある.

「値」と記載しているが,コンピュータの用語として「値」という場合には,数値だけでなく文字列も含められていることは覚えておこう.数値だけをいう場合には「数値」,文字列だけを言う場合には「文字列」とはっきりと言わなければならない.

以下の例ではがく長が平均以上なら1,未満なら0を割り当てた新しい変数bがく長を作成している.

data_iris2 <- data_iris 
data_iris2$bがく長 <- ifelse(data_iris2$がく長 >= mean(data_iris2$がく長), 1, 0)
print(data_iris2$bがく長)
##   [1] 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
##  [38] 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 0 1 0 1 0 1 0 0 1 1 1 0 1 0 0 1 0 1 1 1 1
##  [75] 1 1 1 1 1 0 0 0 0 1 0 1 1 1 0 0 0 1 0 0 0 0 0 1 0 0 1 0 1 1 1 1 0 1 1 1 1
## [112] 1 1 0 0 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1
## [149] 1 1

ついでにas.factor()を用いて要因型に変換をしてみた.

data_iris2$bがく長 <- as.factor(data_iris2$bがく長)
summary(data_iris2)
##      がく長          がく幅         花びら長        花びら幅    
##  Min.   :4.300   Min.   :2.000   Min.   :1.000   Min.   :0.100  
##  1st Qu.:5.100   1st Qu.:2.800   1st Qu.:1.600   1st Qu.:0.300  
##  Median :5.800   Median :3.000   Median :4.350   Median :1.300  
##  Mean   :5.843   Mean   :3.057   Mean   :3.758   Mean   :1.199  
##  3rd Qu.:6.400   3rd Qu.:3.300   3rd Qu.:5.100   3rd Qu.:1.800  
##  Max.   :7.900   Max.   :4.400   Max.   :6.900   Max.   :2.500  
##          種類    bがく長
##  setosa    :50   0:80   
##  versicolor:50   1:70   
##  virginica :50          
##                         
##                         
## 

2.1 ifelse()の入れ子

ifelse()関数は入れ子にすることもできる.すなわち,

ifelse(<<条件>>, <<条件が真の場合の処理>>, 
    ifelse(<<条件2>>, <<条件2が真の場合の処理>>, <<条件2が偽の場合の処理)
    )

という書き方ができる.

以下の例では,入れ子を用いてがく長を以下の4段階に分けてたものをbがく長に格納している.

  • 平均-標準偏差未満には0
  • 平均-標準偏差以上,平均未満ならば1
  • 平均以上,平均+標準偏差未満ならば2
  • 平均+標準偏差以上ならば3
data_iris2 <- data_iris
M <- mean(data_iris2$がく長)
SD <-sd(data_iris2$がく長)
data_iris2$bがく長 <- ifelse(data_iris2$がく長 < M-SD,0,
                          ifelse(data_iris2$花びら長 <M,1,
                                 ifelse(data_iris2$花びら長 < M+SD,2,3)
                                )
                          )
print(data_iris2$bがく長)
##   [1] 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 1 1 1 1 1 1 1 0 1 0 0 0 1 1 0 0 1 1 1 0 0 1
##  [38] 0 0 1 0 0 0 0 1 0 1 0 1 0 1 1 1 1 1 1 1 0 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1
##  [75] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 2 1 2 1 1 2 0 2 1 2 1
## [112] 1 1 1 1 1 1 3 3 1 1 1 3 1 1 2 1 1 1 1 2 2 1 1 1 2 1 1 1 1 1 1 1 2 1 1 1 1
## [149] 1 1
data_iris2$bがく長 <- as.factor(data_iris2$bがく長)
summary(data_iris2)
##      がく長          がく幅         花びら長        花びら幅    
##  Min.   :4.300   Min.   :2.000   Min.   :1.000   Min.   :0.100  
##  1st Qu.:5.100   1st Qu.:2.800   1st Qu.:1.600   1st Qu.:0.300  
##  Median :5.800   Median :3.000   Median :4.350   Median :1.300  
##  Mean   :5.843   Mean   :3.057   Mean   :3.758   Mean   :1.199  
##  3rd Qu.:6.400   3rd Qu.:3.300   3rd Qu.:5.100   3rd Qu.:1.800  
##  Max.   :7.900   Max.   :4.400   Max.   :6.900   Max.   :2.500  
##          種類    bがく長
##  setosa    :50   0: 32  
##  versicolor:50   1:105  
##  virginica :50   2: 10  
##                  3:  3  
##                         
## 

なお,上記では分かりやすさのため,入れ子となっている箇所で改行をしているが,別に改行しなくてもよい.以下は改行せずに書いた例.上記と同じ結果を返す.

data_iris2$bがく長 <- ifelse(data_iris2$がく長<M-SD, 0, ifelse(data_iris2$花びら長<M, 1, ifelse(data_iris2$花びら長<M+SD, 2, 3)))
print(data_iris2$bがく長)
##   [1] 1 0 0 0 0 1 0 0 0 0 1 0 0 0 1 1 1 1 1 1 1 1 0 1 0 0 0 1 1 0 0 1 1 1 0 0 1
##  [38] 0 0 1 0 0 0 0 1 0 1 0 1 0 1 1 1 1 1 1 1 0 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1
##  [75] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 2 1 2 1 1 2 0 2 1 2 1
## [112] 1 1 1 1 1 1 3 3 1 1 1 3 1 1 2 1 1 1 1 2 2 1 1 1 2 1 1 1 1 1 1 1 2 1 1 1 1
## [149] 1 1
data_iris2$bがく長 <- as.factor(data_iris2$bがく長)
summary(data_iris2)
##      がく長          がく幅         花びら長        花びら幅    
##  Min.   :4.300   Min.   :2.000   Min.   :1.000   Min.   :0.100  
##  1st Qu.:5.100   1st Qu.:2.800   1st Qu.:1.600   1st Qu.:0.300  
##  Median :5.800   Median :3.000   Median :4.350   Median :1.300  
##  Mean   :5.843   Mean   :3.057   Mean   :3.758   Mean   :1.199  
##  3rd Qu.:6.400   3rd Qu.:3.300   3rd Qu.:5.100   3rd Qu.:1.800  
##  Max.   :7.900   Max.   :4.400   Max.   :6.900   Max.   :2.500  
##          種類    bがく長
##  setosa    :50   0: 32  
##  versicolor:50   1:105  
##  virginica :50   2: 10  
##                  3:  3  
##                         
## 
ifelse()関数の第2,第3引数

ifelse()関数は第2,第3引数はそれぞれ第1引数で指定した条件が真であった場合に返す値,偽であった場合に返す値を設定しているが,この部分に式や関数を指定しても良い.先のifelse()の入れ子はまさに関数の1つとしてのifelse()を実行している例だが,例えば,以下のようにすることもできる.

M<-mean(data_iris2$がく長)
SD<-sd(data_iris2$がく長)
data_iris2$がく長r <- ifelse(data_iris2$がく長<M-SD, M-SD, ifelse(data_iris2$花びら長>=M+SD, M+SD, M))
print(data_iris2$がく長r)
##   [1] 5.843333 5.015267 5.015267 5.015267 5.015267 5.843333 5.015267 5.015267
##   [9] 5.015267 5.015267 5.843333 5.015267 5.015267 5.015267 5.843333 5.843333
##  [17] 5.843333 5.843333 5.843333 5.843333 5.843333 5.843333 5.015267 5.843333
##  [25] 5.015267 5.015267 5.015267 5.843333 5.843333 5.015267 5.015267 5.843333
##  [33] 5.843333 5.843333 5.015267 5.015267 5.843333 5.015267 5.015267 5.843333
##  [41] 5.015267 5.015267 5.015267 5.015267 5.843333 5.015267 5.843333 5.015267
##  [49] 5.843333 5.015267 5.843333 5.843333 5.843333 5.843333 5.843333 5.843333
##  [57] 5.843333 5.015267 5.843333 5.843333 5.015267 5.843333 5.843333 5.843333
##  [65] 5.843333 5.843333 5.843333 5.843333 5.843333 5.843333 5.843333 5.843333
##  [73] 5.843333 5.843333 5.843333 5.843333 5.843333 5.843333 5.843333 5.843333
##  [81] 5.843333 5.843333 5.843333 5.843333 5.843333 5.843333 5.843333 5.843333
##  [89] 5.843333 5.843333 5.843333 5.843333 5.843333 5.015267 5.843333 5.843333
##  [97] 5.843333 5.843333 5.843333 5.843333 5.843333 5.843333 5.843333 5.843333
## [105] 5.843333 5.843333 5.015267 5.843333 5.843333 5.843333 5.843333 5.843333
## [113] 5.843333 5.843333 5.843333 5.843333 5.843333 6.671399 6.671399 5.843333
## [121] 5.843333 5.843333 6.671399 5.843333 5.843333 5.843333 5.843333 5.843333
## [129] 5.843333 5.843333 5.843333 5.843333 5.843333 5.843333 5.843333 5.843333
## [137] 5.843333 5.843333 5.843333 5.843333 5.843333 5.843333 5.843333 5.843333
## [145] 5.843333 5.843333 5.843333 5.843333 5.843333 5.843333

この例では,がく長が平均-標準偏差未満のものは,すべて平均-標準偏差=5.0152672に,平均+標準偏差以上のものは,すべて平均+標準偏差=6.6713995に,平均-標準偏差から平均+標準偏差の間にあるものは全て平均=5.8433333に置き換えられる

3 文字列の置換

文字列型の列や要因型の列を置換したい場合にはifelse()関数を使うよりも,recode()関数を使うほうが簡単である.

第1引数には,変換したいベクトル,第2引数以降には,置換内容を記述していく.置換内容が指定されていないものは元の値が維持される.以下の例では,setosaセトサに,versicolorバーシカラーに置換している.

data_iris2 <- data_iris 
data_iris2$種類 <- recode(data_iris2$種類, "setosa"="セトサ", "versicolor"="バーシカラー")
print(data_iris2$種類)
##   [1] セトサ       セトサ       セトサ       セトサ       セトサ      
##   [6] セトサ       セトサ       セトサ       セトサ       セトサ      
##  [11] セトサ       セトサ       セトサ       セトサ       セトサ      
##  [16] セトサ       セトサ       セトサ       セトサ       セトサ      
##  [21] セトサ       セトサ       セトサ       セトサ       セトサ      
##  [26] セトサ       セトサ       セトサ       セトサ       セトサ      
##  [31] セトサ       セトサ       セトサ       セトサ       セトサ      
##  [36] セトサ       セトサ       セトサ       セトサ       セトサ      
##  [41] セトサ       セトサ       セトサ       セトサ       セトサ      
##  [46] セトサ       セトサ       セトサ       セトサ       セトサ      
##  [51] バーシカラー バーシカラー バーシカラー バーシカラー バーシカラー
##  [56] バーシカラー バーシカラー バーシカラー バーシカラー バーシカラー
##  [61] バーシカラー バーシカラー バーシカラー バーシカラー バーシカラー
##  [66] バーシカラー バーシカラー バーシカラー バーシカラー バーシカラー
##  [71] バーシカラー バーシカラー バーシカラー バーシカラー バーシカラー
##  [76] バーシカラー バーシカラー バーシカラー バーシカラー バーシカラー
##  [81] バーシカラー バーシカラー バーシカラー バーシカラー バーシカラー
##  [86] バーシカラー バーシカラー バーシカラー バーシカラー バーシカラー
##  [91] バーシカラー バーシカラー バーシカラー バーシカラー バーシカラー
##  [96] バーシカラー バーシカラー バーシカラー バーシカラー バーシカラー
## [101] virginica    virginica    virginica    virginica    virginica   
## [106] virginica    virginica    virginica    virginica    virginica   
## [111] virginica    virginica    virginica    virginica    virginica   
## [116] virginica    virginica    virginica    virginica    virginica   
## [121] virginica    virginica    virginica    virginica    virginica   
## [126] virginica    virginica    virginica    virginica    virginica   
## [131] virginica    virginica    virginica    virginica    virginica   
## [136] virginica    virginica    virginica    virginica    virginica   
## [141] virginica    virginica    virginica    virginica    virginica   
## [146] virginica    virginica    virginica    virginica    virginica   
## Levels: セトサ バーシカラー virginica

上記の例では文字列を文字列で置換しているので,=の前後はすべて""で括っているが,数値に置換する場合には当然ながら""は不要である.ただし,この場合,置換内容が指定されていなかったものはNAとして処理されるので注意が必要である.

data_iris2 <- data_iris 
data_iris2$種類 <- recode(data_iris2$種類, "setosa"=1, "versicolor"=2) # virginicaは置換されないのでNAになる
print(data_iris2$種類)
##   [1]  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1
##  [26]  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1
##  [51]  2  2  2  2  2  2  2  2  2  2  2  2  2  2  2  2  2  2  2  2  2  2  2  2  2
##  [76]  2  2  2  2  2  2  2  2  2  2  2  2  2  2  2  2  2  2  2  2  2  2  2  2  2
## [101] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
## [126] NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA NA
ifelse()を使った文字列置換

文字列の変換にifelse()関数を使うこともできるが,列が要因型の場合には,一旦文字列型に変換してからでないと,思ったような結果とならない場合がある.例えば,以下の例ではsetosaだけをセトサに置き換えたいと思って実行したのにもかかわらず,列が要因型ではなく勝手に文字列型にされ,さらに,他のもの種類名も勝手に23に設定されてしまっているのがわかる.

data_iris2 <- data_iris
data_iris2$種類 <- ifelse(data_iris2$種類=="setosa", "セトサ", data_iris2$種類 )
summary(data_iris2$種類) #サマリー表示 classがCharacter(文字列型)となっている
data_iris2$種類 #中身を確認してみる
##    Length     Class      Mode 
##       150 character character 
##   [1] "セトサ" "セトサ" "セトサ" "セトサ" "セトサ" "セトサ" "セトサ" "セトサ"
##   [9] "セトサ" "セトサ" "セトサ" "セトサ" "セトサ" "セトサ" "セトサ" "セトサ"
##  [17] "セトサ" "セトサ" "セトサ" "セトサ" "セトサ" "セトサ" "セトサ" "セトサ"
##  [25] "セトサ" "セトサ" "セトサ" "セトサ" "セトサ" "セトサ" "セトサ" "セトサ"
##  [33] "セトサ" "セトサ" "セトサ" "セトサ" "セトサ" "セトサ" "セトサ" "セトサ"
##  [41] "セトサ" "セトサ" "セトサ" "セトサ" "セトサ" "セトサ" "セトサ" "セトサ"
##  [49] "セトサ" "セトサ" "2"      "2"      "2"      "2"      "2"      "2"     
##  [57] "2"      "2"      "2"      "2"      "2"      "2"      "2"      "2"     
##  [65] "2"      "2"      "2"      "2"      "2"      "2"      "2"      "2"     
##  [73] "2"      "2"      "2"      "2"      "2"      "2"      "2"      "2"     
##  [81] "2"      "2"      "2"      "2"      "2"      "2"      "2"      "2"     
##  [89] "2"      "2"      "2"      "2"      "2"      "2"      "2"      "2"     
##  [97] "2"      "2"      "2"      "2"      "3"      "3"      "3"      "3"     
## [105] "3"      "3"      "3"      "3"      "3"      "3"      "3"      "3"     
## [113] "3"      "3"      "3"      "3"      "3"      "3"      "3"      "3"     
## [121] "3"      "3"      "3"      "3"      "3"      "3"      "3"      "3"     
## [129] "3"      "3"      "3"      "3"      "3"      "3"      "3"      "3"     
## [137] "3"      "3"      "3"      "3"      "3"      "3"      "3"      "3"     
## [145] "3"      "3"      "3"      "3"      "3"      "3"

意図した通りのことをしたいならば,以下のようになる.つまり,一旦,文字列型に戻した後に,ifelse()で置換を行ない,再度要因型に変換している.

data_iris2 <- data_iris
data_iris2$種類 <- as.character(data_iris2$種類) #要因型から文字列型に変換
data_iris2$種類 <- ifelse(data_iris2$種類=="setosa", "セトサ", data_iris2$種類 )
data_iris2$種類 #中身を確認してみる
summary(data_iris2$種類) #サマリー表示
data_iris2$種類 <- as.factor(data_iris2$種類) #文字列型から要因型に変換
summary(data_iris2$種類) #サマリー表示 要因型に変換したので表示が変わる
##   [1] "セトサ"     "セトサ"     "セトサ"     "セトサ"     "セトサ"    
##   [6] "セトサ"     "セトサ"     "セトサ"     "セトサ"     "セトサ"    
##  [11] "セトサ"     "セトサ"     "セトサ"     "セトサ"     "セトサ"    
##  [16] "セトサ"     "セトサ"     "セトサ"     "セトサ"     "セトサ"    
##  [21] "セトサ"     "セトサ"     "セトサ"     "セトサ"     "セトサ"    
##  [26] "セトサ"     "セトサ"     "セトサ"     "セトサ"     "セトサ"    
##  [31] "セトサ"     "セトサ"     "セトサ"     "セトサ"     "セトサ"    
##  [36] "セトサ"     "セトサ"     "セトサ"     "セトサ"     "セトサ"    
##  [41] "セトサ"     "セトサ"     "セトサ"     "セトサ"     "セトサ"    
##  [46] "セトサ"     "セトサ"     "セトサ"     "セトサ"     "セトサ"    
##  [51] "versicolor" "versicolor" "versicolor" "versicolor" "versicolor"
##  [56] "versicolor" "versicolor" "versicolor" "versicolor" "versicolor"
##  [61] "versicolor" "versicolor" "versicolor" "versicolor" "versicolor"
##  [66] "versicolor" "versicolor" "versicolor" "versicolor" "versicolor"
##  [71] "versicolor" "versicolor" "versicolor" "versicolor" "versicolor"
##  [76] "versicolor" "versicolor" "versicolor" "versicolor" "versicolor"
##  [81] "versicolor" "versicolor" "versicolor" "versicolor" "versicolor"
##  [86] "versicolor" "versicolor" "versicolor" "versicolor" "versicolor"
##  [91] "versicolor" "versicolor" "versicolor" "versicolor" "versicolor"
##  [96] "versicolor" "versicolor" "versicolor" "versicolor" "versicolor"
## [101] "virginica"  "virginica"  "virginica"  "virginica"  "virginica" 
## [106] "virginica"  "virginica"  "virginica"  "virginica"  "virginica" 
## [111] "virginica"  "virginica"  "virginica"  "virginica"  "virginica" 
## [116] "virginica"  "virginica"  "virginica"  "virginica"  "virginica" 
## [121] "virginica"  "virginica"  "virginica"  "virginica"  "virginica" 
## [126] "virginica"  "virginica"  "virginica"  "virginica"  "virginica" 
## [131] "virginica"  "virginica"  "virginica"  "virginica"  "virginica" 
## [136] "virginica"  "virginica"  "virginica"  "virginica"  "virginica" 
## [141] "virginica"  "virginica"  "virginica"  "virginica"  "virginica" 
## [146] "virginica"  "virginica"  "virginica"  "virginica"  "virginica" 
##    Length     Class      Mode 
##       150 character character 
## versicolor  virginica     セトサ 
##         50         50         50

4 欠損データの除外

実験や調査を行った際にデータの一部が欠損しているケースは多々ある.Rでは欠損データはデータフレーム上でNA(Not Available:利用不可能の意味)と表記されている.

関数によっては欠損データを含んでいるデータを受け付けないケースがある.例えばすでに平均値で紹介したようにmean()関数は,そのままでは欠損値を含んだデータは受け付けない.

mean()関数では,関数の引数でna.rm = Tを設定しておくと関数処理の中でNAを除いて処理してくれるが,そもそも与えるデータそのものでNAを除いておくほうが望ましい.

例えば,次のようなデータを処理するケースを考えよう.これはAからFまでの6人に対して10問セットの2種類のクイズを解いてもらった時のスコアである.ただ,1つめのデータセットではDさんのデータが,2つめのデータではEさんのデータが欠損してしまっている.このスコアをもとに,どちらのデータセットが難しいデータだったのかを比べたい.そこで,それぞれの平均や標準偏差を求めてみた.欠損データを含んでいるのでna.rm=Tを設定した.さて,このやり方は正しいだろうか.

data_iris3 <- data.frame(
  name = c("A", "B", "C", "D", "E", "F"),
  score1 = c(9,8,9,NA,6,7),
  score2 = c(8,7,8,10,NA,7)
)

mean(data_iris3$score1, na.rm=T)
## [1] 7.8
sd(data_iris3$score1, na.rm=T)
## [1] 1.30384
mean(data_iris3$score2, na.rm=T)
## [1] 8
sd(data_iris3$score2, na.rm=T)
## [1] 1.224745

このデータの場合,平均や標準偏差を単純比較してはまずい.理由としてscore1ではD以外のデータが使われているのに対して,score2ではE以外のデータが使われているからである.すなわち,それぞれを計算するときに使われたデータが異なっているからである.もしDさんがクイズがすごく得意な人であったならば,Dさんのデータが入っていないscore1の方は,不当に低くくなってしまっているであろうし,逆にEさんがクイズが苦手な人であったならば,Eさんのデータが入っていないscore2の方は不当に高くなっている可能性がある.

データを比較するときには,比較しようとしている項目以外で結果に影響を与えると考えられる項目(条件)は同一条件となっている,ということが大前提である.したがって,上記のようなデータを比較するときには,2種類のクイズのデータが両方ともそろっているA,B,C,Fの4人のデータだけで比較するべきである.

このように,データの一部に欠損値を含んでいるよな観測をすべて除外して,すべての変数でデータがそろっているものだけを残すには,na.omit()関数を用いる.

data_iris3.naomit <- na.omit(data_iris3)
data_iris3.naomit
##   name score1 score2
## 1    A      9      8
## 2    B      8      7
## 3    C      9      8
## 6    F      7      7
mean(data_iris3.naomit$score1)
## [1] 8.25
sd(data_iris3.naomit$score1)
## [1] 0.9574271
mean(data_iris3.naomit$score2)
## [1] 7.5
sd(data_iris3.naomit$score2)
## [1] 0.5773503

こうした欠損値は特に任意回答の項目を入れているアンケート調査では間違いなく含まれてくるし,紙でとったアンケート調査においては無回答であったり,択一回答の質問なのに勝手に2つ以上の選ぶなど不正回答をしてくるケースも多々ある.そういう場合にはデータ上はNAとして処理すべきデータとなる.そのような形でNAを含んだデータではデータを読み込んだ時点で上記の通りにna.omit()を実施しておくとよい.

5 データの整理方法(ワイド形式とロング形式)

例えば,ある中学校のあるクラス(25人)での1年間の定期テストでの数学の得点をデータとして扱う事例を考えてみよう.

人がそのようなデータを扱うときには以下のような表を作るだろう.

学生番号 前期中間 前期期末 後期中間 後期期末
1 75 94 89 65
2 60 81 70 95
3 68 84 65 70
4 99 78 80 88
5 73 59 85 64
・・・ ・・・ ・・・ ・・・ ・・・
24 85 62 75 88
25 72 77 87 92

このようなデータの並べ方を「ワイド形式」と呼ぶ.ワイド形式は一般に人がみてわかりやすい表形式となる. それに対して,Rで扱う表は以下のような並べ方のデータを並べている方が扱いやすい.このような並べ方を「ロング形式」と呼ぶ.

データNo. 生徒番号 時期 得点
1 1 前期中間 75
2 1 前期期末 94
3 1 後期中間 89
4 1 後期期末 65
5 2 前期中間 60
6 2 前期期末 81
7 2 後期中間 70
8 2 後期期末 95
9 3 前期中間 68
10 3 前期期末 84
11 3 後期中間 65
12 3 後期期末 70
13 4 前期中間 99
・・・ ・・・ ・・・ ・・・
92 23 後期期末 94
93 24 前期中間 85
94 24 前期期末 62
95 24 後期中間 75
96 24 後期期末 88
97 25 前期中間 72
98 25 前期期末 77
99 25 後期中間 87
100 25 後期期末 92

ワイド形式では列は測定された属性変数を属性値ごとに横に並べるのに対して,ロング形式では属性変数はあくまで一つの変数として1つの列にまとめられ縦方向に各属性値が記述されていくが異なる.

また,ワイド形式の場合,例えば,数学の得点を並べた表とは国語の得点を並べた表ば別個に作成する必要がある. 一方で,ロング形式の場合,数学の得点の列,国語の得点の列,というように並べて行けばよい.

データ処理という点では,人の目からはわかりづらいがロング形式のデータの方が,何が変数なのかが明確に分かるため,ロング形式のデータを作る習慣を持っておこう.

5.1 ワイド形式からロング形式への変換

tidyverseパッケージ(正確にはtidyrパッケージ)ではワイド形式のデータをロング形式のデータに変換するpivot_longer()関数が存在している.もし提供されたデータがワイド形式であった場合には,pivot_longer()関数を用いればよい(もちろん,Excelで手作業で並べ変えを行っても構わない).

pivot_longer()関数の引数は以下のとおりである.

  • 第1引数:変換対象となっているワイド形式のデータフレーム
  • 第2引数:cols=の形でワイド形式で列名になっている属性名を与える.
  • 第3引数:names_to=で第2引数で指定した属性名を収めた列に与える属性変数の名を与える.
  • 第4引数:values_to=でワイド形式での各値そのものの変数名を与える.

以下に例を示す.

# ワイド形式のデータフレームを作成
wide_data <- data.frame(
  id = 1:4,
  性別=c("男性","女性","男性","女性"),
  time1 = c(10, 20, 30, 40),
  time2 = c(50, 60, 70, 80),
  time3 = c(90, 100, 110,120)
)

# ワイド形式のデータを表示
print(wide_data)
##   id 性別 time1 time2 time3
## 1  1 男性    10    50    90
## 2  2 女性    20    60   100
## 3  3 男性    30    70   110
## 4  4 女性    40    80   120
# ロング形式に変換
long_data <- pivot_longer(
  wide_data,
  cols=c("time1","time2","time3"),# cols=colnames(wide_data)[c(3:5)]でも可
  names_to = "時期", #属性変数に与える列名(変数名)
  values_to= "得点" #測定変数に与える列名
)

#ロング形式のデータを表示
print(long_data)
## # A tibble: 12 × 4
##       id 性別  時期   得点
##    <int> <chr> <chr> <dbl>
##  1     1 男性  time1    10
##  2     1 男性  time2    50
##  3     1 男性  time3    90
##  4     2 女性  time1    20
##  5     2 女性  time2    60
##  6     2 女性  time3   100
##  7     3 男性  time1    30
##  8     3 男性  time2    70
##  9     3 男性  time3   110
## 10     4 女性  time1    40
## 11     4 女性  time2    80
## 12     4 女性  time3   120

この例の「性別」列のように,pivot_longer()関数実行時に特に何の指定もしなかった変数については,属性変数として,その列名がそのまま使われるとともに,各列に対して自動で適切な値が付与される.

pivot_longer()の出力の型

pivot_longer()の出力結果はdata.frame型ではなくtibble型となっている.tibbledata.frameの上位互換のデータ型である.いくつかの違いがあるが,わかりやすい点としてはprint()させたときに,列名に各列の型が表示される点が異なっている.基本的な使い方はdata.frameと変わらないが,もしdata.frameにしたければ,as.data.frmae()関数を利用すればよい.

class(long_data)
## [1] "tbl_df"     "tbl"        "data.frame"
long_data <- as.data.frame(long_data)
class(long_data)
## [1] "data.frame"

次のデータを読み込み,以下の処理を行え.

  1. summay()関数によって,このデータの内容がどのようなものかを確認せよ.
  2. 性別と血液型をそれぞれ要因型にし,再度sumary()関数を実施し,先の結果との違いを確認せよ.
  3. 性別のMを「男性」,Fを「女性」に置換し,再度sumary()関数を実施し,先の結果との違いを確認せよ.
  4. 男性のみのデータを抽出し,そのデータの身長の平均値と標準偏差,ならびに平均の95%信頼区間を求めよ.
  5. 女性でかつ身長が170㎝以上の人の数が何人か求めよ.
  6. BMIが25以上には「過体重」,18.5以上で25未満には「標準」,18.5未満には「低体重」というルールでラベルを付け,新たな列「体重ラベル」としてデータに追加し,その列を要因型に変換して,summary()関数で各ラベルがついた人数を確認せよ.

次のデータは,調査協力者40名(男性19名,女性21名)に対して,食パンA, B, Cを試食し,風味の良さを10段階で回答してもらったデータである.以下の課題を行え.

  1. head()関数によって,このデータの内容がどのようなものかを確認せよ.
  2. ワイド形式のデータをロング形式に変換し,同様にhead()関数で内容を確認せよ.
  3. ロング形式に変換したデータを用いて,男性の食パンAの平均点と女性の食パンAの平均点をそれぞれ算出せよ.

6 まとめ

今回用いた関数は以下の通りである.

  • colnames():データフレームの列名を取得する関数.これを左辺に<-で新しい列名を収めたベクトルを与えることによって列名(変数名)を置換できる.
    • 第1引数:データフレーム
  • head():データフレームの先頭から指定した行数だけを表示する関数
    • 第1引数:データフレーム
    • 第2引数:表示する行数(デフォルトは6)
  • filter():デーソースの行を抽出する関数
    • 第1引数:データフレーム
    • 第2引数:フィルタの条件式.=, <, >, |, &を組み合わせて作る
  • ifelse():条件分岐を行う関数  - 第1引数:条件式
    • 第2引数:条件式がTRUEの場合の値もしくは式
    • 第3引数:条件式がFALSEの場合の値もしくは式
  • recode():文字列や要因型の列を置換する関数
    • 第1引数:置換対象のベクトル
    • 第2引数以降:置換内容("置換前"="置換後")を,で区切りながら順に記述する.
  • na.omit():欠損データを除外する関数
    • 第1引数:データフレーム
  • pivot_longer():ワイド形式のデータをロング形式に変換する関数
    • 第1引数:データフレーム
    • 第2引数:cols=でワイド形式で列名になっている属性値を与える.
    • 第3引数:names_to=で第2引数で指定した属性値を収めた列に与える属性変数の名を与える.
    • 第4引数:values_to=でワイド形式での各値そのものの変数名を与える.