df <- readr::read_csv("../data/BaseballDecade.csv")
# 変数名の確認
names(df)
# 必要な変数だけを選択
df2 <- df %>%
dplyr::select(Year, Name, team, height, weight, salary, position)
df2U7. dplyr: データの絞り込みと加工
ユニット概要
分類: 幹(全員必須)
依存関係: U0 → U1 → U2 → U4 → U5 → U6 → U7
学習目標:
- パイプ演算子(
%>%)を使ってデータ処理を連結できる select()で列(変数)を選択できるfilter()で行(ケース)を条件で絞り込めるmutate()で新しい変数を作成・変換できるgroup_by()+summarise()でグループ別の要約統計量を算出できるacross()で複数の変数に同じ処理を一括で適用できる
事前知識: dplyrとパイプ演算子
dplyrとは
dplyrはtidyverseパッケージ群に含まれるデータ操作パッケージです。データフレームに対して、列の選択、行の絞り込み、新しい変数の作成、集計といった操作を、一貫した文法で行うことができます。
dplyrの主要な関数(動詞)は次の通りです:
| 関数 | 操作 | 意味 |
|---|---|---|
select() |
列選択 | 使いたい変数だけを抜き出す |
filter() |
行選択 | 条件に合うケースだけを残す |
mutate() |
変数作成 | 既存の変数から新しい変数を作る |
group_by() |
グループ化 | カテゴリごとにデータを分ける |
summarise() |
要約 | グループごとに統計量を計算する |
arrange() |
並べ替え | 指定した変数の値で行を並べる |
これらは英語の動詞として読めるように設計されているため、コードが「データに対して何をするか」を自然に表現できます。
パイプ演算子
dplyrの関数はパイプ演算子(%>%)と組み合わせて使います。パイプ演算子は、左側の結果を右側の関数の第一引数として渡す演算子です。RStudioでは Ctrl+Shift+M(Mac: Cmd+Shift+M)で入力できます。
パイプ演算子を使わない場合、標準偏差の計算は次のようになります:
dat <- c(10, 13, 15, 12, 14)
M <- mean(dat) # 平均
dev <- dat - M # 平均偏差
pow <- dev^2 # 2乗
variance <- mean(pow) # 分散
standardDev <- sqrt(variance) # 標準偏差途中結果を格納するオブジェクトが4つも必要です。あるいは関数を入れ子にすると:
standardDev <- sqrt(mean((dat - mean(dat))^2))今度は内側から読む必要があり、思考の流れと逆転します。パイプ演算子を使うと:
standardDev <- dat %>%
{. - mean(.)} %>%
{.^2} %>%
mean() %>%
sqrt()データ → 平均偏差 → 2乗 → 平均 → 平方根、という計算の流れとスクリプトの流れが一致します。dplyrの関数はすべて「第一引数にデータフレームを取る」設計なので、パイプで繋ぐだけで複雑な操作を直感的に記述できます。
関数名の衝突に注意
tidyverseを読み込むと、Rの基本パッケージと同名の関数が上書きされます。特に注意が必要なのは:
dplyr::filter()がstats::filter()を上書きdplyr::lag()がstats::lag()を上書き
別のパッケージの同名関数を使いたい場合は、stats::filter()のようにパッケージ名::関数名で明示してください。
ランクC: 基礎知識を確認しよう
7-C-1
パイプ演算子%>%は、左側の結果を右側の関数の第一引数として渡す。
これが正しいです。たとえばiris %>% head(3)はhead(iris, 3)と同じ意味になります。パイプの左側の結果が、右側の関数の第一引数に入ります。dplyrの関数はすべて第一引数にデータフレームを取るので、パイプとの相性が良いのです。
7-C-2
select()関数は行(ケース)を選択するための関数である。
select()は列(変数)を選択する関数です。行を選択するのはfilter()です。
7-C-3
次のコードを見てください。
iris %>% select(-Species)ここで-Speciesは何を意味していますか?
select()関数でマイナス(-)を付けると、その列を除外した残りの列を選択します。元のデータが変更されるわけではなく、結果として返されるデータフレームにSpecies列が含まれなくなります。
7-C-4
filter()関数で、Sepal.Lengthが5以上のケースを選ぶコードはどれですか?
filter()関数では条件式を書きます。等号の判定には==、大小比較には>, >=, <, <=を使います。=ひとつは代入演算子なので条件判定には使えません。また、select()は列選択なので行の絞り込みには使えません。
7-C-5
mutate()関数は新しい変数を作成するときに使う。既存の変数を上書きすることもできる。
mutate()で生成先の変数名を既存の変数名と同じにすると、上書きされます。たとえば変数の型変換(文字型→Factor型など)にも使えます:
iris %>% mutate(Species = as.character(Species))7-C-6
次のコードを見てください。
iris %>%
group_by(Species) %>%
summarise(n = n(), M = mean(Sepal.Length))このコードの結果は何行になりますか?
group_by(Species)でSpeciesの水準ごと(setosa, versicolor, virginica)にグループ化し、summarise()で各グループの要約を1行にまとめます。3水準あるので結果は3行です。
7-C-7
filter()で複数の条件を同時に指定したい場合、カンマ(,)で区切るとどのように解釈されますか?
filter()関数内でカンマで条件を区切ると、AND条件として扱われます。つまり、すべての条件を同時に満たすケースだけが残ります。
# Sepal.Lengthが6以上 かつ Speciesがversicolorのケースのみ
iris %>% filter(Sepal.Length >= 6, Species == "versicolor")OR条件を使いたい場合は|演算子を使います:
# Speciesがsetosa または virginica のケース
iris %>% filter(Species == "setosa" | Species == "virginica")7-C-8
select()関数でstarts_with("Sepal")と書くと何が起きますか?
starts_with()はselect()関数内で使えるヘルパー関数で、指定した文字列で始まる列名を選択します。
iris %>% select(starts_with("Sepal"))
# → Sepal.Length, Sepal.Width の2列が選択される同様のヘルパー関数:
ends_with("Length"): 指定文字列で終わる列contains("etal"): 指定文字列を含む列matches(".t."): 正規表現にマッチする列
7-C-9
次のコードで、==を=にするとどうなりますか?
iris %>% filter(Species == "setosa")==は「等しいかどうかを判定する」比較演算子です。=は引数への代入を意味するため、filter()内で=を使うと意図しない挙動やエラーになります。条件判定には必ず==を使ってください。
同様に、「等しくない」は!=で表します。
7-C-10
summarise()関数の中で使えるn()は何を返しますか?
n()はsummarise()の中で使う関数で、各グループのケース数(行数)を返します。group_by()と組み合わせると、グループごとのケース数が得られます。
iris %>%
group_by(Species) %>%
summarise(count = n())
# setosa: 50, versicolor: 50, virginica: 50ランクB: 実践スキルを磨こう
7-B-1
BaseballDecade.csvを読み込んで、どんな変数があるか確認してください。その後、年度(Year)、選手名(Name)、所属球団(team)、身長(height)、体重(weight)、年俸(salary)、守備位置(position)の7つの変数だけを選択してください。
names()関数やstr()関数でデータの変数名を確認できます。select()関数で必要な変数を選びます。
select()で列名を列挙するだけで、必要な変数のみを持つデータフレームが作れます。
7-B-2
7-B-1で作ったdf2から、2020年度のデータだけを取り出してください。
filter()を使います。Year変数の値がどのような形式(数値か文字列か)になっているか、class()やunique()で確認してから条件を書きましょう。
# Year変数の形式を確認
unique(df2$Year)
# 「2020年度」のように文字列で入っている場合
df2 %>%
dplyr::filter(Year == "2020年度")
# 数値型の場合
# df2 %>% dplyr::filter(Year == 2020)filter()の条件式では==(等号2つ)を使います。=ひとつだと代入になってしまうので注意してください。
7-B-3
7-B-1のデータから、2020年度の阪神タイガース(Tigers)のデータだけを取り出してください。また、阪神タイガース以外のデータも取り出してみてください。
filter()に複数条件を渡すとAND条件になります。「以外」は!=を使います。
# 2020年度の阪神タイガース
df2 %>%
dplyr::filter(Year == "2020年度", team == "Tigers")
# 2020年度の阪神タイガース以外
df2 %>%
dplyr::filter(Year == "2020年度", team != "Tigers")filter()にカンマ区切りで条件を並べるとAND条件です。!=は「等しくない」を表す比較演算子です。
7-B-4
BMI(Body Mass Index)を計算する新しい変数を追加してください。BMIは体重(kg) / 身長(m)の2乗で計算します。height変数の単位がcmであることに注意してください。
mutate()で新しい変数を作ります。身長をメートルに変換するには100で割ります。
df2 %>%
dplyr::mutate(BMI = weight / (height / 100)^2)mutate()の中で既存の変数を使った式を書くことで、新しい変数BMIが追加されます。元のデータフレームは変更されず、新しいデータフレームが返されます(結果を保存したい場合は<-で代入が必要です)。
7-B-5
投手(positionが「投手」)と野手(それ以外)を区別する新しいFactor型変数position2を作成してください。
mutate()の中でifelse()とfactor()を組み合わせます。ifelse(条件, 真の値, 偽の値)の形式です。
df2 %>%
dplyr::mutate(
position2 = ifelse(position == "投手", "投手", "野手"),
position2 = factor(position2)
)mutate()は連続して同じ変数名を指定できます。最初のmutate()で文字型のposition2を作り、次のmutate()でFactor型に変換しています。1つのmutate()の中でカンマで区切って複数の変数操作を書くことも可能です。
7-B-6
年度(Year)ごとに、登録選手数(行数)と平均年俸を集計してください。
group_by()でグループ化してからsummarise()で集計します。ケース数はn()、平均はmean()です。年俸に欠損値がある場合はna.rm = TRUEを指定します。
df2 %>%
dplyr::group_by(Year) %>%
dplyr::summarise(
n = n(),
mean_salary = mean(salary, na.rm = TRUE)
)group_by(Year)で年度ごとにグループ化し、summarise()で各グループのケース数と平均年俸を計算しています。結果は年度ごとに1行のデータフレームになります。
7-B-7
年度(Year)と球団(team)の2つでグループ化し、登録選手数、平均年俸、最高年俸、最低年俸を集計してください。
group_by()に複数の変数を指定できます。max()とmin()で最大・最小を求めます。
df2 %>%
dplyr::group_by(Year, team) %>%
dplyr::summarise(
n = n(),
mean_salary = mean(salary, na.rm = TRUE),
max_salary = max(salary, na.rm = TRUE),
min_salary = min(salary, na.rm = TRUE),
.groups = "drop"
).groups = "drop"を指定すると、集計後にグループ化が解除されます。指定しない場合、Yearのグループ化が残ったままになります。
7-B-8
irisデータセットについて、SpeciesごとにSepalで始まる変数の平均値と標準偏差を一括で計算してください。
across()関数をsummarise()の中で使います。starts_with("Sepal")で対象変数を指定し、.fnsに複数の関数をリストで渡します。
iris %>%
dplyr::group_by(Species) %>%
dplyr::summarise(
dplyr::across(
starts_with("Sepal"),
.fns = list(
M = ~mean(.x),
SD = ~sd(.x)
)
)
)across()により、Sepal.LengthとSepal.Widthの両方に対してmean()とsd()が適用されます。結果の列名はSepal.Length_M, Sepal.Length_SD, Sepal.Width_M, Sepal.Width_SDのように自動で命名されます。
~mean(.x)はラムダ関数(無名関数)の書き方で、.xは各変数の値が入るプレイスホルダーです。
7-B-9
セ・リーグとパ・リーグを区別する新しいFactor型変数Leagueを作成してください。セ・リーグに所属する球団はGiants, Carp, Tigers, Swallows, Dragons, DeNAです。
mutate()の中でifelse()を使います。セ・リーグの球団名のベクトルを作って、%in%演算子で判定すると便利です。
central <- c("Giants", "Carp", "Tigers", "Swallows", "Dragons", "DeNA")
df2 %>%
dplyr::mutate(
League = ifelse(team %in% central, "Central", "Pacific"),
League = factor(League)
)%in%演算子は「左側の値が右側のベクトルに含まれるか」を判定します。複数の値と一致するかどうかを調べるときに、==をORで繋げるより簡潔に書けます。
7-B-10
7-B-9で作ったLeague変数を使って、リーグごと・年度ごとの平均年俸を集計してください。さらにarrange()で年度の昇順に並べ替えてください。
group_by()にLeagueとYearを指定し、summarise()で平均を計算した後、arrange()で並べ替えます。
central <- c("Giants", "Carp", "Tigers", "Swallows", "Dragons", "DeNA")
df2 %>%
dplyr::mutate(
League = ifelse(team %in% central, "Central", "Pacific"),
League = factor(League)
) %>%
dplyr::group_by(Year, League) %>%
dplyr::summarise(
mean_salary = mean(salary, na.rm = TRUE),
.groups = "drop"
) %>%
dplyr::arrange(Year)パイプで操作を繋げることで、変数の作成 → グループ化 → 集計 → 並べ替えという一連の処理を、上から下へ読める流れで記述できます。これがdplyrとパイプ演算子の威力です。
ランクA: AI協働に挑戦しよう
7-A-1
課題: BaseballDecade.csvを使って、「年俸の高い選手トップ10」を年度ごとに抽出し、そのトップ10の顔ぶれ(球団、ポジション)が年度によってどう変化するか調べてください。
AIに相談しながら:
- 年度ごとに年俸トップ10を抽出する方法を考える
- 結果を見やすい形にまとめる
- 何か面白い傾向が読み取れるか考察する
提出物: AIとの対話ログ、完成したコードと結果、気づいたこと。
7-A-2
課題: irisデータセットを使って、「どの種(Species)が最もバランスの良い形をしているか」を定量的に評価してください。
「バランスの良い形」の定義は自分で考えてください(例: SepalとPetalの比率、LengthとWidthの比率、変動係数の小ささなど)。AIと相談しながら:
- 「バランスの良さ」の指標を定義する
- dplyrで集計する
- 結果を解釈する
提出物: AIとの対話内容、指標の定義と理由、集計コードと結果。
7-A-3
課題: BaseballDecade.csvから、「年俸と身体的特徴(身長・体重・BMI)の関係」を球団ごとに調べたいと思います。
AIに相談しながら、dplyrを駆使してこの分析を行ってください。データの絞り込み、変数の作成、グループごとの集計を組み合わせる必要があります。
考えるポイント:
- BMI変数の作成(
mutate()) - ポジション別の傾向(投手 vs 野手)
- 球団ごとの違い
- 年度による変化
提出物: - AIとの対話の記録(どのように問題を分解して伝えたか) - 完成したコード - 発見した傾向や気づき
まとめ
このユニットでは、dplyrパッケージの主要な関数を学びました:
select(): 必要な変数(列)だけを選ぶfilter(): 条件に合うケース(行)だけを残すmutate(): 新しい変数を作る、既存の変数を変換するgroup_by()+summarise(): グループごとに要約統計量を計算するacross(): 複数の変数に同じ処理を一括適用するarrange(): データを並べ替える
これらの関数をパイプ演算子(%>%)で繋ぐことで、データの選択・加工・集計を流れるように記述できます。dplyrは卒論のデータ分析で最も頻繁に使うパッケージです。繰り返し練習して、手に馴染ませましょう。
次のユニット: U8. tidyr: ワイド⇔ロング変換では、データの「持ち方」を変換する方法を学びます。
進捗: あなたは今 7-C-10 まで完了しました!(と仮定)次は 7-B-1 に進みましょう。