14 データハンドリング [要約]
前章ではデータの一部(subset)を抽出する方法について説明しましたが、本章はデータを拡張する、あるいは全く別のデータが得られるような処理について解説します。後者は主に元のデータを要約し(記述統計量)、その結果を出力する方法で、前者はデータ内の変数に基づき、指定された計算を行った結果を新しい列として追加する方法です。今回も前章と同じデータを使用します。
データの詳細については第13.3章を参照してください。
14.1 記述統計量の計算
14.1.1 summarise()
による記述統計量の計算
ある変数の平均値や標準偏差、最小値、最大値などの記述統計量(要約統計量)を計算することも可能です。これはsummarize()
またはsummarise()
関数を使いますが、この関数は後で紹介するgroup_by()
関数と組み合わせることで力を発揮します。ここではグルーピングを考えずに、全データの記述統計量を計算する方法を紹介します。
summarise()
関数の使い方は以下の通りです。
もし、Score
変数の平均値を計算し、その結果をMean
という列にしたい場合は以下のようなコードになります。
ただし、mean()
関数は欠損値が含まれるベクトルの場合、NA
を返します。この場合方法は2つ考えられます。
filter()
関数を使ってScore
が欠損しているケースを予め除去する。na.rm
引数を指定し、欠損値を除去した平均値を求める。
ここでは2番目の方法を使います。
df
のScore
変数の平均値はNAであることが分かります。また、summarise()
関数は複数の記述統計量を同時に計算することも可能です。以下はScore
変数の平均値、中央値、標準偏差、最小値、最大値、第一四分位点、第三四分位点を計算し、Score.Desc
という名のデータフレームに格納するコードです。
Score.Desc <- df |>
summarize(Mean = mean(Score, na.rm = TRUE), # 平均値
Median = median(Score, na.rm = TRUE), # 中央値
SD = sd(Score, na.rm = TRUE), # 標準偏差
Min = min(Score, na.rm = TRUE), # 最小値
Max = max(Score, na.rm = TRUE), # 最大値
Q1 = quantile(Score, 0.25, na.rm = TRUE), # 第一四分位点
Q3 = quantile(Score, 0.75, na.rm = TRUE)) # 第三四分位点
# A tibble: 1 × 7
Mean Median SD Min Max Q1 Q3
<dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 3.66 3.58 0.719 1 5 3 4
むろん、複数の変数に対して記述統計量を計算することも可能です。たとえば、平均予算 (Budget
)、口コミ数 (ScoreN
)、口コミ評価 (Score
)の平均値を求めるとしたら、
df |>
summarize(Budget_Mean = mean(Budget, na.rm = TRUE), # 平均予算の平均値
SocreN_Mean = mean(ScoreN, na.rm = TRUE), # 口コミ数の平均値
Score_Mean = mean(Score, na.rm = TRUE)) # 評価の平均値
# A tibble: 1 × 3
Budget_Mean SocreN_Mean Score_Mean
<dbl> <dbl> <dbl>
1 1232. 0.537 3.66
のように書きます。実はsummarise()
はこれくらいで十分便利です。ただし、以上の操作はもっと簡単なコードに置換できます。ただし、ラムダ関数など、やや高度な内容になるため、以下の内容は飛ばして、次の節 (グルーピング)を読んでいただいても構いません。
まずは、複数の変数に対して同じ記述統計量を求める例を考えてみましょう。たとえば、Budget
、ScoreN
、Score
に対して平均値を求める例です。これはacross()
関数を使うとよりコードが短くなります。まずはacross()
関数の書き方から見ましょう。
変数名のベクトルは長さ1以上のベクトルです。たとえば、Budget
、ScoreN
、Score
の場合c(Budget, ScoreN, Score)
になります。これはdf
内で隣接する変数ですからBudget:Score
の書き方も使えます。また、where()
やany_of()
、starts_with()
のような関数を使って変数を指定することも可能です。ラムダ関数(lambda function; ラムダ式)は~
で始まる関数であり、無名関数とも呼ばれます。たとえば、平均値を計算するラムダ関数は~mean()
であり、第一引数は.x
となります。この.x
にacross()
の第一引数(変数たち)が入ります。また、()
内には更に引数を指定することもでき、たとえば欠損値を除外して計算するna.rm = TRUE
などが追加できます。ラムダ関数の詳細は後でもう一度解説するとし、とりあえずBudget
、ScoreN
、Score
の平均値を計算してみましょう。
# A tibble: 1 × 3
Budget ScoreN Score
<dbl> <dbl> <dbl>
1 1232. 0.537 3.66
across()
使わない場合、4行必要だったコードが2行になりました。変数が少ない場合はacross()
を使わない方が、可読性が高くなる場合もあります。しかし、変数が多くなる場合、可読性がやや落ちてもacross()
を使った方が効率的でしょう。
次は、ある変数に対して複数の記述統計量を計算したい場合について考えます。Budget
、ScoreN
、Score
変数の第一四分位点と第三四分位点をacross()
を使わずに計算すると家のような7行のコードになります。
df |>
summarize(Budget_Q1 = quantile(Budget, 0.25, na.rm = TRUE),
Budget_Q3 = quantile(Budget, 0.75, na.rm = TRUE),
ScoreN_Q1 = quantile(ScoreN, 0.25, na.rm = TRUE),
ScoreN_Q3 = quantile(ScoreN, 0.75, na.rm = TRUE),
Score_Q1 = quantile(Score, 0.25, na.rm = TRUE),
Score_Q3 = quantile(Score, 0.75, na.rm = TRUE))
# A tibble: 1 × 6
Budget_Q1 Budget_Q3 ScoreN_Q1 ScoreN_Q3 Score_Q1 Score_Q3
<dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 800 1000 0 0 3 4
この作業もacross()
を使ってより短縮することができます。ここではラムダ関数の知識が必要になります。ラムダ関数とは関数名を持たない無名関数 (anonymous functions)を意味しますが、詳細は割愛します。興味のある読者はWikipediaなどを参照してください。簡単にいうとその場で即席に関数を作成し、計算が終わったら破棄する関数です。ラムダ関数の書き方にはバリエーションがありますが、ここでは{purrr}パッケージのラムダ関数スタイルを使用します1。まずは、書き方から確認します。
ここでは~mean(.x, na.rm = TRUE)
と書いたが、他の書き方もある。昔からのやり方ではfunction(x) mean(x, na.rm = TRUE)
のような書き方がある。関数内の内容が複数行に渡る場合はfunction(x) { mean(x, na.rm = TRUE) }
と表記する。そしてR 4.1から追加された\(x) mean(x, na.rm = TRUE)
のような書き方もある。以下のコードはすべて同じ結果を返す。Rネイティブの書き方の場合、引数は.x
でなく、x
であることに注意しよう(\(y)
ならy
が引数となる)。
# purrrスタイル
df |>
summarize(across(Budget:Score, ~mean(.x, na.rm = TRUE)))
# R nativeスタイル
df |>
summarize(across(Budget:Score, function(x) mean(x, na.rm = TRUE)))
# R nativeスタイル
df |>
summarize(across(Budget:Score, function(x) { mean(x, na.rm = TRUE) }))
# R nativeスタイル(R 4.1以降)
# function を \ に省略したもの
df |>
summarize(across(Budget:Score, \(x) mean(x, na.rm = TRUE)))
先ほどの書き方と似ていますが、関数を複数書く必要があるため、今回は関数名をlist型にまとめ、.fns
引数に指定します。そして、結果の変数名は結果として出力されるデータフレームの列名を指定する引数です。たとえば、Mean
にすると結果は元の変数名1_Mean
、元の変数名2_Mean
…のように出力されます。そして、ラムダ関数が実際の関数が入る箇所です。とりあえず今回はコードを走らせ、結果から確認してみましょう。
df |>
summarize(across(Budget:Score,
.fns = list(Q1 = ~quantile(.x, 0.25, na.rm = TRUE),
Q3 = ~quantile(.x, 0.75, na.rm = TRUE))))
# A tibble: 1 × 6
Budget_Q1 Budget_Q3 ScoreN_Q1 ScoreN_Q3 Score_Q1 Score_Q3
<dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 800 1000 0 0 3 4
結果の列名がBudget_Q1
、Budget_Q3
、ScoreN_Q1
…のようになり、それぞれの変数の第一四分位点と第三四分位点が出力されます。問題はラムダ関数の方ですが、普通の関数に非常に近いことが分かります。across()
内のラムダ関数は~関数名(.x, その他の引数)
のような書き方になります。関数名の前に~
が付いていることに注意してください。分位数を求める関数はquantile()
であり、quantile(ベクトル, 分位数)
であり、必要に応じてna.rm
を付けます。この分位数が0.25なら第一四分位点、0.5なら第二四分位点 (=中央値)、0.75なら第三四分位点になります。それではラムダ関数~quantile(.x, 0.25, na.rm = TRUE)
はどういう意味でしょうか。これは.x
の箇所にBudget
やScoreN
、Score
が入ることを意味します。.x
という書き方は決まりです。.y
とか.Song-san-Daisuki
などはダメです。そして、0.25
を付けることによって第一四分位点を出力するように指定します。また、Budget
、ScoreN
、Score
に欠損値がある場合、無視するようにna.rm = TRUE
を付けます。
ラムダ関数を第11章で解説した自作関数で表現すると、以下のようになります。
この3つは全て同じですが、ラムダ関数は関数名を持たず、その場で使い捨てる関数です。むろん、ラムダ関数を使わずに事前に第一四分位点と第三四分位点を求める関数を予め作成し、ラムダ関数の代わりに使うことも可能です。まずは第一四分位点と第三四分位点を求める自作関数FuncQ1
とFuncQ2
を作成します。
後は先ほどのほぼ同じ書き方ですが、今回はラムダ関数を使わないため関数名に~
を付けず、関数名のみで十分です。()
も不要です。
# A tibble: 1 × 6
Budget_Q1 Budget_Q3 ScoreN_Q1 ScoreN_Q3 Score_Q1 Score_Q3
<dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 800 1000 0 0 3 4
事前に関数を用意するのが面倒ですが、across()
の中身はかなりスッキリしますね。もし、このような作業を何回も行うなら、ラムダ関数を使わず、自作関数を用いることも可能です。ただし、自作関数であっても引数が2つ以上必要な場合はラムダ関数を使います。
14.1.2 summarise()
に使える便利な関数
以下の内容は後で説明するgroup_by()
関数を使っているため、まだgroup_by()
に馴染みのない読者はまずはここを読み飛ばし、グルーピングの節にお進みください。
IQR()
: 四分位範囲を求める
四分位範囲は第三四分位点から第一四分位点を引いた値であり、Rの内蔵関数であるIQR()
を使えば便利です。この関数はmean
やsd()
関数と同じ使い方となります。
df |>
filter(!is.na(Walk)) |> # 予め欠損したケースを除くと、後でna.rm = TRUEが不要
group_by(Pref) |>
summarise(Mean = mean(Walk),
SD = sd(Walk),
IQR = IQR(Walk),
N = n(),
.groups = "drop") |>
arrange(Mean)
# A tibble: 9 × 5
Pref Mean SD IQR N
<chr> <dbl> <dbl> <dbl> <int>
1 東京都 4.29 4.49 4 919
2 大阪府 5.92 6.08 6 932
3 神奈川県 8.21 7.91 10 878
4 京都府 8.38 6.95 9 339
5 兵庫県 8.52 7.27 10 484
6 奈良県 10.6 6.59 10 123
7 千葉県 10.6 8.21 12 776
8 埼玉県 11.6 8.99 14 817
9 和歌山県 12.8 6.83 9 107
first()
、last()
、nth()
: n番目の要素を求める
稀なケースかも知れませんが、データ内、またはグループ内のn
番目の行を抽出する時があります。たとえば、市区町村の情報が格納されているデータセットで、人口が大きい順でデータがソートされているとします。各都道府県ごとに最も人口が大きい市区町村のデータ、あるいは最も少ない市区町村のデータが必要な際、first()
とlast()
関数が有効です。
それでは各都道府県ごとに「最も駅から遠いラーメン屋」の店舗名と最寄りの駅からの徒歩距離を出力したいとします。まずは、徒歩距離のデータが欠損しているケースを除去し、データを徒歩距離順でソートします。これはfilter()
とarrange()
関数を使えば簡単です。続いて、group_by()
を使って都府県単位でデータをグループ化します。最後にsummarise()
関数内にlast()
関数を使います。データは駅から近い順に鳴っているため、各都府県内の最後の行は駅から最も遠い店舗になるからです。
df |>
filter(!is.na(Walk)) |>
arrange(Walk) |>
group_by(Pref) |>
summarise(Farthest = last(Name),
Distance = last(Walk))
# A tibble: 9 × 3
Pref Farthest Distance
<chr> <chr> <dbl>
1 京都府 熱烈らぁめん 30
2 兵庫県 濃厚醤油 中華そば いせや 玉津店 43
3 千葉県 札幌ラーメン どさん子 佐原51号店 59
4 和歌山県 中華そば まる乃 30
5 埼玉県 札幌ラーメン どさん子 小鹿野店 116
6 大阪府 河童ラーメン本舗 岸和田店 38
7 奈良県 博多長浜らーめん 夢街道 四条大路店 29
8 東京都 てんがら 青梅新町店 30
9 神奈川県 札幌ラーメン どさん子 中津店 73
このlast()
をfirst()
に変えると、最寄りの駅から最も近い店舗情報が表示されます。また、「n
番目の情報」が必要な際はnth()
関数を使います。nth(Name, 2)
に変えることで2番目の店舗名が抽出できます。
n_distinct()
: ユニーク値の個数を求める
n_distinct()
は何種類の要素が含まれているかを計算する関数であり、length(unique())
関数と同じ機能をします。たとえば、以下のmyVec1
に対して何種類の要素があるかを確認してみましょう。
myVec1
は"A"
、"B"
、"D"
、"C"
の要素で構成されていることが分かります。これがmyVec1
のユニーク値 (unique values)です。そして、このユニーク値の個数を調べるためにlength()
を使います。
これでmyVec1
は4種類の値が存在することが分かります。これと全く同じ機能をする関数がn_distinct()
です。
この関数をsummarise()
に使うことで、都府県ごとに駅の個数が分かります。あるいは「東京都内の選挙区に、これまでの衆院選において何人の候補者が存在したか」も分かります。ここではdf
内の都府県ごとに駅の個数を計算してみましょう。最後の駅数が多い順でソートします。
df |>
filter(!is.na(Station)) |> # 最寄りの駅が欠損しているケースを除去
group_by(Pref) |>
summarise(N_Station = n_distinct(Station),
.groups = "drop") |>
arrange(desc(N_Station))
# A tibble: 9 × 2
Pref N_Station
<chr> <int>
1 東京都 368
2 大阪府 341
3 千葉県 241
4 神奈川県 240
5 兵庫県 199
6 埼玉県 185
7 京都府 123
8 奈良県 52
9 和歌山県 46
当たり前かも知れませんが、駅数が最も多いのは東京都で次が大阪府であることが分かります。
any()
、all()
: 条件に合致するか否かを求める
any()
とall()
はベクトル内の全要素に対して条件に合致するか否かを判定する関数です。ただし、any()
は一つの要素でも条件に合致すればTRUE
を、全要素が合致しない場合FALSE
を返します。一方、all()
は全要素に対して条件を満たせばTRUE
、一つでも満たさない要素があればFALSE
を返します。以下はany()
とall()
の例です。
myVec1 <- c(1, 2, 3, 4, 5)
myVec2 <- c(1, 3, 5, 7, 11)
any(myVec1 %% 2 == 0) # myVec1を2で割った場合、一つでも余りが0か
[1] TRUE
[1] FALSE
[1] TRUE
それでは実際にdf
に対してany()
とall()
関数を使ってみましょう。一つ目は「ある都府県に最寄りの駅から徒歩60分以上の店舗が一つでもあるか」であり、二つ目は「ある都府県の店舗は全て最寄りの駅から徒歩30分以下か」です。それぞれの結果をOver60
とWithin30
という列で出力してみましょう。
df |>
group_by(Pref) |>
summarise(Over60 = any(Walk >= 60, na.rm = TRUE),
Within30 = all(Walk <= 30, na.rm = TRUE),
.groups = "drop")
# A tibble: 9 × 3
Pref Over60 Within30
<chr> <lgl> <lgl>
1 京都府 FALSE TRUE
2 兵庫県 FALSE FALSE
3 千葉県 FALSE FALSE
4 和歌山県 FALSE TRUE
5 埼玉県 TRUE FALSE
6 大阪府 FALSE FALSE
7 奈良県 FALSE TRUE
8 東京都 FALSE TRUE
9 神奈川県 TRUE FALSE
埼玉県と神奈川県において、最寄りの駅から徒歩60以上の店がありました。また、京都府、東京都、奈良県、和歌山県の場合、全店舗が最寄りの駅から徒歩30分以下ということが分かります。当たり前ですがOver60
がTRUE
ならWithin30
は必ずFALSE
になりますね。
14.2 グルーピング
14.2.1 group_by()
によるグループ化
先ほどのsummarise()
関数は確かに便利ですが、特段に便利とも言いにくいです。df
のScore
の平均値を計算するだけなら、summarise()
関数を使わない方が楽です。
# A tibble: 1 × 1
Mean
<dbl>
1 3.66
[1] 3.663457
しかし、これをグループごとに計算するならどうでしょう。たとえば、Score
の平均値を都府県ごとに計算するとします。この場合、以下のようなコードになります。
[1] 3.674256
[1] 3.533931
[1] 3.715983
[1] 3.641573
[1] 3.765194
[1] 3.684976
[1] 3.543936
[1] 3.854762
[1] 3.96999
変わったのはdf$Score
がdf$Score[df$Pref == "東京都"]
に変わっただけです。df$Pref
が"東京都"
であるか否かをTRUE
とFALSE
で判定し、これを基準にdf$Score
を抽出する仕組みです。df$Score
とdf$Pref
は同じデータフレームですから、このような書き方で問題ありません。
これだけでもかなり書くのが面倒ですが、これが47都道府県なら、あるいは200ヶ国ならかなり骨の折れる作業でしょう。ここで大活躍するのが{dplyr}パッケージのgroup_by()
関数です。引数はグループ化する変数名だけです。先ほどの作業を{dplyr}を使うならPref
変数でグループ化し、summarise()
関数で平均値を求めるだけです。今回はScore
だけでなく、ScoreN
の平均値も求めてみましょう。そして、評価が高い順にソートもしてみます。
# ScoreNとScoreの平均値をPrefごとに求める
df |>
group_by(Pref) |>
summarise(ScoreN_Mean = mean(ScoreN, na.rm = TRUE),
Score_Mean = mean(Score, na.rm = TRUE)) |>
arrange(desc(Score_Mean))
# A tibble: 9 × 3
Pref ScoreN_Mean Score_Mean
<chr> <dbl> <dbl>
1 和歌山県 0.593 3.97
2 奈良県 0.306 3.85
3 大阪府 0.516 3.77
4 千葉県 0.259 3.72
5 京都府 0.522 3.68
6 東京都 1.17 3.67
7 埼玉県 0.278 3.64
8 兵庫県 0.389 3.54
9 神奈川県 0.587 3.53
評判が最も高い都府県は和歌山県、最も低いのは神奈川県ですね。Songも和歌山ラーメンは井出系も車庫前系も好きです。しかし、大事なのは「井出系」と「車庫前系」といった分類が正しいかどうかではありません。コードが非常に簡潔となり、ソートなども自由自在であることです。都府県ごとにScoreN
とScore
の平均値を求める場合、{dplyr}を使わなかったら18行のコードとなり、ソートも自分でやる必要があります。一方、group_by()
関数を使うことによってコードが5行になりました。
group_by()
と同じ機能を持つ.by
引数
最新の{dplyr}を使う場合(2024年10月21日現在、{dplyr}1.1.4)、group_by()
関数を使わず、summarise()
関数内に.by
引数を指定しても同じ動きをします。具体的には.by = グルーピングする変数名
を追加するだけです。したがって、以下は上記のコードと同じ内容です。
df |>
summarise(ScoreN_Mean = mean(ScoreN, na.rm = TRUE),
Score_Mean = mean(Score, na.rm = TRUE),
.by = Pref) |>
arrange(desc(Score_Mean))
# A tibble: 9 × 3
Pref ScoreN_Mean Score_Mean
<chr> <dbl> <dbl>
1 和歌山県 0.593 3.97
2 奈良県 0.306 3.85
3 大阪府 0.516 3.77
4 千葉県 0.259 3.72
5 京都府 0.522 3.68
6 東京都 1.17 3.67
7 埼玉県 0.278 3.64
8 兵庫県 0.389 3.54
9 神奈川県 0.587 3.53
続いて、一つ便利な関数を紹介します。それはグループのサイズを計算する関数、n()
です。この関数をsummarise()
内に使うと、各グループに属するケース数を出力します2。先ほどのコードを修正し、各グループのサイズをN
という名の列として追加してみましょう。そしてソートの順番はN
を最優先とし、同じ場合はScore_Mean
が高い方を上に出力させます。また、ScoreN_Mean
の前に、口コミ数の合計も出してみましょう。
# Prefごとに口コミ数の合計、口コミ数の平均値、評価の平均値、店舗数を求める
# 店舗数-評価の平均値順でソートする
df |>
group_by(Pref) |>
summarise(ScoreN_Sum = sum(ScoreN, na.rm = TRUE),
ScoreN_Mean = mean(ScoreN, na.rm = TRUE),
Score_Mean = mean(Score, na.rm = TRUE),
N = n()) |>
arrange(desc(N), desc(Score_Mean))
# A tibble: 9 × 5
Pref ScoreN_Sum ScoreN_Mean Score_Mean N
<chr> <dbl> <dbl> <dbl> <int>
1 大阪府 516 0.516 3.77 1000
2 千葉県 259 0.259 3.72 1000
3 東京都 1165 1.17 3.67 1000
4 埼玉県 278 0.278 3.64 1000
5 神奈川県 587 0.587 3.53 1000
6 兵庫県 230 0.389 3.54 591
7 京都府 216 0.522 3.68 414
8 奈良県 45 0.306 3.85 147
9 和歌山県 83 0.593 3.97 140
記述統計をグループごとに求めるのは普通にあり得るケースですし、実験データの場合はほぼ必須の作業でう。統制群と処置群間においてグループサイズが均一か、共変量のバラツキが十分に小さいかなどを判断する際にgroup_by()
とsummarise()
関数の組み合わせは非常に便利です。
14.2.2 複数の変数を用いたグループ化
グループ化変数は2つ以上指定することも可能です。たとえば、都府県 (Pref
)と最寄りの駅の路線 (Line
)でグループ化することも可能です。それではPref
とLine
でグループ化し、店舗数と口コミ数、評価の平均値を計算し、ソートの順番は店舗数、店舗数が同じなら評価の平均値が高い順にしましょう。今回はTop 20まで出してみます。
# ScoreNとScoreの平均値をPrefごとに求める
df |>
filter(!is.na(Line)) |> # Lineが欠損していないケースのみ残す
group_by(Pref, Line) |> # PrefとLineでグループ化
summarise(N = n(),
ScoreN_Sum = sum(ScoreN, na.rm = TRUE),
Score_Mean = mean(Score, na.rm = TRUE)) |>
arrange(desc(N), desc(Score_Mean)) |>
print(n = 20)
`summarise()` has grouped output by 'Pref'. You can override using the
`.groups` argument.
# A tibble: 523 × 5
# Groups: Pref [9]
Pref Line N ScoreN_Sum Score_Mean
<chr> <chr> <int> <dbl> <dbl>
1 埼玉県 東武東上線 122 27 3.68
2 東京都 JR 104 231 3.56
3 神奈川県 小田急小田原線 96 31 3.59
4 埼玉県 東武伊勢崎線 96 18 3.51
5 神奈川県 横浜市営ブルーライン 82 77 3.66
6 千葉県 京成本線 82 29 3.34
7 神奈川県 京急本線 68 40 3.33
8 千葉県 東武野田線 63 2 4.75
9 神奈川県 小田急江ノ島線 62 8 3.79
10 大阪府 阪急京都本線 53 32 3.67
11 大阪府 南海本線 52 11 4.22
12 兵庫県 阪神本線 52 23 3.80
13 埼玉県 JR高崎線 51 5 4
14 兵庫県 山陽電鉄本線 51 15 2.98
15 千葉県 JR総武本線(東京-銚子) 47 8 4
16 埼玉県 西武新宿線 45 8 4.17
17 埼玉県 秩父鉄道線 43 10 3.82
18 大阪府 京阪本線 43 10 3.69
19 千葉県 新京成電鉄 43 6 3.6
20 京都府 阪急京都本線 43 27 3.5
# ℹ 503 more rows
結果としては問題ないように見えますが、気になるメッセージが出力されます。
`summarise()` has grouped output by 'Pref'. You can override using the `.groups` argument.
これはLine
はグルーピング変数として機能しなくなり、Pref
変数のみグループ変数になったという意味です。なぜLine
が解除され、Pref
が残るかというと、それはgroup_by()
関数内でPref
を先に指定したからです。group_by(Line, Pref)
でグルーピングすると、Pref
が解除され、Line
のみがグルーピング変数となります。summarise()
後のグルーピング変数の扱いはsummarise()
関数の.groups
引数で指定できます。既定値は.groups = "drop_last"
で「最後のグルーピング変数を解除する」という意味です(場合によっては既定値が"keep"
になりますが、後述します)。多くの場合、summarise()
後は全グルーピングを解除するのが一般的なので、この場合は.groups = "drop"
と指定します。もし、グルーピングを維持したい場合は.groups = "keep"
と指定します。今回の例はsummarise()
内に.group = "drop"
を指定し、グループ化を解除します。
df |>
filter(!is.na(Line)) |>
group_by(Pref, Line) |>
summarise(N = n(),
ScoreN_Sum = sum(ScoreN, na.rm = TRUE),
Score_Mean = mean(Score, na.rm = TRUE),
.groups = "drop") |> # グルーピングをすべて解除する
arrange(desc(N), desc(Score_Mean)) |>
print(n = 20)
# A tibble: 523 × 5
Pref Line N ScoreN_Sum Score_Mean
<chr> <chr> <int> <dbl> <dbl>
1 埼玉県 東武東上線 122 27 3.68
2 東京都 JR 104 231 3.56
3 神奈川県 小田急小田原線 96 31 3.59
4 埼玉県 東武伊勢崎線 96 18 3.51
5 神奈川県 横浜市営ブルーライン 82 77 3.66
6 千葉県 京成本線 82 29 3.34
7 神奈川県 京急本線 68 40 3.33
8 千葉県 東武野田線 63 2 4.75
9 神奈川県 小田急江ノ島線 62 8 3.79
10 大阪府 阪急京都本線 53 32 3.67
11 大阪府 南海本線 52 11 4.22
12 兵庫県 阪神本線 52 23 3.80
13 埼玉県 JR高崎線 51 5 4
14 兵庫県 山陽電鉄本線 51 15 2.98
15 千葉県 JR総武本線(東京-銚子) 47 8 4
16 埼玉県 西武新宿線 45 8 4.17
17 埼玉県 秩父鉄道線 43 10 3.82
18 大阪府 京阪本線 43 10 3.69
19 千葉県 新京成電鉄 43 6 3.6
20 京都府 阪急京都本線 43 27 3.5
# ℹ 503 more rows
group_by()
と同じ機能を持つ.by
引数
ちなみに.by
引数でグルーピングを行う場合は自動的にグルーピングが解除されるので、.groups = "drop"
は不要です。したがって、以下は上記のコードと同じ内容となります。
df |>
filter(!is.na(Line)) |>
summarise(N = n(),
ScoreN_Sum = sum(ScoreN, na.rm = TRUE),
Score_Mean = mean(Score, na.rm = TRUE),
.by = c(Pref, Line)) |>
arrange(desc(N), desc(Score_Mean)) |>
print(n = 20)
# A tibble: 523 × 5
Pref Line N ScoreN_Sum Score_Mean
<chr> <chr> <int> <dbl> <dbl>
1 埼玉県 東武東上線 122 27 3.68
2 東京都 JR 104 231 3.56
3 神奈川県 小田急小田原線 96 31 3.59
4 埼玉県 東武伊勢崎線 96 18 3.51
5 神奈川県 横浜市営ブルーライン 82 77 3.66
6 千葉県 京成本線 82 29 3.34
7 神奈川県 京急本線 68 40 3.33
8 千葉県 東武野田線 63 2 4.75
9 神奈川県 小田急江ノ島線 62 8 3.79
10 大阪府 阪急京都本線 53 32 3.67
11 大阪府 南海本線 52 11 4.22
12 兵庫県 阪神本線 52 23 3.80
13 埼玉県 JR高崎線 51 5 4
14 兵庫県 山陽電鉄本線 51 15 2.98
15 千葉県 JR総武本線(東京-銚子) 47 8 4
16 埼玉県 西武新宿線 45 8 4.17
17 埼玉県 秩父鉄道線 43 10 3.82
18 大阪府 京阪本線 43 10 3.69
19 千葉県 新京成電鉄 43 6 3.6
20 京都府 阪急京都本線 43 27 3.5
# ℹ 503 more rows
ぐるなびに登録されているラーメン屋が最も多い路線は埼玉県内の東武東上線で122店舗があります。東武東上線は東京都と埼玉県をまたがる路線ですので、東武東上線だけならもっと多いかも知れませんね。
ここで.groups
引数についてもう少し考えてみたいと思います。多くの場合、.groups
引数の既定値は"drop_last"
ですが、場合によっては"keep"
になるケースもあります。それは記述統計量の結果が長さ2以上のベクトルである場合です。平均値を求めるmean()
、標準偏差を求めるsd()
などは、結果として長さ1のベクトルを返します。しかし、長さ2以上ののベクトルを返す関数もあります。たとえば、分位数を求めるquantile()
関数があります。quantile(ベクトル名, 0.25)
の場合、第一四分位点のみ返すため、結果は長さ1のベクトルです。しかし、quantile(ベクトル名, c(0.25, 0.5, 0.75))
のように第一四分位点から第三四分位点を同時に計算し、長さ3のベクトルが返されるケースもありますし、第二引数を省略すると、最小値・第一四分位点・第二四分位点・第三四分位点・最大値、つまり、長さ5のベクトルが返される場合があります。
25%
2
0% 25% 50% 75% 100%
1 2 5 12 116
.groups
のデフォルト値が"keep"
になるのは、このように長さ2以上のベクトルが返されるケースです。たとえば、都府県と最寄りの駅の路線でグループ化し、店舗までの徒歩距離の平均値を求めるとします。デフォルト値の変化を見るために、ここではあえて.groups
引数を省略しました。
`summarise()` has grouped output by 'Pref'. You can override using the
`.groups` argument.
# A tibble: 509 × 3
# Groups: Pref [9]
Pref Line Mean
<chr> <chr> <dbl>
1 京都府 JR奈良線 3.33
2 京都府 JR小浜線 16.5
3 京都府 JR山陰本線(京都-米子) 8.67
4 京都府 JR東海道本線(米原-神戸) 16.3
5 京都府 JR片町線〔学研都市線〕 9.4
6 京都府 JR舞鶴線 19
7 京都府 京福北野線 8.36
8 京都府 京福嵐山本線 6.5
9 京都府 京福電気鉄道嵐山本線 6
10 京都府 京都丹後鉄道宮福線 7.44
# ℹ 499 more rows
最初はPref
とLine
でグループ化しましたが、summarise()
の後、Line
がグループ化変数から外されました。つまり、引数が"drop_last"
になっていることです。
それでは、平均値に加えて、第一四分位点と第三四分位点も計算し、Quantile
という名で格納してみましょう。
df |>
filter(!is.na(Walk)) |>
group_by(Pref, Line) |>
summarise(Mean = mean(Walk),
Quantile = quantile(Walk, c(0.25, 0.75)))
Warning: Returning more (or less) than 1 row per `summarise()` group was deprecated in
dplyr 1.1.0.
ℹ Please use `reframe()` instead.
ℹ When switching from `summarise()` to `reframe()`, remember that `reframe()`
always returns an ungrouped data frame and adjust accordingly.
`summarise()` has grouped output by 'Pref', 'Line'. You can override using the
`.groups` argument.
# A tibble: 1,018 × 4
# Groups: Pref, Line [509]
Pref Line Mean Quantile
<chr> <chr> <dbl> <dbl>
1 京都府 JR奈良線 3.33 2
2 京都府 JR奈良線 3.33 5
3 京都府 JR小浜線 16.5 9.75
4 京都府 JR小浜線 16.5 23.2
5 京都府 JR山陰本線(京都-米子) 8.67 2
6 京都府 JR山陰本線(京都-米子) 8.67 15
7 京都府 JR東海道本線(米原-神戸) 16.3 16
8 京都府 JR東海道本線(米原-神戸) 16.3 17
9 京都府 JR片町線〔学研都市線〕 9.4 2
10 京都府 JR片町線〔学研都市線〕 9.4 9
# ℹ 1,008 more rows
同じPref
、Line
のケースが2つずつ出来ています。最初に来る数値は第一四分位点、次に来るのが第三四分位点です。そして最初のグループ化変数であったPref
とLine
が、summarise()
後もグループ化変数として残っていることが分かります。
しかし、気になる警告(warning)が表示されますね。
Warning: Returning more (or less) than 1 row per `summarise()` group was deprecated in
dplyr 1.1.0.
ℹ Please use `reframe()` instead.
ℹ When switching from `summarise()` to `reframe()`, remember that `reframe()`
always returns an ungrouped data frame and adjust accordingly.
これは「長さ2以上のベクトルを返す場合、summarise()
を使うな!」という意味です。今は警告が出るだけで結果としては問題なく動いてますが、{dplyr}1.1.0以降はreframe()
関数の仕様を推奨しています。これはsummarise()
をreframe()
に変えるだけで対応可能です。
df |>
filter(!is.na(Walk)) |>
group_by(Pref, Line) |>
reframe(Mean = mean(Walk),
Quantile = quantile(Walk, c(0.25, 0.75)))
# A tibble: 1,018 × 4
Pref Line Mean Quantile
<chr> <chr> <dbl> <dbl>
1 京都府 JR奈良線 3.33 2
2 京都府 JR奈良線 3.33 5
3 京都府 JR小浜線 16.5 9.75
4 京都府 JR小浜線 16.5 23.2
5 京都府 JR山陰本線(京都-米子) 8.67 2
6 京都府 JR山陰本線(京都-米子) 8.67 15
7 京都府 JR東海道本線(米原-神戸) 16.3 16
8 京都府 JR東海道本線(米原-神戸) 16.3 17
9 京都府 JR片町線〔学研都市線〕 9.4 2
10 京都府 JR片町線〔学研都市線〕 9.4 9
# ℹ 1,008 more rows
reframe()
の場合、自動的にグルーピングが解除されます(.groups
引数の指定はできません)。長さ2以上のベクトルを返す関数を使用する場合は、なるべくsummarise()
のかわりにreframe()
を使いましょう。