7 tidyverse

tidyverse は CRAN パッケージの一つで、tidyr、readr、dplyr、ggplot2 などのパッケージをまとめたパッケージである。 これらのパッケージには、データの読み書き、データの処理、データの可視化などの機能が実装されている。 R の標準関数を使用しても、これらの作業を行うことは可能だが、 tidyverse の機能を使うことで、データの取り扱いが直感的でわかりやすく、 コーディング量も大幅に減らすことができるため、解析に専念できる。 tidyverse の機能を積極的に使用している R は、標準機能の R と区別して、modern R とも呼ばれている。 ここでは、modern R の基本的な使い方を解説していく。 なお、modern R の詳細な使い方に関しては、次の文献を参考にして勉強してくだい。

7.1 tidyverse 準備

tidyverse パッケージは CRAN のパッケージとして提供されている。 このパッケージをインストールするには install.packages 関数を使用する。 tidyverse パッケージをインストールすることで、 readr パッケージ、dplyr パッケージ、ggplot2 パッケージ、tidyr パッケージなど も自動的にインストールされる。

install.packages('tidyverse')

tidyverse パッケージの機能を呼び出して使うとき、 他のパッケージと同様に library 関数あるいは require 関数を使う。 tidyverse パッケージを呼び出すと、自動的に dplyr や ggplot2 などのパッケージも呼び出される。

library(tidyverse)
## ── Attaching packages ─────────────────────────────────────────────────────────────────────────────────────── tidyverse 1.3.0 ──
## ✓ ggplot2 3.3.2     ✓ purrr   0.3.4
## ✓ tibble  3.0.3     ✓ dplyr   1.0.1
## ✓ tidyr   1.1.1     ✓ stringr 1.4.0
## ✓ readr   1.3.1     ✓ forcats 0.5.0
## ── Conflicts ────────────────────────────────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
## x dplyr::filter() masks stats::filter()
## x dplyr::lag()    masks stats::lag()

tidyverse を呼び出すと、R の標準関数が書き換えられる。 その情報が Conflicts に書かれている。 上の例では、R の標準パッケージ(stats)にある標準関数の filter 関数および lag 関数が、 tidyverse 中の dplyr パッケージの filter 関数および lag 関数にマスク(上書き)されたことを表している。 したがって、tidyverse パッケージを呼び出したあとに、R のプログラムがエラー出るようになったとき、 自分のプログラムで使っている標準関数が、tidyverse の関数によって上書きされていないかを確認するとよい。 上書きされた場合、自分のプログラムに書かれている関数の前に stats:: をつけると R 本来の標準関数が使えるようになる。

x <- c(1.2, 2.4, 3.6, 4.8)

lag(x)            # before
stats::lag(x)     # after

7.2 readr

readr パッケージには、データの読み書きや文字列の処理などの機能が実装されている。 readr が提供している関数は、R の標準関数に比べて、処理速度が速い。 データが大きいときに、データ処理の効率化が期待できる。

ファイルの読み込み

readr パッケージの関数を使ってファイルを読み込むには、read_csv 関数あるいは read_tsv 関数を使う。 これらの関数は R の標準関数の read.csvread.table 関数とほぼ同じ機能を持つ。 しかし、以下に挙げた何点かにおいて、readr パッケージの関数が優れている。

  • readr パッケージの関数は、R 標準関数に比べ、ファイルの読み込み速度が速い。
  • read_csvread_tsv 関数にはお節介な機能がない。
    • 文字(character)を勝手に因子型(factor)に変換しない。
    • 列名を勝手に X.1、X.2、X.3 などのように変換しない。
  • 読み込まれたデータは tibble 型のオブジェクとして保存される。

実際の使い方は、次のようになっている。

# CSV file
d <- read_csv('data/rice.csv')
head(d)

# TSV file
d <- read_tsv('data/rice.txt')
head(d)

なお、区切り文字を指定して、テキストファイルからデータを読み込むときは、read_delim を使う。 このとき、区切り文字を delim オプションで指定する。

d <- read_delim('data/rice.txt', delim = '\t')
head(d)

ファイル書き出し

ファイル書き出し用の関数には write_csvwrite_tsvwrite_delim などの関数が用意されている。 これら関数を使用して、データをファイルに保存する際に、文字コードが UTF-8 として保存される。 使い方は R の標準関数とほぼ同じように使う。

write_csv(d, 'data/results.csv')
write_tsv(d, 'data/results.csv')
write_tsv(d, 'data/results.csv', delim = ';')

文字列パース

readr パッケージには文字列操作を行うための関数も数多く用意されている。 主な関数を次の表に示した。

関数 機能
parse_logical 文字列を理論型(TRUE または FALSE)に変換する。
parse_integer 文字列を整数型に変換する。
parse_double 文字列を小数型に変換する。
parse_number 文字列中に含まれている数字を数値に変換する。
parse_character 文字列を文字型に変換する。UTF-8 以外の文字列コードから UTF-8 に変換するときに使用する。
parse_datetime 文字列を datetime 型に変換する。
parse_date 文字列を date 型に変換する。
parse_time 文字列を time 型に変換する。

文字列を理論型や数値に変換する例。

x <- c('TRUE', 'FALSE')
y <- parse_logical(x)
print(y)
## [1]  TRUE FALSE
x <- c('64')
y <- parse_integer(x)
print(y)
## [1] 64
x <- c('3.14')
y <- parse_integer(x)
## Warning: 1 parsing failure.
## row col               expected actual
##   1  -- no trailing characters    .14
print(y)
## [1] NA
## attr(,"problems")
## # A tibble: 1 x 4
##     row   col expected               actual
##   <int> <int> <chr>                  <chr> 
## 1     1    NA no trailing characters .14
x <- c('3.14')
y <- parse_double(x)
print(y)
## [1] 3.14

文字列で記載された数値の中に桁区切りとしてカンマが使われることがある。 このように記述された文字列を数値に変換するには、parse_number 関数を使用する。 なお、桁区切りは、国や地域によって異なり、 例えばヨーロッパの多くの国ではピリオドを使い、スイスではシングルクォテーションマークを使っている。 桁区切りがカンマ以外の場合は、grouping_mask オプションで指定できる。

print(parse_number('123,456,789'))  # US, JP, ...
## [1] 123456789
print(parse_number('123.456.789', locale = locale(grouping_mark = '.'))) # EU
## [1] 123456789
print(parse_number("123'456'789", locale = locale(grouping_mark = "'"))) # CH
## [1] 123456789

文字コードの変換は parse_character 関数を使う。 次は、Shift-JIS の日本語文字列を UTF-8 の文字列に変換する例である。 Excel で作られた CSV ファイルを解析するときに便利である。

x <- '\x82\xb1\x82\xf1\x82\xc9\x82\xbf\x82\xcd'
y <- parse_character(x, locale = locale(encoding = 'Shift-JIS'))
print(y)
## [1] "こんにちは"

文字列で記載された日時を日付型や時間型に変換する例。 日付型や時間型に変更した後に、足したり引いたりすることができるようになる。

x <- '2019-10-20 10:00:00'
y <- parse_datetime(x)
print(y)
## [1] "2019-10-20 10:00:00 UTC"
x <- '2019-10-20'
y <- parse_date(x)
print(y)
## [1] "2019-10-20"
print(y + 20)
## [1] "2019-11-09"
x <- '23:00:00'
y <- parse_time(x)
print(y)
## 23:00:00
x <- '11:00:00 pm'
y <- parse_time(x)
print(y)
## 23:00:00

7.3 dplyr

dplyr は表データの操作に特化した R のパッケージである。 特定の行あるいは列を抽出したり、抽出したサブセットに対して集計を行ったりするの ような機能が多く実装されている。 dplyr パッケージで提供している各関数は、2 GB 前後までのデータに対応している。 このサイズを超えるような 10〜100 GB 規模のビッグデータなどを取り扱う場合は、 data.table パッケージの使用が推奨される。

dplyr の基本的な使い方

dplyr パッケージの関数でデータを扱うとき、データの流れに着目するとわかりやすい。 例えば、d というデータに対して、 A 処理を行なった後に B 処理を行い、そして、その処理結果を x に保存したいとき、 データの流れは dABx のようになっている。 これを dplyr のルールにしたがって記述すると次のようになる。

d %>% A %>% B -> x

dplyr パッケージでは、d の内容を関数 A に流すときに %>% 演算子を使用する。 次に、A 関数で処理した結果を B 関数に流すときも同様に %>% 演算子を使用する。 最後に、関数 B の処理結果を x に代入したいから、R の代入演算子(<- または ->)を使用する。

データの流れとして上のように記述した方がわかりやすい。 しかし、R のスタンダードな書き方では、変数名を左側に、処理を右側に記述することが一般的である。 そのため、上の例ではエラーなく実行可能なものの、次のように書いた方がより一般的である。

x <- d %>% A %>% B

各関数の説明をあとで解説するが、 例えばファイルからデータを読み取り、variety 列の情報に基づいて、 データ全体を wt と ANU843 のサブセットにグループ分けし、 それぞれのサブセットで root_dry_mass 列の平均を求める処理は、次のように書ける。

d <- read_tsv('data/rice.txt')
## Parsed with column specification:
## cols(
##   replicate = col_double(),
##   block = col_double(),
##   root_dry_mass = col_double(),
##   shoot_dry_mass = col_double(),
##   trt = col_character(),
##   fert = col_character(),
##   variety = col_character()
## )
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))
## `summarise()` ungrouping output (override with `.groups` argument)
head(variety_ave)
## # A tibble: 2 x 2
##   variety mass_ave
##   <chr>      <dbl>
## 1 ANU843      9.67
## 2 wt         26.5

以下では rice.txt データを使って dplyr の説明を続ける。 このデータの列名やデータの構成を headtail などの関数を使って一度確認しておくとよい。

dplyr の基本関数

dplyr パッケージに実装されたデータ操作用の関数を以下の表に示した。 なお、表に示した関数は dplyr パッケージの関数の一部にすぎない。

関数 動作
select 与えられた条件に基づいて、特定の列を抽出する。
filter 与えられた条件に基づいて、特定の行を抽出する。
arrange 与えられた条件に基づいて、行を並べ替える。
group_by 与えられた条件に基づいて、データセット全体をいくつかのグループに分ける。
summarise 最大値・最小値・平均値を求めるなどのデータの集計を行う。
mutate 既存のデータセットに新しい列を加える。
inner_join 2 つの表データを内部結合させる。
left_join 2 つの表データを左外部結合させる。
right_join 2 つの表データを右外部結合させる。
full_join 2 つの表データを完全外部結合させる。

dplyr::select 関数

select 関数は、与えられた条件に基づいて、特定の列を抽出する関数である。 例えば rice データセットから、系統(variety)、処理(fert)、根部乾燥重量(root_dry_mass)、 地上部乾燥重量(shoot_dry_mass)の 4 列だけを取り出してサブセットを作成したい場合は、次のようにする。

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 %>% 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_withends_withmatches などの関数も使用できる。

dplyr::filter 関数

filter 関数は与えられた条件に基づいて、特定の行を抽出する関数である。 例えば、根部乾燥重量(root_dry_mass 列)が 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 関数に加えればよい。 例えば、variety 列が wt かつ root_dry_mass 列が 50 以上の行を抽出するには、次のようにする。 複数の条件をカンマで区切ることで、自動的に AND 演算が行われる。

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

条件が 2 以上でも同じように行える。 例えば、「wt 系統」、「根部乾燥重量が 50 以上」、「地上部乾燥重量が 120 以上」の 3 つの条件を 同時に満たす行を抽出する場合は、次のようにする。

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

なお、AND 演算を明記したい場合は、次のようにカンマではなく、& 演算子を使って複数の条件をつなげる。

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

複数の条件に対する理論演算の中で、AND 演算のほかに OR 演算というものもある。 OR 演算の場合は | 演算子を使用する。 例えば、「根部乾燥重量が 50 以上」または「地上部乾燥重量が 120 以上」の条件を満たす行を抽出する場合は、 次のようにする。

d_subset <- d %>% filter(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

AND 演算と OR 演算を組み合わせることで、複雑な抽出条件を作ることができる。 例えば、wt 系統の「根部乾燥重量が 50 以上」または「地上部乾燥重量が 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 %>% 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 関数と同様に複数の条件を順に与えればよい。 なお、arrange 関数に複数の条件を代入した場合は、左側の条件が優先されることに注意。 例えば、根部乾燥重量に関して降順で、地上部乾燥重量に関しては昇順で並べるときは、次のように記述する。

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 関数を組み合わせて使うことで、 様々な処理が行えるようになる。 例えば、次のような操作も容易に行える。

  1. rice データセットから系統(variety)が wt の行を抽出し、
  2. 次に、処理(fert)、根部乾燥重量(root_dry_mass)、地上部乾燥重量(shoot_dry_mass)の 4 列だけを抽出し
  3. そのサブセットを地上部乾燥重量に基づいて降順に並べる
wt.subset <- d %>%
              filter(variety == 'wt') %>%
              select(fert, root_dry_mass, shoot_dry_mass) %>%
              arrange(desc(shoot_dry_mass))
head(wt.subset)
## # A tibble: 6 x 3
##   fert   root_dry_mass shoot_dry_mass
##   <chr>          <dbl>          <dbl>
## 1 F10               43            134
## 2 F10               56            132
## 3 NH4NO3            23            129
## 4 F10               66            125
## 5 F10               67            122
## 6 F10               66            120

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))
## `summarise()` ungrouping output (override with `.groups` argument)
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))
## `summarise()` regrouping output by 'variety' (override with `.groups` argument)
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 の標準関数である。 R 標準の mean 関数では、計算元が値に NA が含まれると、その平均値の計算結果も NA となる。 そのため、欠損地を無視して平均値を求めたい場合は、次のように書けばよい。

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))
## `summarise()` regrouping output by 'variety' (override with `.groups` argument)
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))
## `summarise()` regrouping output by 'variety' (override with `.groups` argument)
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 関数は、既存のデータに新しい列を加えたいときに使う関数である。 例えば、根部乾燥重量(root_dry_mass)と地上部乾燥重量(shoot_dry_mass)の和を計算して、 それを dry_mass 列名で既存のデータに追加する場合は次のように記述する。

d_new <- d %>% mutate(dry_mass = root_dry_mass + shoot_dry_mass)
head(d_new)
## # A tibble: 6 x 8
##   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

mutate 関数の中で、作成した直後の変数を使って、さらに別の計算を行うことができる。 次の例では、まず dry_mass というデータを新規追加し、 そのすぐあとに dry_mass のデータを使って、計算などを行っている。

d_new <- d %>% mutate(dry_mass = root_dry_mass + shoot_dry_mass,
                      root_dry_mass_ratio = root_dry_mass / dry_mass,
                      shoot_dry_mass_ratio = shoot_dry_mass / dry_mass)
head(d_new)
## # A tibble: 6 x 10
##   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 2 more variables: root_dry_mass_ratio <dbl>,
## #   shoot_dry_mass_ratio <dbl>

新たに作成した列名だけを新しいデータフレーム(tibble)として保存したい場合は、 mutate 関数の代わりに transmute 関数を使う。

d_new <- d %>% transmute(dry_mass = root_dry_mass + shoot_dry_mass,
                         root_dry_mass_ratio = root_dry_mass / dry_mass,
                         shoot_dry_mass_ratio = shoot_dry_mass / dry_mass)
head(d_new)
## # A tibble: 6 x 3
##   dry_mass root_dry_mass_ratio shoot_dry_mass_ratio
##      <dbl>               <dbl>                <dbl>
## 1      188               0.298                0.702
## 2      186               0.355                0.645
## 3      148               0.270                0.730
## 4      177               0.243                0.757
## 5      174               0.316                0.684
## 6      191               0.346                0.654

dplyr::inner_join 関数

inner_join 関数は 2 つの表データを内部結合するための関数である。 例えば、次のように d1d2 の表を用意し、 d1fruit 列と d2fruit 列を参照列として、 2 つの表を結合させる際に、次のようにする。 ただし、内部結合では、両方の参照列に存在するキーのみに対して、結合が行われる。 片方の参照列に存在し、もう片方に存在していないキーの値は結合されない。

d1 <- dplyr::data_frame(fruit = c('apple', 'banana', 'cherry', 'orange'),
                        weight = c(42, 12, 3, 39))
## Warning: `data_frame()` is deprecated as of tibble 1.1.0.
## Please use `tibble()` instead.
## This warning is displayed once every 8 hours.
## Call `lifecycle::last_warnings()` to see where this warning was generated.
d2 <- dplyr::data_frame(fruit = c('apple', 'banana', 'cherry', 'watermelon'),
                        volume = c(481, 436, 38, 3924))

d <- dplyr::inner_join(d1, d2, by = 'fruit')
d
## # A tibble: 3 x 3
##   fruit  weight volume
##   <chr>   <dbl>  <dbl>
## 1 apple      42    481
## 2 banana     12    436
## 3 cherry      3     38

2 つの表データの参照列の名前が異なる時は、by オプションで次のように指定する。

d1 <- dplyr::data_frame(fruit_x = c('apple', 'banana', 'cherry', 'orange'),
                        weight = c(42, 12, 3, 39))
d2 <- dplyr::data_frame(fruit_y = c('apple', 'banana', 'cherry', 'watermelon'),
                        volume = c(481, 436, 38, 3924))

d <- dplyr::inner_join(d1, d2, by = c('fruit_x' = 'fruit_y'))
d
## # A tibble: 3 x 3
##   fruit_x weight volume
##   <chr>    <dbl>  <dbl>
## 1 apple       42    481
## 2 banana      12    436
## 3 cherry       3     38

dplyr::left_join 関数

left_join 関数は 2 つの表データを左外部結合するための関数である。 左外部結合では、2 つの表データを結合するとき、左側の表の参照列が基準となって結合が行われる。 このため、左側の方の参照列に存在し、右側の方に存在していないキーに対応する行の値は、NA で補填される。

d1 <- dplyr::data_frame(fruit = c('apple', 'banana', 'cherry', 'orange'),
                        weight = c(42, 12, 3, 39))
d2 <- dplyr::data_frame(fruit = c('apple', 'banana', 'cherry', 'watermelon'),
                        volume = c(481, 436, 38, 3924))

d <- dplyr::left_join(d1, d2, by = 'fruit')
d
## # A tibble: 4 x 3
##   fruit  weight volume
##   <chr>   <dbl>  <dbl>
## 1 apple      42    481
## 2 banana     12    436
## 3 cherry      3     38
## 4 orange     39     NA

dplyr::right_join 関数

right_join 関数は 2 つの表データを右外部結合するための関数である。 右外部結合では、2 つの表データを結合するとき、右側の表の参照列が基準となって結合が行われる。 このため、右側の方の参照列に存在し、左側の方に存在していないキーに対応する行の値は、NA で補填される

d1 <- dplyr::data_frame(fruit = c('apple', 'banana', 'cherry', 'orange'),
                        weight = c(42, 12, 3, 39))
d2 <- dplyr::data_frame(fruit = c('apple', 'banana', 'cherry', 'watermelon'),
                        volume = c(481, 436, 38, 3924))

d <- dplyr::right_join(d1, d2, by = 'fruit')
d
## # A tibble: 4 x 3
##   fruit      weight volume
##   <chr>       <dbl>  <dbl>
## 1 apple          42    481
## 2 banana         12    436
## 3 cherry          3     38
## 4 watermelon     NA   3924

dplyr::full_join 関数

full_join 関数は 2 つの表データを外部結合するための関数である。 片方の参照列に存在し、もう片方の参照列に存在してないようなキーの行の値は NA で補填される。

d1 <- dplyr::data_frame(fruit = c('apple', 'banana', 'cherry', 'orange'),
                        weight = c(42, 12, 3, 39))
d2 <- dplyr::data_frame(fruit = c('apple', 'banana', 'cherry', 'watermelon'),
                        volume = c(481, 436, 38, 3924))

d <- dplyr::full_join(d1, d2, by = 'fruit')
d
## # A tibble: 5 x 3
##   fruit      weight volume
##   <chr>       <dbl>  <dbl>
## 1 apple          42    481
## 2 banana         12    436
## 3 cherry          3     38
## 4 orange         39     NA
## 5 watermelon     NA   3924

7.4 tidyr

tidyr はデータフレーム(正確には tibble 型のオブジェクト)の並べ方を展開したり、 集約したりする際に利用する関数が多く用意されている。

tidyr パッケージの機能を説明するために、iris のデータセットを使用する。 このデータセットは、次のように 150 行、5 列からなるデータフレームである。

iris <- read_tsv('data/iris.txt')
## Parsed with column specification:
## cols(
##   ID = col_double(),
##   Sepal.Length = col_double(),
##   Sepal.Width = col_double(),
##   Petal.Length = col_double(),
##   Petal.Width = col_double(),
##   Species = col_character()
## )
head(iris)
## # A tibble: 6 x 6
##      ID Sepal.Length Sepal.Width Petal.Length Petal.Width Species
##   <dbl>        <dbl>       <dbl>        <dbl>       <dbl> <chr>  
## 1     1          5.1         3.5          1.4         0.2 setosa 
## 2     2          4.9         3            1.4         0.2 setosa 
## 3     3          4.7         3.2          1.3         0.2 setosa 
## 4     4          4.6         3.1          1.5         0.2 setosa 
## 5     5          5           3.6          1.4         0.2 setosa 
## 6     6          5.4         3.9          1.7         0.4 setosa

tidyr::pivot_longer 関数

pivot_longer 関数はデータフレームにを折りたたむときに使う。 例えば、花弁(petal)およびがく(sepal)の長さと幅を attribute として、 その値を value として折りたたむときは、次のように記述する。

iris_df <- iris %>% tidyr::pivot_longer(c(Sepal.Length, Sepal.Width, Petal.Length, Petal.Width),
                                        names_to = 'attribute', values_to = 'value')
head(iris_df)
## # A tibble: 6 x 4
##      ID Species attribute    value
##   <dbl> <chr>   <chr>        <dbl>
## 1     1 setosa  Sepal.Length   5.1
## 2     1 setosa  Sepal.Width    3.5
## 3     1 setosa  Petal.Length   1.4
## 4     1 setosa  Petal.Width    0.2
## 5     2 setosa  Sepal.Length   4.9
## 6     2 setosa  Sepal.Width    3

次のように、- を付けることで特定の列を取り除いたあとに、 残った列で折りたたむことができる。

iris_df <- iris %>% tidyr::pivot_longer(-c(ID, Species),
                                        names_to = 'attribute', values_to = 'value')
head(iris_df)
## # A tibble: 6 x 4
##      ID Species attribute    value
##   <dbl> <chr>   <chr>        <dbl>
## 1     1 setosa  Sepal.Length   5.1
## 2     1 setosa  Sepal.Width    3.5
## 3     1 setosa  Petal.Length   1.4
## 4     1 setosa  Petal.Width    0.2
## 5     2 setosa  Sepal.Length   4.9
## 6     2 setosa  Sepal.Width    3

各列の属性が同じであれば(すべての列が数値、あるいはすべての列が文字列ならば)、 次のように everything() を使うことで、すべての列を折りたたむことができる。 なお、次の例では iris のデータの ID および Species 列は文字列となっているので、 これらの列を除いてから everything() の例を試している。

iris_df <- iris %>%
              dplyr::select(- c(ID, Species)) %>%
              tidyr::pivot_longer(everything(),
                                 names_to = 'attribute', values_to = 'value')
head(iris_df)
## # A tibble: 6 x 2
##   attribute    value
##   <chr>        <dbl>
## 1 Sepal.Length   5.1
## 2 Sepal.Width    3.5
## 3 Petal.Length   1.4
## 4 Petal.Width    0.2
## 5 Sepal.Length   4.9
## 6 Sepal.Width    3

tidyr::pivot_wider 関数

折りたたまれたデータフレームを、特定の列に基づいて展開したいときは pivot_wider 関数を使用する。 pivot_longer 関数の逆の働きをする関数である。 次は pivot_longer 関数でデータを折りたたんだ後に、pivot_wider 関数で展開する例を示している。

iris_df <- iris %>% tidyr::pivot_longer(c(Sepal.Length, Sepal.Width, Petal.Length, Petal.Width),
                                        names_to = 'attribute', values_to = 'value')


iris_spreaded <- iris_df %>% tidyr::pivot_wider(names_from = 'attribute', values_from = 'value')
head(iris_spreaded)
## # A tibble: 6 x 6
##      ID Species Sepal.Length Sepal.Width Petal.Length Petal.Width
##   <dbl> <chr>          <dbl>       <dbl>        <dbl>       <dbl>
## 1     1 setosa           5.1         3.5          1.4         0.2
## 2     2 setosa           4.9         3            1.4         0.2
## 3     3 setosa           4.7         3.2          1.3         0.2
## 4     4 setosa           4.6         3.1          1.5         0.2
## 5     5 setosa           5           3.6          1.4         0.2
## 6     6 setosa           5.4         3.9          1.7         0.4
CC BY 4.0