Rのadverbial::step_by_step()でデータ処理を導く

adverbial
R
公開

2025年5月25日

Note: この記事は英語で書いた記事の日本語訳です.

データ処理の知見を効率的に共有するには,適切な関数を実装することが欠かせません.しかし,使いやすさとカスタマイズ性のバランスを取るのは容易ではありません.

このバランスを取るための一つのアプローチは,データ処理のフローを複数の関数に分割することです.このアプローチを効果的に機能させるには,ユーザーが全体のワークフローを明確に理解している必要があります.

プログラミングの利点の一つは,これまで人間が正確に理解しておく必要があった暗黙知を明示化できることだと私は考えています.この考え方が,Rの adverbial パッケージにおけるステップバイステップのデータ処理関数の実装のきっかけとなりました.

このパッケージが提供するステップバイステップのデータ処理機能は,主に次の3つの関数から構成されています.

  1. step_by_step() は,ステップバイステップのデータ処理ワークフローを定義します.
  2. as_step() は,関数をワークフロー内で使用できるステップに変換します.
  3. 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

ステップバイステップのデータ処理ワークフローを定義する

この処理は,次のステップに分解できます.

  1. select() でデータフレームから列を選択する.
  2. filter() でデータフレームから行を絞り込む.
  3. mutate() でデータフレームの列を変換する.
  4. summarise() でデータフレームを集計する.
  5. 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 パッケージは,データ処理タスクをより小さく管理しやすいステップに分解する手段を提供します.このアプローチにより,ユーザーは全体のワークフローを理解し,自分のニーズに合わせて調整できるようになります.データ処理の各ステップを頭の中で覚えておくという,労力のかかる作業を作業者に強いる必要をなくすことが目的です.