Goodな生活

INTP型コンサルタントが好奇心の受け皿を探す

【R】【データ前処理】はじめてdplyrを使うときに調べたこと

f:id:good_na_life:20210524102921p:plain

参考にした本

今年の夏ごろに買いました。


パイプ演算子(%>%)を見るに「ひょっとして面倒なループなのではないか」という印象を抱いてしまい、意味もなく敬遠していました。が、いざ使ってみるととても直観的で操作しやすいです。Excelでいうピボットテーブルのような機能です。

必要な準備

インストール

> install.packages("dplyr")
> library(dplyr)

データセットを読み込む

Rの内蔵データセットのアイリスを使用。含まれる変数は、
Sepal.Length:がく辺の長さ
Sepal.Width:がく辺の幅
Petal.Length:花びらの長さ
Petal.Width:花びらの幅
Species:品種("setosa","versicolor","virginica"の3種類)

> head(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

フィルターをかける

一つの条件

アイリスのデータから

  • Sepal.Lengthが5以上

を満たす行のみを抽出。変数の分布をみて外れ値を除外するときなどに便利。
加工前データ(iris)と加工後データ(iris.2)を分ける。

> iris.2 <- iris %>%
+   filter(Sepal.Length >= 5)
> head(iris.2)
  Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1          5.1         3.5          1.4         0.2  setosa
2          5.0         3.6          1.4         0.2  setosa
3          5.4         3.9          1.7         0.4  setosa
4          5.0         3.4          1.5         0.2  setosa
5          5.4         3.7          1.5         0.2  setosa
6          5.8         4.0          1.2         0.2  setosa

複数条件の場合

  • Sepal.Lengthが5以上
  • Speciesがvirginica

の条件をどちらも満たす行を取り出す場合は、&(AND)を使って

> iris.2 <- iris %>%
+   filter(iris$Sepal.Length >= 5 & Species == "virginica")

どちらか一方を満たす行を抽出する場合は、|(OR)を使って

> iris.2 <- iris %>%
+   filter(iris$Sepal.Length >= 5 | Species == "virginica") 

条件の否定

  • Sepal.Lengthが5以上ではない
  • Speciesがvirginicadではない

個体を選びたい場合、!を使う。今回のように種類が少なければ否定を使わなくても書けますが、カテゴリー数の多いデータを扱う際に便利。

> iris.2 <- iris %>%
+   filter(!(iris$Sepal.Length >= 5 | Species == "virginica")) 

グループごとに集計する

カウントする

  • Speciesごとの数を集計する

group_by()で集計単位を指定し、mutate()で新しくnという名前の列を作る

> iris.2 <- iris %>%
+   group_by(Species) %>%
+   mutate(n=n())

group_by()はあくまで集計の単位なので、データセットの構造自体を変える訳ではありません。Speacisごとの数だけ確認したいときは、distinct()で「重複するSpeaciesを削除=ユニークなSpeaciesだけ残す」操作を行う。すると、Speaciesの数=3行だけ残る。keep.allは集計対象以外の列も残す、というオプション。

> iris.2 <- iris %>%
+   group_by(Species) %>%
+   mutate(n =n()) %>%
+   distinct(Species,.keep_all = TRUE)
> head(iris.2)
# A tibble: 3 x 6
# Groups:   Species [3]
  Sepal.Length Sepal.Width Petal.Length Petal.Width Species        n
         <dbl>       <dbl>        <dbl>       <dbl> <fct>      <int>
1          5.1         3.5          1.4         0.2 setosa        50
2          7           3.2          4.7         1.4 versicolor    50
3          6.3         3.3          6           2.5 virginica     50

平均や分散を出す

  • SpeciesごとにSepal.Length(がく片の長さ)の平均と標準偏差を算出する

mutate()でmeanとsdという名前の新しい列を作る。記述統計量を算出するsummerize()というコマンドもある。

> iris.2 <- iris %>%
+   group_by(Species) %>%
+   mutate(mean = mean(Sepal.Length),
+          sd = sd(Sepal.Length)) %>%
+   distinct(Species,.keep_all = TRUE)
> iris.2
# A tibble: 3 x 7
# Groups:   Species [3]
  Sepal.Length Sepal.Width Petal.Length Petal.Width Species     mean    sd
         <dbl>       <dbl>        <dbl>       <dbl> <fct>      <dbl> <dbl>
1          5.1         3.5          1.4         0.2 setosa      5.01 0.352
2          7           3.2          4.7         1.4 versicolor  5.94 0.516
3          6.3         3.3          6           2.5 virginica   6.59 0.636

複数の単位で集計

データセットの中には、大カテゴリー、中カテゴリー、・・という風に集計単位が複数になる場合もある。その場合はgroup_by(A,B,・・・)のように、2つ以上の変数を入れる

一回集計したあとに再度集計

データセットによっては同じidのデータが複数行に渡って含まれる場合もある。一度id別に平均等を算出し、更にidのカテゴリーごとに集計する場合、group_by(id), mutate(new=mean(A)),group_by(category)という風に、group_byを2回に分けて使うこともできる。

条件下で新たな変数を作る

  • Spetal.Length(がく片の長さ)が5以下だと1をとるフラグを作成する
> iris.2 <- iris %>%
+   group_by(Species) %>%
+   mutate(flg = ifelse(Sepal.Length <= 5,1,0))
> head(iris.2)
# A tibble: 6 x 6
# Groups:   Species [1]
  Sepal.Length Sepal.Width Petal.Length Petal.Width Species   flg
         <dbl>       <dbl>        <dbl>       <dbl> <fct>   <dbl>
1          5.1         3.5          1.4         0.2 setosa      0
2          4.9         3            1.4         0.2 setosa      1
3          4.7         3.2          1.3         0.2 setosa      1
4          4.6         3.1          1.5         0.2 setosa      1
5          5           3.6          1.4         0.2 setosa      1
6          5.4         3.9          1.7         0.4 setosa      0

列名の変更と並び変え

前処理の終盤で行う操作。必要な列だけをselect()で選ぶ。並び替えも同時にできる。

> iris.2 <- iris %>%
+   group_by(Species) %>%
+   mutate(mean = mean(Sepal.Length),
+          sd = sd(Sepal.Length)) %>%
+   distinct(Species,.keep_all = TRUE) %>%
+   select(Species,mean,sd)
> iris.2
# A tibble: 3 x 3
# Groups:   Species [3]
  Species     mean    sd
  <fct>      <dbl> <dbl>
1 setosa      5.01 0.352
2 versicolor  5.94 0.516
3 virginica   6.59 0.636

列名を変える

  • 列名を日本語に変える
> iris.2 <- iris %>%
+   group_by(Species) %>%
+   mutate(mean = mean(Sepal.Length),
+          sd = sd(Sepal.Length)) %>%
+   distinct(Species,.keep_all = TRUE) %>%
+   select(Species,mean,sd) %>%
+   rename(品種 = Species,
+           平均 = mean,
+           標準偏差 = sd)
> iris.2
# A tibble: 3 x 3
# Groups:   品種 [3]
  品種        平均 標準偏差
  <fct>      <dbl>    <dbl>
1 setosa      5.01    0.352
2 versicolor  5.94    0.516
3 virginica   6.59    0.636

並び順を変える(ソートする)

  • 平均が大きい順に並び替える

arrange()で並び替え。デフォルトは降順なので、"-"(マイナス)またはdesc()で逆にする。

> iris.2 <- iris %>%
+   group_by(Species) %>%
+   mutate(mean = mean(Sepal.Length),
+          sd = sd(Sepal.Length)) %>%
+   distinct(Species,.keep_all = TRUE) %>%
+   select(Species,mean,sd) %>%
+   rename(品種 = Species,
+           平均 = mean,
+           標準偏差 = sd) %>%
+   arrange(desc(平均))
> iris.2
# A tibble: 3 x 3
# Groups:   品種 [3]
  品種        平均 標準偏差
  <fct>      <dbl>    <dbl>
1 virginica   6.59    0.636
2 versicolor  5.94    0.516
3 setosa      5.01    0.352

エクセルに出力する

そのまま出力

> write.xlsx(iris.2,"iris2.xlsx")

集計単位ごとにシートを分ける

split()をかましてからエクセルに出力する。1品種1シートごとに結果が出力される。

> iris.2 <- iris %>%
+   group_by(Species) %>%
+   mutate(mean = mean(Sepal.Length),
+          sd = sd(Sepal.Length)) %>%
+   distinct(Species,.keep_all = TRUE) %>%
+   select(Species,mean,sd) %>%
+   rename(品種 = Species,
+           平均 = mean,
+           標準偏差 = sd) %>%
+   arrange(desc(平均)) %>%
+   split(.,.$品種)
>   write.xlsx(iris.2,"iris2.xlsx")