心理学を始め,データを扱うサイエンスでは,データ収集の計画,実行と,データに基づいた解析結果,それを踏まえてのコミュニケーションとの間に,「データをわかりやすい形に加工し,可視化し,分析する」という手順がある。このデータの加工をデータハンドリングという。統計といえば「分析」に注目されがちだが,実際にはデータハンドリングと可視化のステップが最も時間を必要とし,重要なプロセスである。
tidyverseの導入
本講義ではtidyverse
をつかったデータハンドリングを扱う。tidyverse
は,データに対する統一的な設計方針を表す概念でもあり,具体的にはそれを実装したパッケージ名でもある。まずはtidyverse
パッケージをインストール(ダウンロード)し,次のコードでRに読み込んでおく。
── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
✔ dplyr 1.1.4 ✔ readr 2.1.5
✔ forcats 1.0.0 ✔ stringr 1.5.1
✔ ggplot2 3.5.1 ✔ tibble 3.2.1
✔ lubridate 1.9.3 ✔ tidyr 1.3.1
✔ purrr 1.0.2
── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag() masks stats::lag()
ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
Attaching core tidyverse packages,と表示され,複数のパッケージ名にチェックマークが入っていたものが表示されただろう。tidyverse
パッケージはこれらの下位パッケージを含むパッケージ群である。これに含まれるdplyr
,tidyr
パッケージはデータの整形に,readr
はファイルの読み込みに,forecats
はFactor型変数の操作に,stringr
は文字型変数の操作に,lubridate
は日付型変数の操作に,tibble
はデータフレーム型オブジェクトの操作に,purrr
はデータに適用する関数に,ggplot2
は可視化に特化したパッケージである。
続いてConflictsについての言及がある。tidyverse
パッケージに限らず,パッケージを読み込むと表示されることのあるこの警告は,「関数名の衝突」を意味している。ここまで,Rを起動するだけで,sqrt
,mean
などの関数が利用できた。これはRの基本関数であるが,具体的にはbase
パッケージに含まれた関数である。Rは起動時にbase
などいくつかのパッケージを自動的に読み込んでいるのである。これに別途パッケージを読み込むとき,あとで読み込まれたパッケージが同名の関数を使っていることがある。このとき,関数名は後から読み込んだもので上書きされる。そのことについての警告が表示されているのである。具体的にみると,dplyr::filter() masks stats::filter()
とあるのは,最初に読み込んでいたstats
パッケージのfilter
関数は,(tidyverse
パッケージに含まれる)dplyr
パッケージのもつ同名の関数で上書きされ,今後はこちらが優先的に利用されるよ,ということを示している。
このような同音異字関数は,関数を特定するときに混乱を招くかもしれない。あるパッケージの関数であることを明示したい場合は,この警告文にあるように,パッケージ名::
関数名,という書き方にすると良い。
パイプ演算子
続いてパイプ演算子について解説する。パイプ演算子はtidyverse
パッケージに含まれていたmagrittr
パッケージで導入されたもので,これによってデータハンドリングの利便性が一気に向上した。そこでRもver 4.2からこの演算子を導入し,特段パッケージのインストールを必要としなくとも使えるようになった。このR本体のパイプ演算子のことを,tidyverse
のそれと区別して,ナイーブパイプと呼ぶこともある。
ともあれこのパイプ演算子がいかに優れたものであるかを解説しよう。次のスクリプトは,あるデータセットの標準偏差を計算するものである。数式で表現すると次の通り。ここで\(\bar{x}\)はデータベクトル\(x\)の算術平均。 \[v = \sqrt{\frac{1}{n}\sum_{i=1}^n (x_i - \bar{x})^2}\]
dat <- c(10, 13, 15, 12, 14) # データ
M <- mean(dat) # 平均
dev <- dat - M # 平均偏差
pow <- dev^2 # 平均偏差の2乗
variance <- mean(pow) # 平均偏差の2乗の平均が分散
standardDev <- sqrt(variance) # 分散の正の平方根が標準偏差
ここでは,標準偏差オブジェクトstandardDev
を作るまでに平均オブジェクトM
,平均偏差ベクトルdev
,その2乗したものpow
,分散variance
と4つものオブジェクトを作って答えに到達している。また,作られるオブジェクトが左側にあり,その右側にどのような演算をしているかが記述されているため,頭の中では「オブジェクトを作る,次の計算で」と読んでいったことだろう。
パイプ演算子はこの思考の流れをそのまま具現化する。パイプ演算子は%>%
と書き,左側の演算結果をパイプ演算子の右側に来る関数の第一引数として右側に渡す役目をする。これを踏まえて上のスクリプトを書き直してみよう。ちなみにパイプ演算子はショートカットCtrl(Cmd)+Shift+M
で入力できる。
dat <- c(10, 13, 15, 12, 14)
standardDev <- dat %>%
{
. - mean(.)
} %>%
{
.^2
} %>%
mean() %>%
sqrt()
ここでピリオド(.
)は,前の関数から引き継いだもの(プレイスホルダー)であり,二行目は{dat - mean(dat)}
,すなわち平均偏差の計算を意味している。それを次のパイプで二乗し,平均し,平方根を取っている。平均や平方根を取るときにプレイスホルダーが明示されていないのは,引き受けた引数がどこに入るかが明らかなので省略しているからである。
この例に見るように,パイプ演算子を使うと,データ\(\to\)平均偏差$\(2乗\)\(平均\)$平方根,という計算の流れと,スクリプトの流れが一致しているため,理解しやすくなったのではないだろうか。
また,ここでの計算は,次のように書くこともできる。
standardDev <- sqrt(mean((dat - mean(dat))^2))
この書き方は,関数の中に関数がある入れ子状態になっており,\(y = h(g(f(x)))\)のような形式である。これも対応するカッコの内側から読み解いていく必要があり,思考の流れと逆転しているため理解が難しい。パイプ演算子を使うと,x %>% f() %>% g() %>% h() -> y
のように記述できるため,苦労せずに読むことができる。
以下はこのパイプ演算子を使った記述で進めていくので,この表記法(およびショートカット)に慣れていこう。
課題1.パイプ演算子
sqrt
,mean
関数がbase
パッケージに含まれることをヘルプで確認してみましょう。どこを見れば良いでしょうか。filter
,lag
関数はどうでしょうか。
tidyverse
パッケージを読み込んだことで,filter
関数はdplyr
パッケージのものが優先されることになりました。dplyr
パッケージのfilter
関数をヘルプで見てみましょう。
- 上書きされる前の
stats
パッケージのfilter
関数に関するヘルプを見てみましょう。
- 先ほどのデータを使って,平均値絶対偏差(MeanAD)および中央絶対偏差(MAD)をパイプ演算子を使って算出してみましょう。なお平均値絶対偏差,中央値絶対偏差は次のように定義されるものです。また絶対値を計算するR関数は
abs
です。
\[MeanAD = \frac{1}{n}\sum_{i=1}^n|x_i - \bar{x}|\] \[MAD = median(|x_1-median(x)|,\cdots,|x_n-median(x)|)\]
列選択と行選択
ここからはtidyverse
を使ったより具体的なデータハンドリングについて言及する。 まずは特定の列および行だけを抜き出すことを考える。データの一部にのみ処理を加えたい場合に重宝する。
列選択
列選択はselect
関数である。これはtidyverse
パッケージ内のdplyr
パッケージに含まれている。 select
関数はMASS
パッケージなど,他のパッケージに同名の関数が含まれることが多いので注意が必要である。
例示のために,Rがデフォルトで持つサンプルデータ,iris
を用いる。なお,iris
データは150行あるので,以下ではデータセットの冒頭を表示するhead
関数を用いているが,演習の際にはhead
を用いなくても良い。
# irisデータの確認
iris %>% head()
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
# 一部の変数を抜き出す
iris %>%
select(Sepal.Length, Species) %>%
head()
Sepal.Length Species
1 5.1 setosa
2 4.9 setosa
3 4.7 setosa
4 4.6 setosa
5 5.0 setosa
6 5.4 setosa
逆に,一部の変数を除外したい場合はマイナスをつける。
iris %>%
select(-Species) %>%
head()
Sepal.Length Sepal.Width Petal.Length Petal.Width
1 5.1 3.5 1.4 0.2
2 4.9 3.0 1.4 0.2
3 4.7 3.2 1.3 0.2
4 4.6 3.1 1.5 0.2
5 5.0 3.6 1.4 0.2
6 5.4 3.9 1.7 0.4
# 複数変数の除外
iris %>%
select(-c(Petal.Length, Petal.Width)) %>%
head()
Sepal.Length Sepal.Width Species
1 5.1 3.5 setosa
2 4.9 3.0 setosa
3 4.7 3.2 setosa
4 4.6 3.1 setosa
5 5.0 3.6 setosa
6 5.4 3.9 setosa
これだけでも便利だが,select
関数は適用時に抜き出す条件を指定してやればよく,そのために便利な以下のような関数がある。
- starts_with()
- ends_with()
- contains()
- matches()
使用例を以下に挙げる。
# starts_withで特定の文字から始まる変数を抜き出す
iris %>%
select(starts_with("Petal")) %>%
head()
Petal.Length Petal.Width
1 1.4 0.2
2 1.4 0.2
3 1.3 0.2
4 1.5 0.2
5 1.4 0.2
6 1.7 0.4
# ends_withで特定の文字で終わる変数を抜き出す
iris %>%
select(ends_with("Length")) %>%
head()
Sepal.Length Petal.Length
1 5.1 1.4
2 4.9 1.4
3 4.7 1.3
4 4.6 1.5
5 5.0 1.4
6 5.4 1.7
# containsで部分一致する変数を取り出す
iris %>%
select(contains("etal")) %>%
head()
Petal.Length Petal.Width
1 1.4 0.2
2 1.4 0.2
3 1.3 0.2
4 1.5 0.2
5 1.4 0.2
6 1.7 0.4
# matchesで正規表現による選択をする
iris %>%
select(matches(".t.")) %>%
head()
Sepal.Length Sepal.Width Petal.Length Petal.Width
1 5.1 3.5 1.4 0.2
2 4.9 3.0 1.4 0.2
3 4.7 3.2 1.3 0.2
4 4.6 3.1 1.5 0.2
5 5.0 3.6 1.4 0.2
6 5.4 3.9 1.7 0.4
ここで触れた正規表現とは,文字列を特定するためのパターンを指定する表記ルールであり,R言語に限らずプログラミング言語一般で用いられるものである。書誌検索などでも用いられることがあり,任意の文字列や先頭・末尾の語などを記号(メタ文字)を使って表現するものである。詳しくは正規表現で検索すると良い(たとえばこちらのサイトなどがわかりやすい。)
行選択
一般にデータフレームは列に変数が並んでいるので,select
関数による列選択とは変数選択とも言える。 これに対し,行方向にはオブザベーションが並んでいるので,行選択とはオブザベーション(ケース,個体)の選択である。行選択にはdplyr
のfilter
関数を使う。
# Sepal.Length変数が6以上のケースを抜き出す
iris %>%
filter(Sepal.Length > 6) %>%
head()
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1 7.0 3.2 4.7 1.4 versicolor
2 6.4 3.2 4.5 1.5 versicolor
3 6.9 3.1 4.9 1.5 versicolor
4 6.5 2.8 4.6 1.5 versicolor
5 6.3 3.3 4.7 1.6 versicolor
6 6.6 2.9 4.6 1.3 versicolor
# 特定の種別だけ抜き出す
iris %>%
filter(Species == "versicolor") %>%
head()
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1 7.0 3.2 4.7 1.4 versicolor
2 6.4 3.2 4.5 1.5 versicolor
3 6.9 3.1 4.9 1.5 versicolor
4 5.5 2.3 4.0 1.3 versicolor
5 6.5 2.8 4.6 1.5 versicolor
6 5.7 2.8 4.5 1.3 versicolor
# 複数指定の例
iris %>%
filter(Species != "versicolor", Sepal.Length > 6) %>%
head()
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
1 6.3 3.3 6.0 2.5 virginica
2 7.1 3.0 5.9 2.1 virginica
3 6.3 2.9 5.6 1.8 virginica
4 6.5 3.0 5.8 2.2 virginica
5 7.6 3.0 6.6 2.1 virginica
6 7.3 2.9 6.3 1.8 virginica
ここで==
とあるのは一致しているかどうかの判別をするための演算子である。=
ひとつだと「オブジェクトへの代入」と同じになるので,判別条件の時には重ねて表記する。同様に,!=
とあるのはnot equal,つまり不一致のとき真になる演算子である。
変数を作る・再割り当てする
既存の変数から別の変数を作る,あるいは値の再割り当ては,データハンドリング時に最もよく行う操作のひとつである。たとえば連続変数をある値を境に「高群・低群」というカテゴリカルな変数に作り変えたり,単位を変換するために線形変換したりすることがあるだろう。このように,変数を操作するときに「既存の変数を加工して特徴量を作りだす」というときの操作は,基本的にdplyr
のmutate
関数を用いる。次の例をみてみよう。
mutate(iris, Twice = Sepal.Length * 2) %>% head()
Sepal.Length Sepal.Width Petal.Length Petal.Width Species Twice
1 5.1 3.5 1.4 0.2 setosa 10.2
2 4.9 3.0 1.4 0.2 setosa 9.8
3 4.7 3.2 1.3 0.2 setosa 9.4
4 4.6 3.1 1.5 0.2 setosa 9.2
5 5.0 3.6 1.4 0.2 setosa 10.0
6 5.4 3.9 1.7 0.4 setosa 10.8
新しくTwice
変数ができたのが確認できるだろう。この関数はパイプ演算子の中で使うことができる(というかその方が主な使い方である)。次の例は,Sepal.Length
変数を高群と低群の2群に分けるものである。
iris %>%
select(Sepal.Length) %>%
mutate(Sepal.HL = ifelse(Sepal.Length > mean(Sepal.Length), 1, 2)) %>%
mutate(Sepal.HL = factor(Sepal.HL, label = c("High", "Low"))) %>%
head()
Sepal.Length Sepal.HL
1 5.1 Low
2 4.9 Low
3 4.7 Low
4 4.6 Low
5 5.0 Low
6 5.4 Low
ここでもちいたifelse
関数は,if(条件判断,真のときの処理,偽のときの処理)
という形でもちいる条件分岐関数であり,ここでは平均より大きければ1,そうでなければ2を返すようになっている。mutate
関数でこの結果をSepal.HL変数に代入(生成)し,次のmutate
関数では今作ったSepal.HL変数をFactor型に変換して,その結果をまたSepal.HL変数に代入(上書き)している。このように,変数の生成先を生成元と同じにしておくと上書きされるため,たとえば変数の型変換(文字型から数値型へ,数値型からFactor型へ,など)にも用いることができる。
課題2.select, filter, mutate
Baseball.csv
を読み込んで、データフレームdf
に代入しましょう。
df
には複数の変数が含まれています。変数名の一覧はnames
関数で確認できます。df
オブジェクトに含まれる変数名を確認しましょう。
df
には多くの変数がありますが、必要なのは年度(Year)、選手名(Name)、所属球団(team)、身長(height)、体重(weight)、年俸(salary)、守備位置(position)だけです。これらの変数だけを選択して、df2
オブジェクトを作成しましょう。
df2
には数年分のデータが含まれています。2020年度
のデータだけを分析したいので、選別してみましょう。
- 同じく、
2020年度
の阪神タイガース
に関するデータだけを選別してみましょう。
- 同じく、
2020年度
の阪神タイガース
以外のデータセットはどのようにして選別できるでしょうか。
- 選手の身体的特徴を表すBMI変数を作成しましょう。なお、BMIは体重(kg)を身長(m)の二乗で除したものです。変数
height
の単位がcmであることに注意しましょう。
- 投手と野手を区別する新しい変数
position2
を作成しましょう。これはFactor型にします。なお、野手は投手でないもの、すなわち内野手、外野手、捕手のいずれかです。
- 日本プロ野球界は大きく分けてセリーグ(Central League)とパリーグ(Pacific League)に分かれています。セリーグに所属する球団はGiants, Carp, Tigers, Swallows, Dragons, DeNAであり、パ・リーグはそれ以外です。
df2
を加工して、所属するリーグの変数League
を作成しましょう。この変数もFactor型にしておきましょう。
- 変数
Year
は語尾に「年度」という文字が入っているため文字列型になっています。実際に使うときは不便なので、「年度」という文字を除外し、数値型変数に変換しましょう。
ロング型とワイド型
ここまでみてきたデータは行列の2次元に,ケース\(\times\)変数の形で格納されていた。この形式は,人間が見て管理するときにわかりやすい形式をしているが,計算機にとっては必ずしもそうではない。たとえば「神エクセル」と揶揄されることがあるように,稀に表計算ソフトを方眼紙ソフトあるいは原稿用紙ソフトと勘違いしたかのような使い方がなされる場合がある。人間にとってはわかりやすい(見て把握しやすい)かもしれないが,計算機にとって構造が把握できないため,データ解析に不向きである。巷には,こうした分析しにくい電子データがまだまだたくさん存在する。
これをうけて2020年12月,総務省により機械判読可能なデータの表記方法の統一ルールが策定された(総務省 2020)。それには次のようなチェック項目が含まれている。
- ファイル形式はExcelかCSVとなっているか
- 1セル1データとなっているか
- 数値データは数値属性とし,文字列を含まないこと
- セルの結合をしていないか
- スペースや改行等で体裁を整えていないか
- 項目名を省略していないか
- 数式を使用している場合は,数値データに修正しているか
- オブジェクトを使用していないか
- データの単位を記載しているか
- 機種依存文字を使用していないか
- データが分断されていないか
- 1シートに複数の表が掲載されていないか
データの入力の基本は,1行に1ケースの情報が入っている,過不足のない1つのデータセットを作ることといえるだろう。
同様に,計算機にとって分析しやすいデータの形について,Hadley (2014) が提唱したのが整然データ(Tidy Data)という考え方である。整然データとは,次の4つの特徴を持ったデータ形式のことを指す。
- 個々の変数(variable)が1つの列(column)をなす。
- 個々の観測(observation)が1つの行(row)をなす。
- 個々の観測の構成単位の類型(type of observational unit)が1つの表(table)をなす。
- 個々の値(value)が1つのセル(cell)をなす。
この形式のデータであれば,計算機が変数と値の対応構造を把握しやすく,分析しやすいデータになる。データハンドリングの目的は,混乱している雑多なデータを,利用しやすい整然データの形に整えることであると言っても過言ではない。 さて,ここでよく考えてみると,変数名も一つの変数だと考えることに気づく。一般に,行列型のデータは次のような書式になっている。
ワイド型データ
東京 |
晴 |
晴 |
雨 |
雨 |
大阪 |
晴 |
曇 |
晴 |
晴 |
福岡 |
晴 |
曇 |
曇 |
雨 |
ここで,たとえば大阪の夕方の天気を見ようとすると「晴れ」であることは明らかだが,この時の視線の動きは大阪行の,夕方列,という参照の仕方である。言い方を変えると,大阪・夕方の「晴れ」を参照するときに,行と列の両方のラベルを参照する必要がある。
ここで同じデータを次のように並べ替えてみよう。
ロング型データ
東京 |
午前 |
晴 |
東京 |
午後 |
晴 |
東京 |
夕方 |
雨 |
東京 |
深夜 |
雨 |
大阪 |
午前 |
晴 |
大阪 |
午後 |
曇 |
大阪 |
夕方 |
晴 |
大阪 |
深夜 |
晴 |
福岡 |
午前 |
晴 |
福岡 |
午後 |
曇 |
福岡 |
夕方 |
曇 |
福岡 |
深夜 |
雨 |
このデータが表す情報は同じだが,大阪・夕方の条件を絞り込むことは行選択だけでよく,計算機にとって使いやすい。この形式をロング型データ,あるいは「縦持ち」データという。これに対して前者の形式をワイド型データ,あるいは「横持ち」データという。
ロング型データにする利点のひとつは,欠損値の扱いである。ワイド型データで欠損値が含まれる場合,その行あるいは列全体を削除するのは無駄が多く,かと言って行・列両方を特定するのは技術的にも面倒である。これに対しロング型データの場合は,当該行を絞り込んで削除するだけで良い。
tidyverse
には(正確にはtidyr
には),このようなロング型データ,ワイド型データの変換関数が用意されている。 実例とともに見てみよう。まずはワイド型データをロング型に変換するpivot_longer
である。
iris %>% pivot_longer(-Species)
# A tibble: 600 × 3
Species name value
<fct> <chr> <dbl>
1 setosa Sepal.Length 5.1
2 setosa Sepal.Width 3.5
3 setosa Petal.Length 1.4
4 setosa Petal.Width 0.2
5 setosa Sepal.Length 4.9
6 setosa Sepal.Width 3
7 setosa Petal.Length 1.4
8 setosa Petal.Width 0.2
9 setosa Sepal.Length 4.7
10 setosa Sepal.Width 3.2
# ℹ 590 more rows
ここでは元のiris
データについて,Species
セルを軸として,それ以外の変数名と値をname
,value
に割り当てて縦持ちにしている。
逆に,ロング型のデータをワイド型に持ち替えるには,pivot_wider
を使う。 実例は以下の通りである。
iris %>%
select(-Species) %>%
rowid_to_column("ID") %>%
pivot_longer(-ID) %>%
pivot_wider(id_cols = ID, names_from = name, values_from = value)
# A tibble: 150 × 5
ID Sepal.Length Sepal.Width Petal.Length Petal.Width
<int> <dbl> <dbl> <dbl> <dbl>
1 1 5.1 3.5 1.4 0.2
2 2 4.9 3 1.4 0.2
3 3 4.7 3.2 1.3 0.2
4 4 4.6 3.1 1.5 0.2
5 5 5 3.6 1.4 0.2
6 6 5.4 3.9 1.7 0.4
7 7 4.6 3.4 1.4 0.3
8 8 5 3.4 1.5 0.2
9 9 4.4 2.9 1.4 0.2
10 10 4.9 3.1 1.5 0.1
# ℹ 140 more rows
今回はSpecies
変数を除外し,別途ID
変数として行番号を変数に付与した。この行番号をキーに,変数名はnames
列から,その値はvalue
列から持ってくることでロング型をワイド型に変えている。
グループ化と要約統計量
データをロング型にすることで,変数やケースの絞り込みが容易になる。その上で,ある群ごとに要約した統計量を算出したい場合は,group_by
変数によるグループ化と,summarise
あるいはreframe
がある。実例を通して確認しよう。
iris %>% group_by(Species)
# A tibble: 150 × 5
# Groups: Species [3]
Sepal.Length Sepal.Width Petal.Length Petal.Width Species
<dbl> <dbl> <dbl> <dbl> <fct>
1 5.1 3.5 1.4 0.2 setosa
2 4.9 3 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 3.6 1.4 0.2 setosa
6 5.4 3.9 1.7 0.4 setosa
7 4.6 3.4 1.4 0.3 setosa
8 5 3.4 1.5 0.2 setosa
9 4.4 2.9 1.4 0.2 setosa
10 4.9 3.1 1.5 0.1 setosa
# ℹ 140 more rows
上のコードでは,一見したところ表示されたデータに違いがないように見えるが,出力時にSpecies[3]
と表示されていることがわかる。ここで,Species変数の3水準で群分けされていることが示されている。これを踏まえて,summarise
してみよう。
iris %>%
group_by(Species) %>%
summarise(
n = n(),
Mean = mean(Sepal.Length),
Max = max(Sepal.Length),
IQR = IQR(Sepal.Length)
)
# A tibble: 3 × 5
Species n Mean Max IQR
<fct> <int> <dbl> <dbl> <dbl>
1 setosa 50 5.01 5.8 0.400
2 versicolor 50 5.94 7 0.7
3 virginica 50 6.59 7.9 0.675
ここではケース数(n
),平均(mean
),最大値(max
),四分位範囲(IQR
)を算出した。
また,ここではSepal.Length
についてのみ算出したが,他の数値型変数に対しても同様の計算がしたい場合は,across
関数を使うことができる。
iris %>%
group_by(Species) %>%
summarise(across(
c(Sepal.Length, Sepal.Width, Petal.Length),
~ mean(.x)
))
# A tibble: 3 × 4
Species Sepal.Length Sepal.Width Petal.Length
<fct> <dbl> <dbl> <dbl>
1 setosa 5.01 3.43 1.46
2 versicolor 5.94 2.77 4.26
3 virginica 6.59 2.97 5.55
ここで,~mean(.x)
の書き方について言及しておく。チルダ(tilda,~
)で始まるこの式を,Rでは特にラムダ関数とかラムダ式と呼ぶ。これはこの場で使う即席関数の作り方である。別の方法として,正式に関数を作る関数function
を使って次のように書くこともできる。
iris %>%
group_by(Species) %>%
summarise(across(
c(Sepal.Length, Sepal.Width, Petal.Length),
function(x) {
mean(x)
}
))
# A tibble: 3 × 4
Species Sepal.Length Sepal.Width Petal.Length
<fct> <dbl> <dbl> <dbl>
1 setosa 5.01 3.43 1.46
2 versicolor 5.94 2.77 4.26
3 virginica 6.59 2.97 5.55
ラムダ関数や自作関数の作り方については,後ほどあらためて触れるとして,ここでは複数の変数に関数をあてがう方法を確認して置いて欲しい。across
関数で変数を選ぶ際は,select
関数の時に紹介したstarts_with
なども利用できる。次に示す例は,複数の変数を選択し,かつ,複数の関数を適用する例である。複数の関数を適用するために,ラムダ関数をリストで与えることができる。
iris %>%
group_by(Species) %>%
summarise(across(starts_with("Sepal"),
.fns = list(
M = ~ mean(.x),
Q1 = ~ quantile(.x, 0.25),
Q3 = ~ quantile(.x, 0.75)
)
))
# A tibble: 3 × 7
Species Sepal.Length_M Sepal.Length_Q1 Sepal.Length_Q3 Sepal.Width_M
<fct> <dbl> <dbl> <dbl> <dbl>
1 setosa 5.01 4.8 5.2 3.43
2 versicolor 5.94 5.6 6.3 2.77
3 virginica 6.59 6.22 6.9 2.97
# ℹ 2 more variables: Sepal.Width_Q1 <dbl>, Sepal.Width_Q3 <dbl>
課題3.データの整形
- 上で作った
df2
オブジェクトを利用します。環境にdf2
オブジェクトが残っていない場合は、もう一度上の課題に戻って作り直しておきましょう。
- 年度(Year)でグルーピングし、年度ごとの登録選手数(データの数)、平均年俸を見てみましょう。
- 年度(Year)とチーム(team)でグルーピングし、同じく年度ごとの登録選手数(データの数)、平均年俸を見てみましょう。
- 続いて、一行に1年度分、列に各チームと変数の組み合わせが入った、ワイド型データを作りたいと思います。
pivot_wider
を使って上のオブジェクトをワイド型にしてみましょう。
- ワイド型になったデータを、
Year
変数をキーにしてpivot_longer
でロング型データに変えてみましょう。
Hadley, Wickham. 2014.
“Tidy Data.” Journal of Statistical Software 59: 1–23.
https://doi.org/10.18637/jss.v059.i10.
総務省. 2020.
“統計表における機械判別可能なデータ作成に関する表記方法.” 統計企画会議申し合わせ.
https://www.soumu.go.jp/main_content/000723697.pdf.