Note: この記事は英語で書いた記事の日本語訳です.
データ処理の知見を効率的に共有するには,適切な関数を実装することが欠かせません.しかし,使いやすさとカスタマイズ性のバランスを取るのは容易ではありません.
このバランスを取るための一つのアプローチは,データ処理のフローを複数の関数に分割することです.このアプローチを効果的に機能させるには,ユーザーが全体のワークフローを明確に理解している必要があります.
プログラミングの利点の一つは,これまで人間が正確に理解しておく必要があった暗黙知を明示化できることだと私は考えています.この考え方が,Rの adverbial パッケージにおけるステップバイステップのデータ処理関数の実装のきっかけとなりました.
このパッケージが提供するステップバイステップのデータ処理機能は,主に次の3つの関数から構成されています.
-
step_by_step() は,ステップバイステップのデータ処理ワークフローを定義します.
-
as_step() は,関数をワークフロー内で使用できるステップに変換します.
-
end_step() は,ワークフロー内のステップを終了します.
これらの関数は,データ処理のための明確な枠組みを提供し,同様の構造を持つデータ分析タスクにおける知見の共有や共同作業を容易にします.
例
次のデータ処理を,ステップバイステップのデータ処理に変換してみましょう.この処理では,penguins データを使って,島・種・年ごとのペンギンの平均体重を算出します.
library(tidyverse)
as_tibble(penguins) |>
select(species, island, body_mass, year) |>
filter(!is.na(body_mass)) |>
mutate(
# body_massをkgに変換
body_mass = units::set_units(body_mass, g) |>
units::set_units("kg")
) |>
summarise(
mean_body_mass = mean(body_mass),
.by = c(island, species, year)
) |>
arrange(island, species, year)
# A tibble: 15 × 4
island species year mean_body_mass
<fct> <fct> <int> [kg]
1 Biscoe Adelie 2007 3.62
2 Biscoe Adelie 2008 3.63
3 Biscoe Adelie 2009 3.86
4 Biscoe Gentoo 2007 5.07
5 Biscoe Gentoo 2008 5.02
6 Biscoe Gentoo 2009 5.14
7 Dream Adelie 2007 3.67
8 Dream Adelie 2008 3.76
9 Dream Adelie 2009 3.65
10 Dream Chinstrap 2007 3.69
11 Dream Chinstrap 2008 3.8
12 Dream Chinstrap 2009 3.72
13 Torgersen Adelie 2007 3.76
14 Torgersen Adelie 2008 3.86
15 Torgersen Adelie 2009 3.49
ステップバイステップのデータ処理ワークフローを定義する
この処理は,次のステップに分解できます.
-
select() でデータフレームから列を選択する.
-
filter() でデータフレームから行を絞り込む.
-
mutate() でデータフレームの列を変換する.
-
summarise() でデータフレームを集計する.
-
arrange() でデータフレームの行を並べ替える.
このように,adverbial パッケージを使うと,以下のようにステップバイステップのデータ処理ワークフローを定義できます.これで data_wrangler() を使って,データをステップバイステップで処理できるようになります.データ処理に関わるステップがヘッダーに表示されるため,ワークフローの理解やカスタマイズが容易になります.
library(adverbial)
data_wrangler <- step_by_step(
c(
select_step = "データフレームから列を選択",
filter_step = "データフレームから行を絞り込み",
mutate_step = "データフレームの列を変換",
summarise_step = "データフレームを集計",
arrange_step = "データフレームの行を並べ替え"
)
)
select_step <- as_step(select, "select_step")
filter_step <- as_step(filter, "filter_step")
mutate_step <- as_step(mutate, "mutate_step")
summarise_step <- as_step(summarise, "summarise_step")
arrange_step <- as_step(arrange, "arrange_step")
data_wrangler(as_tibble(penguins))
# Steps:
# ☐ 1. select_step: データフレームから列を選択
# ☐ 2. filter_step: データフレームから行を絞り込み
# ☐ 3. mutate_step: データフレームの列を変換
# ☐ 4. summarise_step: データフレームを集計
# ☐ 5. arrange_step: データフレームの行を並べ替え
# ℹ Please call `select_step()` to continue.
#
# A tibble: 344 × 8
species island bill_len bill_dep flipper_len body_mass sex year
* <fct> <fct> <dbl> <dbl> <int> <int> <fct> <int>
1 Adelie Torgersen 39.1 18.7 181 3750 male 2007
2 Adelie Torgersen 39.5 17.4 186 3800 female 2007
3 Adelie Torgersen 40.3 18 195 3250 female 2007
4 Adelie Torgersen NA NA NA NA <NA> 2007
5 Adelie Torgersen 36.7 19.3 193 3450 female 2007
6 Adelie Torgersen 39.3 20.6 190 3650 male 2007
7 Adelie Torgersen 38.9 17.8 181 3625 female 2007
8 Adelie Torgersen 39.2 19.6 195 4675 male 2007
9 Adelie Torgersen 34.1 18.1 193 3475 <NA> 2007
10 Adelie Torgersen 42 20.2 190 4250 <NA> 2007
# ℹ 334 more rows
データをステップバイステップで処理する
次のように,データをステップバイステップで処理できます(ステップ3まで).as_step() 関数を使えば,任意の関数をワークフロー内で使用できるステップに変換することもできます.誤ったステップ名を指定すると,エラーが発生します.
data_wrangler(as_tibble(penguins)) |>
select_step(species, island, body_mass, year) |>
filter_step(!is.na(body_mass)) |>
mutate_step(
# body_massをkgに変換
body_mass = units::set_units(body_mass, g) |>
units::set_units("kg")
) |>
# ステップバイステップの処理中に別の関数を使うこともできます
as_step(head)(n = 3)
# Steps:
# ☒ 1. select_step: データフレームから列を選択
# ☒ 2. filter_step: データフレームから行を絞り込み
# ☒ 3. mutate_step: データフレームの列を変換
# ☐ 4. summarise_step: データフレームを集計
# ☐ 5. arrange_step: データフレームの行を並べ替え
# ℹ Please call `summarise_step()` to continue.
#
# A tibble: 3 × 4
species island body_mass year
* <fct> <fct> [kg] <int>
1 Adelie Torgersen 3.75 2007
2 Adelie Torgersen 3.8 2007
3 Adelie Torgersen 3.25 2007
データ処理を完了する
残りのステップを追加し,end_step() でワークフローを終了することで,データ処理を完了できます.以下のコードは元のコードとほぼ同じ処理を行いますが,次に適用すべき関数を順次ユーザーに知らせる点だけが異なります.
data_wrangler(as_tibble(penguins)) |>
select_step(species, island, body_mass, year) |>
filter_step(!is.na(body_mass)) |>
mutate_step(
# body_massをkgに変換
body_mass = units::set_units(body_mass, g) |>
units::set_units("kg")
) |>
summarise_step(
mean_body_mass = mean(body_mass),
.by = c(island, species, year)
) |>
arrange_step(island, species, year) |>
end_step()
# A tibble: 15 × 4
island species year mean_body_mass
* <fct> <fct> <int> [kg]
1 Biscoe Adelie 2007 3.62
2 Biscoe Adelie 2008 3.63
3 Biscoe Adelie 2009 3.86
4 Biscoe Gentoo 2007 5.07
5 Biscoe Gentoo 2008 5.02
6 Biscoe Gentoo 2009 5.14
7 Dream Adelie 2007 3.67
8 Dream Adelie 2008 3.76
9 Dream Adelie 2009 3.65
10 Dream Chinstrap 2007 3.69
11 Dream Chinstrap 2008 3.8
12 Dream Chinstrap 2009 3.72
13 Torgersen Adelie 2007 3.76
14 Torgersen Adelie 2008 3.86
15 Torgersen Adelie 2009 3.49
まとめ
adverbial パッケージは,データ処理タスクをより小さく管理しやすいステップに分解する手段を提供します.このアプローチにより,ユーザーは全体のワークフローを理解し,自分のニーズに合わせて調整できるようになります.データ処理の各ステップを頭の中で覚えておくという,労力のかかる作業を作業者に強いる必要をなくすことが目的です.