R (tidyverse), %>% パイプ

select 関数の記事でも少し触れましたが、 tidyverse パッケージを用いた R では、シェルスクリプトのようなパイプの処理が可能です。具体的には、 %>% という演算子を用います。

オブジェクト %>% 関数(引数2)

演算子の左側に書かれたオブジェクトが、演算子の右側の関数の引数に用いられます。例えば、次のような表現が可能です。

result_data %>% select(Symbol, Sample1)

上記では、result_data を select() 関数の最初の引数として渡しています。つまり、下記の表現と同義です。

select(result_data, Symbol, Smple1)

パイプ %>% を使った表現の何がメリットかというと、処理結果を連続して渡せることです。よって、下記のような表現が可能です。

result_data %>% select(Symbol, Sample1) %>% filter(Sample1 > 1)

これで、select関数で列を選択したのち、filter関数で行を選択するという作業を続けて書けます。途中で改行を挟んでも良いので、コードも読みやすくなります。

result_data <- input_data %>%
  inner_join(meta_data)   %>%
  select(Symbol, Sample1) %>%
  filter(Sample > 1)

上記のように続けて書くと、「inner_join で結合したのち、select で列を選択し、filter で行を選択した結果が result_data に入る」という意味になります。

もし、パイプを使わなかったら、下記のような表現になるでしょう。

result_data <- inner_join(result_data, meta_data)
result_data <- select(result_data, Symbol, Sample1)
result_data <- filter(result_data, Sample > 1)

# もしくは、無理やり1行で、、、
result_data <- filter(select(inner_join(input_data, meta_data), Symbol, Sample1), Sample1 > 1)

同じ変数に代入し続けたり、ネストを深くする書き方は、避けたほうが無難です。後でコードを見返したときに分かりにくくなるためです。コーディングのミスや、デバッグ時の手間の増加につながります。

 

R (tidyverse), filter

列を選択する場合は、select() 関数を用いますが、それに対して、「行を選択する」には、 filter() 関数を用います。

filter(対象のデータ, 条件式)

具体的には、下記のようになります。filter(result_data, Symbol == "AAA") の部分。

> result_data
# A tibble: 3 x 5
  Id    Symbol Description   Sample1 Sample2
  <chr> <chr>  <chr>           <dbl>   <dbl>
1 id1   AAA    lorem ipsum a       1       4
2 id2   BBB    lorem ipsum b       2       5
3 id3   CCC    lorem ipsum c       3       6
>
>
> filter(result_data, Symbol == "AAA")
# A tibble: 1 x 5
  Id    Symbol Description   Sample1 Sample2
  <chr> <chr>  <chr>           <dbl>   <dbl>
1 id1   AAA    lorem ipsum a       1       4

条件式には、かつ (AND)、または (OR) の表現も可能です。AND を意味するの記号が & 、OR を意味する記号が | です。(&&, || と2つ書くとエラーにはなりませんが、違う動作になるため注意しましょう。)下記は、 Sample1 の値が 1 以上、かつ、 Sample2 の値が 2 以上という条件の例です。

> filter(result_data, Sample1 > 1 & Sample2 > 2)
# A tibble: 2 x 5
  Id    Symbol Description   Sample1 Sample2
  <chr> <chr>  <chr>           <dbl>   <dbl>
1 id2   BBB    lorem ipsum b       2       5
2 id3   CCC    lorem ipsum c       3       6

文字列が対象で、複雑な条件式(正規表現など)が必要な場合は、stringr パッケージを用います。(tidyverse を読み込むと自動で読み込まれます。)

例えば、「特定の文字列を含む」という条件式は、 str_detect() を使います。filter() の条件式の部分に str_detect(対象の文字列, パターン) を記述します。

> filter(result_data, str_detect(Description, "ipsum a"))
# A tibble: 1 x 5
  Id    Symbol Description   Sample1 Sample2
  <chr> <chr>  <chr>           <dbl>   <dbl>
1 id1   AAA    lorem ipsum a       1       4

stringr パッケージには、他にも、文字列を置換 (str_replace) したり、連結 (str_c) したりする関数が含まれています。

検出 (str_detect), 置換 (str_replace), 連結 (str_c), 大文字に変換(str_to_upper), 小文字に変換 (str_to_lower), 先頭だけ大文字 (str_to_title)

https://stringr.tidyverse.org

 

R (tidyverse), select

テーブルのデータから、必要な「列」だけを選択して取り出すには、 select()関数を使います。

select(対象のデータ, 列名1, 列名2, ...)

具体的には、下記のようになります。 select(result_data, Symbol, Sample1, Sample2) の部分です。

> result_data
# A tibble: 3 x 5
  Id    Sample1 Sample2 Symbol Description
  <chr>   <dbl>   <dbl> <chr>  <chr>
1 id1         1       4 AAA    lorem ipsum a
2 id2         2       5 BBB    lorem ipsum b
3 id3         3       6 CCC    lorem ipsum c
>
>
> select(result_data, Symbol, Sample1, Sample2)
# A tibble: 3 x 3
  Symbol Sample1 Sample2
  <chr>    <dbl>   <dbl>
1 AAA          1       4
2 BBB          2       5
3 CCC          3       6

従来のデータフレームと異なり、列名をダブルクオーテーションで囲む必要がありません。(列名に空白や記号を含む場合は、バッククオート「`」で囲む必要があります。)

# 従来の書き方だと、可読性が今ひとつです。
result_data[, c("Symbol", "Sample1", "Sample2")]

また、select 関数は、指定した順番に取り出せるので、列を並び替える目的にも使えます。

output_data <- select(result_data, Id, Symbol, Description, Sample1, Sample2)

# 実際には、 %>% の処理と組み合わせて書くことが多いです。
# 1つ前の処理結果が対象のデータになるので、その指定を省略して記述できます。

result_data <- inner_join(input_data, meta_data) %>%
  select(Id, Symbol, Description, Sample1, Sample2)
 

R (tidyverse), inner_join

「計算用の数値を含むデータ」と、「各行を説明する名前 (symbol) や説明の文章などのデータ」という2つのテーブル同士を、それぞれに含まれる共通の列をキーに連結させたいことは、よくある処理だと思います。

このようなとき、R の tidyverse では、 inner_join() 関数を用いて、簡単に結合することができます。

例えば、下記のように2つのテーブルを読み込んだとします。

library(tidyverse)

input_data <- read_tsv("input_data.tsv")
meta_data  <- read_tsv("meta_data.tsv")

テーブルの中身は、下記のようなイメージです。2つのファイルも1列目に id の列を持ちます。

> # 計算用のデータ。
> input_data
# A tibble: 3 x 3
  Id    Sample1 Sample2
  <chr>   <dbl>   <dbl>
1 id1         1       4
2 id2         2       5
3 id3         3       6
>
>
>
> # 各行を説明するデータ。アノテーションの情報など。
> meta_data
# A tibble: 4 x 3
  Id    Symbol Description
  <chr> <chr>  <chr>
1 id1   AAA    lorem ipsum a
2 id2   BBB    lorem ipsum b
3 id3   CCC    lorem ipsum c
4 id4   DDD    lorem ipsum d

2つのテーブルの共通な列である Id をもとに、テーブルを結合するには、下記のように書けます。

result_data <- inner_join(input_data, meta_data)

実行結果は、下記のようになります。

> result_data <- inner_join(input_data, meta_data)
Joining, by = "Id"
> result_data
# A tibble: 3 x 5
  Id    Sample1 Sample2 Symbol Description
  <chr>   <dbl>   <dbl> <chr>  <chr>
1 id1         1       4 AAA    lorem ipsum a
2 id2         2       5 BBB    lorem ipsum b
3 id3         3       6 CCC    lorem ipsum c

Joining, by = “Id” となっていることで、 Id 列をもとに結合されたことが分かります。この場合は、2つのテーブルに同じ名前の列があるので、自動的に認識されています。明示的にキーの列を指定する場合は、inner_join(input_data, meta_data, by = "Id") と書きます。

また、結果のテーブル (result_data) では、元の input_data に含まれない id4 の行がないことが確認できます。

 

R (tidyverse), read_tsv

tidyverse には、タブ区切りテキスト (tsv) を読み込むための関数 read_tsv() が使えます。

library(tidyverse) # パッケージを読み込む。

input_data <- read_tsv("input_data.tsv")

上記の例では、input_data.tsv という名前のファイルを読み込んで、 input_data に格納しています。(csv ファイルであれば、read_csv() 関数で読み込みます。)

> input_data
# A tibble: 3 x 3
  id    sample1 sample2
  <chr>   <dbl>   <dbl>
1 AAA         1       4
2 BBB         2       5
3 CCC         3       6

読み込んだファイルは、従来のデータフレームではなく、 tibble という形式のオブジェクトになります。tibble は下記のような特徴があります。

  • 行名 (rownames) を持たない。(列名は持っている)
  • 列ごとに推論された型を持つ。
  • オブジェクトを表示させると、上から10行が表示される。

サイズの大きいデータの場合、データの先頭だけ表示されるのは便利です。読み込みも早いので、これだけでも tidyverse を使うメリットがあると思います。

よく使うオプションとしては、下記のものがあります。*その他は、help(read_tsv) で確認。

  • skip: ヘッダーが不要だったり、1行目がコメント行だったりするとき。
    • 例:skip = 1
  • col_names: 列名を指定したいとき。
    • 例:col_names = c("id", "sample1", "sample2")
  • col_types: 型推論がうまくいかなかったとき。
    • 例:col_types = cols(id = col_character())
input_data <- read_tsv("input_data.tsv", skip = 1,
                       col_names = c("id", "sample1", "sample2"),
                       col_types = cols(id = col_character(),
                                        sample1 = col_double()))

本家のチートシートが大変参考になります。

Data Import:: CHEAT SHEET

https://github.com/rstudio/cheatsheets/blob/master/data-import.pdf