Rで、条件 (時に複数条件) にあうデータを取り出す方法

はじめに

"data" というデータセットから、文字列の列で条件に合うデータを、抽出する方法の覚え書きです。data の例として、R をインストールしてあれば、だれでも呼びだせる iris というアヤメの観測データの見本データを使いました。data に iris を格納して、Species が、virginica または、versicolor のデータを抜き出し、subdata に格納します。

恐らく下記の3つの方法のどれかで、ほとんどの要求に耐えられるはずです。

  1. 行指定を、data[ 行指定 , 列指定 ] 内の左の行指定部分に入れる (基本)
  2. subset 関数を使う
  3. dplyr パッケージの filter を使う

では、あまり、面倒なことは読みたくない方のために、いきなり結論式から書きます。

# データの読み込み
data(iris)
data <- iris
#1. 行番号を、data[ 行番号指定 , 列番号指定 ] 内の左の行番号指定部分に入れる
subdata <- data[data$Species=="virginica" | data$Species=="versicolor", ]

#2. subset 関数を使う
subdata <- subset(data, Species=="virginica" | Species=="versicolor")

#3. dplyr パッケージの filter を使う
subdata<- data %>%
dplyr::filter(Species=="virginica" | Species=="versicolor")

もう少し詳しく知りたい人は、下記を読みましょう。

上の3つの例では、良く見ると、

data$Species=="virginica" | data$Species=="versicolor"

と、いうところが、"data"の有無で表現が異りますが基本構造は共通ですね。このコードの意味するところなどを下記に順番に説明していきます。 これが、「そうだね」って感じられる方は、下記を読む必要性は低いしょう。

行と列の指定方法の基本をおさえよう

データを読み込み

まず、下記でデータを読み込みましょう。R に下のコード2行を、コピー&ペーストすれば、data に iris のデータが格納されます。
# データの読み込み
data(iris)
data <- iris

行と列の指定方法基本

"data[ 行番号指定 , 列番号指定 ]" とするのが基本です。ここで、

  • 「行番号指定」は、上から何番目にある行 (row 約は「行」または「並び」 <水平方向に長い梁のようなかたまり> )を表示して欲しいかを書きます
  • 「列番号指定」は、左から何番目にある列 (column = 柱 <垂直方向に長い柱状のかたまり>) を表示して欲しいかを書きます

2種類の指定方法

行や列の番号を指定する方法は2つあります。以下に、順番に例示していきますので、よかったら、R に、コピー&ペーストして、どうなるか確認して下さい。">" という記号に続けて、コードを書いています。この下には、出力結果が載っていることが多いです。ここで、注意があります。 くれぐれも、">" の次のところから、コピー&ペーストして下さい。">"を入れると、動きません。

  1. 番号を指定する (複数の時は、c() の中に入れるなどベクトル化して入れます。長さは必要な行や列の数だけ)
    • 例1. data[c(1,4,6,10),]
      第1,4,6,10行目を抜き出します。列指定が空欄の場合は、R では全部の行や列が指定されます
      > data[c(1,4,6,10),]
         Sepal.Length Sepal.Width Petal.Length Petal.Width Species
      1           5.1         3.5          1.4         0.2  setosa
      4           4.6         3.1          1.5         0.2  setosa
      6           5.4         3.9          1.7         0.4  setosa
      10          4.9         3.1          1.5         0.1  setosa
      
    • 例2. data[3, c(1,2)]
      > > data[3, c(1,2)]
        Sepal.Length Sepal.Width
      3          4.7         3.2
      
  2. 抜き出したい位置に、"true"、不要な部分に "false" を、行の数だけもれなく記載したブーリアン型のベクトルを入れる (複数条件の時は、こちらの方が便利です)

2番目については、後で詳しく記載します。

データ内の情報のあつめ方

データ内に、どんな情報があるかを知らないと、さすがに指定はできません。おすすめの方法は下記の順です。

  1. dim(data) # データの大きさを知る (垂直方向に何列、水平方向に何列かが判ります)
  2. データが横に長くなければ、summary(data) で概略を知る
以上、2つです。なぜ、データの大きさを知る必要があるかというと、 あまりに大きいと、画面に情報が表示しきれないからです。ちなみに、今回の例の iris は、summary() 表示をしても問題ない大きさです。
> dim(data)
[1] 150   5
> summary(data)
  Sepal.Length    Sepal.Width     Petal.Length    Petal.Width          Species  
 Min.   :4.300   Min.   :2.000   Min.   :1.000   Min.   :0.100   setosa    :50  
 1st Qu.:5.100   1st Qu.:2.800   1st Qu.:1.600   1st Qu.:0.300   versicolor:50  
 Median :5.800   Median :3.000   Median :4.350   Median :1.300   virginica :50  
 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   

summary() の結果を見ると、はじめ4つの列データは数値データで、5つ目の "Species" が文字データです。

数値データには、おおよ数値分布の目安を示してくれます。

  1. 最小値
  2. 第1四分位
  3. 中央値 (median)
  4. 平均 (mean)
  5. 第3四分位
  6. 最大値

文字列のデータには、度数 (登場回数) の多い順に、5つくらいまでの名前と度数を示してくれます。

もし、全部の列名が知りたい時は、colnames()を使います。

> colnames(data)
[1] "Sepal.Length" "Sepal.Width"  "Petal.Length" "Petal.Width"  "Species"     

summary() で表示しきれない文字列データの度数を調べるには、table() が便利です。

> table(data[,5])

    setosa versicolor  virginica 
        50         50         50 

これで、5番目の "Species" という名前の列 (柱) に、"setosa"、 "versicolor"、 "virginica" という3種のアヤメについて 50 個体ずつ観察されたデータで、観測項目に"Sepal.Length"、"Sepal.Width"、 "Petal.Length"、 "Petal.Width"あることを知ることができます。

"virginica" という "Species" のデータを抜き出してみよう

ここで、先程、説明を保留した、TRUE または、FALSE が、行の数だけ並ぶブーリアン型のベクトルというものが、おおいに役に立ちます。

検索条件を記載するには、演算子の使い方を知る必要があります。

演算子 の簡単な説明

"A" と "B" について、等しいのか、等しくないのかを R に尋ねて、TRUE または、FALSE で、答えてもらうことができます。

  • A と B が等しければ "TRUE" と答えて欲しい時は、 "A == B" と書きます
  • A と B が等しくなければ "TRUE" と答えて欲しい時は、 "A != B" と書きます

ここで、"=" が1つだと、A に B が代入されて、TRUE とも FALSE とも答えてくれません。

数字と文字列で例を上げますよ。

> 1==1
[1] TRUE
> 1!=1
[1] FALSE
> 2==1
[1] FALSE
> 2!=1
[1] TRUE
> "りんご" == "みかん"
[1] FALSE
> "りんご" != "みかん"
[1] TRUE

数字の場合は、不等号でも TRUE または、FALSE で、答えてもらうことができます。

> 1>2
[1] FALSE
> 2>1
[1] TRUE

"Species" 内の "virginica"の位置に "TRUE" が入るブーリアン型のベクトルを手に入れよう

"==" で R に尋ねれば、"Species" 内の何番目が "virginica" なのかを、TRUE または、FALSE が、行の数だけ並ぶブーリアン型のベクトルで答えてくれます。 R に尋ねる前に、"Species" のデータは、iris$Species または、iris[,5] として、"Species" のデータを抜き出しましょう。 後から見たとき、人が見たときに、理解しやすいのは、"iris$Species" という表記です。コードを書く時は、書きやすい方よりも、他人が見て判りやすい方を優先しましょう。

> iris$Species
  [1] setosa     setosa     setosa     setosa     setosa     setosa     setosa     setosa    
  [9] setosa     setosa     setosa     setosa     setosa     setosa     setosa     setosa    
 [17] setosa     setosa     setosa     setosa     setosa     setosa     setosa     setosa    
 [25] setosa     setosa     setosa     setosa     setosa     setosa     setosa     setosa    
 [33] setosa     setosa     setosa     setosa     setosa     setosa     setosa     setosa    
 [41] setosa     setosa     setosa     setosa     setosa     setosa     setosa     setosa    
 [49] setosa     setosa     versicolor versicolor versicolor versicolor versicolor versicolor
 [57] versicolor versicolor versicolor versicolor versicolor versicolor versicolor versicolor

<中間部分は略します>

[145] virginica  virginica  virginica  virginica  virginica  virginica 
Levels: setosa versicolor virginica

このように、150個のデータが抜き出せます。 では、"Species" 内の何番目が "virginica" なのかを、"==" を使って尋ねてみましょう。

> iris$Species=="virginica"
  [1] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [15] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [29] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [43] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [57] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [71] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [85] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
 [99] FALSE FALSE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
[113]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
[127]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE
[141]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE

このブリーアン型のベクトルを使って、"virginica" のデータを抜き出しまぞう。

1つの手順を踏んでから、抜き出します。

  1. ブリーアン型のベクトルを、Row_select に納める
  2. data[Row_select, ]に入れて、データを抜き出し,Subdata に格納する
  3. summary() で、Subdata を調べる
> Row_select <- iris$Species=="virginica"
> Subdata <- data[Row_select, ]
> summary(Subdata)
  Sepal.Length    Sepal.Width     Petal.Length    Petal.Width          Species  
 Min.   :4.900   Min.   :2.200   Min.   :4.500   Min.   :1.400   setosa    : 0  
 1st Qu.:6.225   1st Qu.:2.800   1st Qu.:5.100   1st Qu.:1.800   versicolor: 0  
 Median :6.500   Median :3.000   Median :5.550   Median :2.000   virginica :50  
 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       

Subdata には、virginica 50 個体のデータだけが抜きだされました。

論理演算子を使って、Species が、virginica または、versicolor のデータを抜き出してみよう

「かつ」のとき "&" と「または」の時の "|" を上手に使うと、複数条件に合うものを抽出できるようになります。"&" と "|" に、まずは、慣れてみましょう。

> TRUE & TRUE
[1] TRUE
> TRUE & FALSE
[1] FALSE
> TRUE | TRUE
[1] TRUE
> TRUE | FALSE
[1] TRUE
> FALSE & FALSE
[1] FALSE
> FALSE | FALSE
[1] FALSE

となります。

では、Species が、virginica または、versicolor のデータを抜き出してみましょう。「または」ですから、"|"を使いますね。

  1. Species が、virginica のもの、Species が versicolor であるブリーアン型のベクトルを2つ手に入れる
  2. 「または」 の理論演算子 "|" を使って2つのベクトルを1つにする
  3. data から1つにしたベクトルで抜き出す
#Species が、virginica
> Row_virginica <- iris$Species=="virginica"
# Species が versicolor
> Row_versicolor <- iris$Species=="versicolor"
# 演算子で結合
> Row_select <- Row_virginica | Row_versicolor
# 抜き出します
> Subdata <- data[Row_select,]
# summary() で調べてみよう
> summary(Subdata)
  Sepal.Length    Sepal.Width     Petal.Length    Petal.Width          Species  
 Min.   :4.900   Min.   :2.000   Min.   :3.000   Min.   :1.000   setosa    : 0  
 1st Qu.:5.800   1st Qu.:2.700   1st Qu.:4.375   1st Qu.:1.300   versicolor:50  
 Median :6.300   Median :2.900   Median :4.900   Median :1.600   virginica :50  
 Mean   :6.262   Mean   :2.872   Mean   :4.906   Mean   :1.676                  
 3rd Qu.:6.700   3rd Qu.:3.025   3rd Qu.:5.525   3rd Qu.:2.000                  
 Max.   :7.900   Max.   :3.800   Max.   :6.900   Max.   :2.500  

以上でできました。data[Row_select, c(2,3,5)] としたら、2列目、3列目、5列目だけ抜き出してくれます。

subset 関数について

subset(data, 検索条件, select = 抽出する列名)

「select = 抽出する列名」は記載しなければ、全部の行を対象にしてくれます。

subdata <- subset(iris, Species=="virginica" | Species=="versicolor")

で抜き出しができます。下記でも大丈夫です。

> Row_virginica <- iris$Species=="virginica"
> Row_versicolor <- iris$Species=="versicolor"
> Row_select <- Row_virginica | Row_versicolor
> Subdata <- subset(iris, Row_select)

subset(iris, Row_select, select=c(2,3,5)) と、select のオプションを入れれば、2列目、3列目、5列目だけ抜き出してくれます。

数字との組み合わせ

subdata <- iris[iris$Species=="virginica" | iris$Species=="versicolor" & iris$Sepal.Length>6, ]

など、としたら良いです。数字については、普通は "&" で継ぐことになります。 A | B & C の順に書いた時は、 (A | B) & C という順の計算になります。

オマケで、計算速度比較

> prt <- proc.time()
> subdata <- data[data$Species=="virginica" | data$Species=="versicolor",]
> proc.time()-prt
   ユーザ   システム       経過  
     0.027      0.002      0.025 
>
> prt <- proc.time()
> subdata <- subset(data, Species=="virginica" | Species=="versicolor")
> proc.time()-prt
   ユーザ   システム       経過  
     0.007      0.001      0.007 
> 
> library(dplyr)
> prt <- proc.time()
> subdata<- data %>%
+   dplyr::filter(Species=="virginica" | Species=="versicolor")
> proc.time()-prt
   ユーザ   システム       経過  
     0.014      0.001      0.014 

1万行くらいのデータで見てみたら、どれも似たような速さでした。

dplyr は、抜き出すだけではなく、並び換えたり、集計を加えることもできて多機能で、さらに可読性が良いことが美点ですね。 "%>%" と "dplyr::" という書式にびっくりしますが、この2つは、単なるお作法と割り切れば、実はこわいものでもないですね。dplyr は、抜き出すだけだと、ちょっと、良さが出ない印象です。

ちなみに、文字列に限れば、grep するのが最速ではないでしょうか。これは、たぶん、grep が行番号のベクトルを返すことが理由でしょう。

B! LINE