dplyr は表型データの操作に特化した R のパッケージである。dplyr パッケージには、表型データの中からサブセットを抽出したりする関数や抽出したサブセットに対して集計を行う関数などが多数用意されている。dplyr で扱うデータは数百 MB から 2 GB 前後のサイズを想定している。このサイズを超えるような 10〜100 GB 規模なビッグデータなどを取り扱う場合は、data.table パッケージを使用した方が無難。
dplyr の関数の記述方法が非常にシンプルであり、可読性が良い。dplyr を使用することで、研究者はデータの操作や集計に集中することができ、集計等に関係しない無駄なコードを書かなくて済む。
dplyr パッケージを使わないデータ操作例
R では、データフレームの中からある条件を満たすサブセットを抽出し、そのサブセットの平均値を求めたりすることが簡単にできる。ここで、 rice データセットを使って、データの操作方法を示す。rice データセットは 7 列を持ち、それぞれの列が個体番号(replicate)、ブロック番号(block)、根部乾燥重量(root_dry_mass)、地上部乾燥重量(shoot_dry_mass)、系統処理(trt)、処理(fert)、系統(variety)からなる。
d <- read.table('data/rice.txt', header = TRUE, sep = '\t')
head(d)
## replicate block root_dry_mass shoot_dry_mass trt fert variety
## 1 1 1 56 132 F10 F10 wt
## 2 2 1 66 120 F10 F10 wt
## 3 3 1 40 108 F10 F10 wt
## 4 4 1 43 134 F10 F10 wt
## 5 5 1 55 119 F10 F10 wt
## 6 6 1 66 125 F10 F10 wt
ここで、例えば、各系統(wt 系統および ANU843 系統)に対して、根部乾燥重量を求める場合は、データフレームに対して、次のようの操作を行うことで計算できる。
wt <- d$root_dry_mass[d$variety == 'wt']
ANU843 <- d$root_dry_mass[d$variety == 'ANU843']
print(mean(wt))
## [1] 26.47222
print(mean(ANU843))
## [1] 9.666667
平均値・最大値・最小値などの簡単な値を求めるとき、R の標準関数の一つである aggregate
関数を使うと便利である。
aggregate(d$root_dry_mass, by = list(variety = d$variety), mean)
## variety x
## 1 ANU843 9.666667
## 2 wt 26.472222
wt 系統および ANU843 系統の中に、F10 処理、NH4Cl 処理、NH4NO3 処理の 3 つの処理群が含まれている。ここで、wt 系統と ANU843 系統の各処理群それぞれに対して、根部乾燥重量の平均を計算してみる。
wt.F10 <- d$root_dry_mass[d$variety == 'wt' & d$fert == 'F10']
wt.NH4Cl <- d$root_dry_mass[d$variety == 'wt' & d$fert == 'NH4Cl']
wt.NH4NO3 <- d$root_dry_mass[d$variety == 'wt' & d$fert == 'NH4NO3']
ANU843.F10 <- d$root_dry_mass[d$variety == 'ANU843' & d$fert == 'F10']
ANU843.NH4Cl <- d$root_dry_mass[d$variety == 'ANU843' & d$fert == 'NH4Cl']
ANU843.NH4NO3 <- d$root_dry_mass[d$variety == 'ANU843' & d$fert == 'NH4NO3']
m <- c(mean(wt.F10), mean(wt.NH4Cl), mean(wt.NH4NO3),
mean(ANU843.F10), mean(ANU843.NH4Cl), mean(ANU843.NH4NO3))
print(m)
## [1] 49.500000 12.583333 17.333333 6.000000 9.166667 13.833333
複数条件の組み合わせでも aggregate
関数が使える。例えば次のように by オプションに、2 つの条件を与えればよい。
aggregate(d$root_dry_mass, by = list(variety = d$variety, fert = d$fert), mean)
## variety fert x
## 1 ANU843 F10 6.000000
## 2 wt F10 49.500000
## 3 ANU843 NH4Cl 9.166667
## 4 wt NH4Cl 12.583333
## 5 ANU843 NH4NO3 13.833333
## 6 wt NH4NO3 17.333333
dplyr の基本的な使い方
dplyr を使用してデータ操作を扱うとき、データの流れに着目するとわかりやすい。例えば、d というデータに対して A 処理を行なった後に、B 処理を行い、その結果を x に保存したい場合は、d → A → B → x という流れに着目すると、dplyr のルールに従うと次のように記述することができる。d の内容を関数 A に流すときに %>%
演算子を使用する。次に、A 関数で処理した結果を B 関数に流すときに同様に %>%
演算子を使用する。最後に、関数 B の処理結果を x に代入したいから、R の基本文法により ->
を使用する。
d %>% A %>% B -> x
なお、プログラミング言語では、代入演算子の右側の演算結果を左側に代入するという暗黙なルールがあるので、以下のように書くのが一般的である。
x <- d %>% A %>% B
dplyr の記述ルールを踏まえて、wt 系統の個体数を求めるには、次のように行う。データ d を group_by
関数に流し、ここで variety ごとにグループ分けを行う。グループ分けを行なった後に、各グループをさらに次の summarise
関数に流し、平均を求める処理を行なっている。
d <- read_tsv('data/rice.txt')
head(d)
## # A tibble: 6 x 7
## replicate block root_dry_mass shoot_dry_mass trt fert variety
## <dbl> <dbl> <dbl> <dbl> <chr> <chr> <chr>
## 1 1 1 56 132 F10 F10 wt
## 2 2 1 66 120 F10 F10 wt
## 3 3 1 40 108 F10 F10 wt
## 4 4 1 43 134 F10 F10 wt
## 5 5 1 55 119 F10 F10 wt
## 6 6 1 66 125 F10 F10 wt
variety_ave <- d %>%
group_by(variety) %>%
summarise(mass_ave = mean(root_dry_mass))
head(variety_ave)
## # A tibble: 2 x 2
## variety mass_ave
## <chr> <dbl>
## 1 ANU843 9.67
## 2 wt 26.5
各系統の各処理群に対して平均を求める場合は、aggregate
関数と同様に、group_by
のところに条件を 2 つ書けばよい。
variety_ave <- d %>%
group_by(variety, fert) %>%
summarise(mass_ave = mean(root_dry_mass))
head(variety_ave)
## # A tibble: 6 x 3
## # Groups: variety [2]
## variety fert mass_ave
## <chr> <chr> <dbl>
## 1 ANU843 F10 6
## 2 ANU843 NH4Cl 9.17
## 3 ANU843 NH4NO3 13.8
## 4 wt F10 49.5
## 5 wt NH4Cl 12.6
## 6 wt NH4NO3 17.3
このように、dplyr ではデータの流れに着目して、データの操作や集計を行なっていくことができる。研究者がデータ操作・データ集計に関係のないコードを書く必要がなく、効率的である。
dplyr の基本関数
dplyr パッケージで一般的に知られている関数には、次のようなものがある。
関数 | 動作 |
select | 与えられた条件に基づいて、特定の列を抽出する。 |
filter | 与えられた条件に基づいて、特定の行を抽出する。 |
arrange | 与えられた条件に基づいて、行を並べ替える。 |
group_by | 与えられた条件に基づいて、データセット全体をいくつかのグループに分ける。 |
summarise | 最大値・最小値・平均値を求めるなどのデータの集計を行う。 |
mutate | 既存のデータセットに新しい列を加える。 |
dplyr::select 関数
select
関数は、与えられた条件に基づいて、特定の列を抽出する関数である。例えば rice データセットから、系統(variety)、処理(fert)、根部乾燥重量(root_dry_mass)、地上部乾燥重量(shoot_dry_mass)の 4 列だけを取り出してサブセットを作成したい場合は、次のようにする。
# d.subset <- d[, c('variety', 'fert', 'root_dry_mass', 'shoot_dry_mass')]
d.subset <- d %>% select(variety, fert, root_dry_mass, shoot_dry_mass)
head(d.subset)
## # A tibble: 6 x 4
## variety fert root_dry_mass shoot_dry_mass
## <chr> <chr> <dbl> <dbl>
## 1 wt F10 56 132
## 2 wt F10 66 120
## 3 wt F10 40 108
## 4 wt F10 43 134
## 5 wt F10 55 119
## 6 wt F10 66 125
select
関数に列名を指定するとき、- をつけると、その列を含まないようなサブセットが作成される。
# d.subset <- d[, -c('replicate', 'block', 'trt')]
d.subset <- d %>% select(-replicate, -block, -trt)
head(d.subset)
## # A tibble: 6 x 4
## root_dry_mass shoot_dry_mass fert variety
## <dbl> <dbl> <chr> <chr>
## 1 56 132 F10 wt
## 2 66 120 F10 wt
## 3 40 108 F10 wt
## 4 43 134 F10 wt
## 5 55 119 F10 wt
## 6 66 125 F10 wt
select
関数に列名ではなく、条件を与えて列名を列を抽出することもできる。例えば、列名に mass を含む列を抽出する場合は次のようにする。
d.subset <- d %>% select(contains('mass'))
head(d.subset)
## # A tibble: 6 x 2
## root_dry_mass shoot_dry_mass
## <dbl> <dbl>
## 1 56 132
## 2 66 120
## 3 40 108
## 4 43 134
## 5 55 119
## 6 66 125
抽出条件を与えるときに使用した contains
関数の他に、starts_with
、ends_with
、matches
などの関数も使用できる。
dplyr::filter 関数
filter
関数は与えられた条件に基づいて、特定の行を抽出する関数である。例えば、根部乾燥重量が 50 以上の行を抽出するには、次のように行う。
d.subset <- d %>% filter(root_dry_mass > 50)
head(d.subset)
## # A tibble: 5 x 7
## replicate block root_dry_mass shoot_dry_mass trt fert variety
## <dbl> <dbl> <dbl> <dbl> <chr> <chr> <chr>
## 1 1 1 56 132 F10 F10 wt
## 2 2 1 66 120 F10 F10 wt
## 3 5 1 55 119 F10 F10 wt
## 4 6 1 66 125 F10 F10 wt
## 5 8 2 67 122 F10 F10 wt
条件が複数ある場合は、それらの条件を順に filter 関数に加えればよい。例えば、wt 系統かつ根部乾燥重量が 50 以上の行を抽出するには、次のようにする。
d.subset <- d %>% filter(root_dry_mass >= 50, variety == 'wt')
head(d.subset)
## # A tibble: 5 x 7
## replicate block root_dry_mass shoot_dry_mass trt fert variety
## <dbl> <dbl> <dbl> <dbl> <chr> <chr> <chr>
## 1 1 1 56 132 F10 F10 wt
## 2 2 1 66 120 F10 F10 wt
## 3 5 1 55 119 F10 F10 wt
## 4 6 1 66 125 F10 F10 wt
## 5 8 2 67 122 F10 F10 wt
複数条件の場合は &
および |
を使って AND 演算および OR 演算を行うことができる。例えば、「wt 系統」、「根部乾燥重量が 50 以上」、「地上部乾燥重量が 120 以上」の 3 つの条件を同時に満たす行を抽出する場合は、次のようにする。 ただし、複数の条件をカンマで区切って与える場合は AND 演算の結果が行われる。
d.subset <- d %>% filter(variety == 'wt', root_dry_mass >= 50, shoot_dry_mass >= 120)
# d.subset <- d %>% filter(variety == 'wt' & root_dry_mass >= 50 & shoot_dry_mass >= 120)
head(d.subset)
## # A tibble: 4 x 7
## replicate block root_dry_mass shoot_dry_mass trt fert variety
## <dbl> <dbl> <dbl> <dbl> <chr> <chr> <chr>
## 1 1 1 56 132 F10 F10 wt
## 2 2 1 66 120 F10 F10 wt
## 3 6 1 66 125 F10 F10 wt
## 4 8 2 67 122 F10 F10 wt
wt 系統で、「根部乾燥重量が 50 以上」または「地上部乾燥重量が 120 以上」の条件を満たす行を抽出する場合は、次のようにする。
d.subset <- d %>% filter(variety == 'wt', root_dry_mass >= 50 | shoot_dry_mass >= 120)
# d.subset <- d %>% filter(variety == 'wt' & (root_dry_mass >= 50 | shoot_dry_mass >= 120))
head(d.subset)
## # A tibble: 6 x 7
## replicate block root_dry_mass shoot_dry_mass trt fert variety
## <dbl> <dbl> <dbl> <dbl> <chr> <chr> <chr>
## 1 1 1 56 132 F10 F10 wt
## 2 2 1 66 120 F10 F10 wt
## 3 4 1 43 134 F10 F10 wt
## 4 5 1 55 119 F10 F10 wt
## 5 6 1 66 125 F10 F10 wt
## 6 8 2 67 122 F10 F10 wt
別の例として、処理群が F10 以外の行を抽出するときは次のように !
を使う。
# d.subset <- d %>% filter(!(fert == 'F10'))
d.subset <- d %>% filter(fert != 'F10')
head(d.subset)
## # A tibble: 6 x 7
## replicate block root_dry_mass shoot_dry_mass trt fert variety
## <dbl> <dbl> <dbl> <dbl> <chr> <chr> <chr>
## 1 1 1 12 45 NH4Cl NH4Cl wt
## 2 2 1 20 60 NH4Cl NH4Cl wt
## 3 3 1 21 87 NH4Cl NH4Cl wt
## 4 4 1 15 57 NH4Cl NH4Cl wt
## 5 5 1 5 26 NH4Cl NH4Cl wt
## 6 6 1 18 78 NH4Cl NH4Cl wt
dplyr::arrange 関数
arrange
関数は、与えられた条件に基づいて、行を並べ替える。データに欠損値 NA が含まれる場合は、arrange
関数を使って並べ替えるとき、昇順・降順に関わらず、欠損値 NA は常にデータセットの最下部に並べ替えられる。例えば、rice データに対して、根部乾燥重量に基づいて昇順に並べ替えたい場合は、arrange 関数に列名 root_dry_mass を指定すればよい。
d.subset <- d %>% arrange(root_dry_mass)
head(d.subset)
## # A tibble: 6 x 7
## replicate block root_dry_mass shoot_dry_mass trt fert variety
## <dbl> <dbl> <dbl> <dbl> <chr> <chr> <chr>
## 1 7 2 1 35 NH4Cl +ANU843 NH4Cl ANU843
## 2 10 2 3 5 F10 +ANU843 F10 ANU843
## 3 2 1 4 6 F10 +ANU843 F10 ANU843
## 4 3 1 4 3 F10 +ANU843 F10 ANU843
## 5 1 1 4 22 NH4Cl +ANU843 NH4Cl ANU843
## 6 5 1 5 26 NH4Cl NH4Cl wt
根部乾燥重量に基づいて降順に並べ替えたい場合は、desc
関数を利用するか、列名に -
をつけるかで並べ替えられる。
# d.subset <- d %>% arrange(-root_dry_mass)
d.subset <- d %gt;% arrange(desc(root_dry_mass))
head(d.subset)
## # A tibble: 6 x 7
## replicate block root_dry_mass shoot_dry_mass trt fert variety
## <dbl> <dbl> <dbl> <dbl> <chr> <chr> <chr>
## 1 8 2 67 122 F10 F10 wt
## 2 2 1 66 120 F10 F10 wt
## 3 6 1 66 125 F10 F10 wt
## 4 1 1 56 132 F10 F10 wt
## 5 5 1 55 119 F10 F10 wt
## 6 11 2 44 37 F10 F10 wt
次に、根部乾燥重量に関して降順で、地上部乾燥重量に関しては昇順で並べるときは、filter
関数と同様に複数の条件を順に与えればよい。filter
関数に複数の条件を代入した場合は、左側の条件が優先される。
d.subset <- d %>% arrange(desc(root_dry_mass), shoot_dry_mass)
head(d.subset)
## # A tibble: 6 x 7
## replicate block root_dry_mass shoot_dry_mass trt fert variety
## <dbl> <dbl> <dbl> <dbl> <chr> <chr> <chr>
## 1 8 2 67 122 F10 F10 wt
## 2 2 1 66 120 F10 F10 wt
## 3 6 1 66 125 F10 F10 wt
## 4 1 1 56 132 F10 F10 wt
## 5 5 1 55 119 F10 F10 wt
## 6 11 2 44 37 F10 F10 wt
これまでに紹介した select
関数、filter
関数および arrange
関数を組み合わせることで、データセットを柔軟に操作できる。例えば、次のような操作も簡単に行える。
- 系統が wt の行を抽出し、
- rice データから、処理(fert)、根部乾燥重量(root_dry_mass)、地上部乾燥重量(shoot_dry_mass)の 4 列だけを抽出し
- 手順 1-2 で抽出したサブセットを根部乾燥重量に基づいて降順に並べる
wt.subset <- d %>%
filter(variety == 'wt') %>%
select(fert, root_dry_mass, shoot_dry_mass) %>%
arrange(desc(root_dry_mass))
head(wt.subset)
## # A tibble: 6 x 3
## fert root_dry_mass shoot_dry_mass
## <chr> <dbl> <dbl>
## 1 F10 67 122
## 2 F10 66 120
## 3 F10 66 125
## 4 F10 56 132
## 5 F10 55 119
## 6 F10 44 37
dplyr::group_by 関数
group_by
関数は、与えられた条件い基づいて、データをいくつかのグループ分けるときに使用する関数である。group_by
関数で処理した結果を print
しても、group_by
関数の処理前と処理後とでは見た目的には変わらない。しかし、group_by
関数で処理した後のデータセットには、グループ情報が属性として保存される。例えば、rice データセットに対して、各系統ごとにグループ分けを行う場合は次のようにする。
head(d)
## # A tibble: 6 x 7
## replicate block root_dry_mass shoot_dry_mass trt fert variety
## <dbl> <dbl> <dbl> <dbl> <chr> <chr> <chr>
## 1 1 1 56 132 F10 F10 wt
## 2 2 1 66 120 F10 F10 wt
## 3 3 1 40 108 F10 F10 wt
## 4 4 1 43 134 F10 F10 wt
## 5 5 1 55 119 F10 F10 wt
## 6 6 1 66 125 F10 F10 wt
d.grouped <- d %>% group_by(variety)
head(d.grouped)
## # A tibble: 6 x 7
## # Groups: variety [1]
## replicate block root_dry_mass shoot_dry_mass trt fert variety
## <dbl> <dbl> <dbl> <dbl> <chr> <chr> <chr>
## 1 1 1 56 132 F10 F10 wt
## 2 2 1 66 120 F10 F10 wt
## 3 3 1 40 108 F10 F10 wt
## 4 4 1 43 134 F10 F10 wt
## 5 5 1 55 119 F10 F10 wt
## 6 6 1 66 125 F10 F10 wt
group_by
関数で追加されたグループ情報を削除したい場合は ungroup
関数を使用する。
d.grouped <- d %>% group_by(variety)
d.ungrouped <- d.grouped %>% ungroup()
dplyr::summarise 関数
summarise
関数はデータの集計を行う関数で、group_by
関数などと一緒に使われることが多い。例えば、rice データセットに対して、各系統ごとに根部乾燥重量および地上部乾燥重量の平均値を求めたい場合は、まず variety 列に基づいてグループ分けを行い、続いて根部乾燥重量および地上部乾燥重量の列に対して集計処理(平均値計算)を行う。
d.massave <- d %>%
group_by(variety) %>%
summarise(root_dry_mass_ave = mean(root_dry_mass),
shoot_dry_mass_ave = mean(shoot_dry_mass))
head(d.massave)
## # A tibble: 2 x 3
## variety root_dry_mass_ave shoot_dry_mass_ave
## <chr> <dbl> <dbl>
## 1 ANU843 9.67 41.8
## 2 wt 26.5 77.3
各系統の各処理群に対して平均を求める場合は、group_by
のところに条件を 2 つ書く。
d.massave <- d %>%
group_by(variety, fert) %>%
summarise(root_dry_mass_ave = mean(root_dry_mass),
shoot_dry_mass_ave = mean(shoot_dry_mass))
head(d.massave)
## # A tibble: 6 x 4
## # Groups: variety [2]
## variety fert root_dry_mass_ave shoot_dry_mass_ave
## <chr> <chr> <dbl> <dbl>
## 1 ANU843 F10 6 7.33
## 2 ANU843 NH4Cl 9.17 46.6
## 3 ANU843 NH4NO3 13.8 71.5
## 4 wt F10 49.5 108.
## 5 wt NH4Cl 12.6 50.2
## 6 wt NH4NO3 17.3 73.3
summarise
の中で使われている mean
関数は R の標準関数である。そのため、欠損地を無視して平均値を求めたい場合は、次のように書けばよい。
d.massave <- d %>%
group_by(variety, fert) %>%
summarise(root_dry_mass_ave = mean(root_dry_mass, na.rm = TRUE),
shoot_dry_mass_ave = mean(shoot_dry_mass, na.rm = TRUE))
head(d.massave)
## # A tibble: 6 x 4
## # Groups: variety [2]
## variety fert root_dry_mass_ave shoot_dry_mass_ave
## <chr> <chr> <dbl> <dbl>
## 1 ANU843 F10 6 7.33
## 2 ANU843 NH4Cl 9.17 46.6
## 3 ANU843 NH4NO3 13.8 71.5
## 4 wt F10 49.5 108.
## 5 wt NH4Cl 12.6 50.2
## 6 wt NH4NO3 17.3 73.3
summarise
中で使用する関数を独自に定義することもできる。例えば、四分位範囲(IQR)を求める関数 calc.IQR
を独自に定義して、それを summarise
中で使う場合は次のようにする。
calc.IQR <- function(x) {
q1 <- quantile(x, probs = 0.25)
q3 <- quantile(x, probs = 0.75)
iqr <- q3 - q1
return(iqr)
}
d.massave <- d %>%
group_by(variety, fert) %>%
summarise(root_dry_mass_IQR = calc.IQR(root_dry_mass),
shoot_dry_mass_IQR = calc.IQR(shoot_dry_mass))
head(d.massave)
## # A tibble: 6 x 4
## # Groups: variety [2]
## variety fert root_dry_mass_IQR shoot_dry_mass_IQR
## <chr> <chr> <dbl> <dbl>
## 1 ANU843 F10 2.25 4
## 2 ANU843 NH4Cl 2.5 23.2
## 3 ANU843 NH4NO3 8 25.8
## 4 wt F10 17.8 17.2
## 5 wt NH4Cl 11 19.2
## 6 wt NH4NO3 8 24
dplyr::mutate 関数
mutate
関数は、既存のデータに新しい列を加えたいときに使用する。例えば、根部乾燥重量と地上部乾燥重量の和を計算して、それを dry_mass 列名で既存のデータに追加し、さらに根部乾燥重量が乾燥重量全体における割合を root_dry_mass_ratio 列名で既存のデータに追加する場合は、次のようにする。
d.new <- d %>% mutate(dry_mass = root_dry_mass + shoot_dry_mass,
root_dry_mass_ratio = root_dry_mass / dry_mass)
head(d.new)
## # A tibble: 6 x 9
## replicate block root_dry_mass shoot_dry_mass trt fert variety dry_mass
## <dbl> <dbl> <dbl> <dbl> <chr> <chr> <chr> <dbl>
## 1 1 1 56 132 F10 F10 wt 188
## 2 2 1 66 120 F10 F10 wt 186
## 3 3 1 40 108 F10 F10 wt 148
## 4 4 1 43 134 F10 F10 wt 177
## 5 5 1 55 119 F10 F10 wt 174
## 6 6 1 66 125 F10 F10 wt 191
## # … with 1 more variable: root_dry_mass_ratio <dbl>
新たに作成した列名だけを新しいデータとしたい場合は mutate
関数の代わりに transmute
関数を使う。この際、既存の列名を残すこともできる。
d.new <- d %>% transmute(variety,
dry_mass = root_dry_mass + shoot_dry_mass,
root_dry_mass_ratio = root_dry_mass / dry_mass)
head(d.new)
## # A tibble: 6 x 3
## variety dry_mass root_dry_mass_ratio
## <chr> <dbl> <dbl>
## 1 wt 188 0.298
## 2 wt 186 0.355
## 3 wt 148 0.270
## 4 wt 177 0.243
## 5 wt 174 0.316
## 6 wt 191 0.346