この記事は R言語 Advent Calendar 2025 の 15日目の記事です。
1. はじめに
データ分析をやっていると、2つのグループ間で指標に差があるときに、その差を引き起こしている要因を調べたい場合があります。例えば、あるオンラインショップの KPI が前年と比べて悪くなった場合を考えます。このとき、性別に対する深掘り分析を行い、KPI が悪化した原因が男性ユーザにあるのか女性ユーザにあるのか、あるいは性別は関係ないのかを調べたい場合があります。
KPI が売上のような量的指標だった場合は簡単です。男性の売上げと女性の売上の、どちらが下がったのかを調べればいいだけです。しかし、KPI がコンバージョン率のような割合指標のときには難しくなります。量的指標の場合とは異なり、男性グループにおける指標の変化と女性グループにおける指標の変化が、それぞれ全体の指標にどれくらい影響を及ぼすのかを単純に計算することができないからです。
この問題に対処するために、テセウスの船に着想を得たアプローチを提案します。このアプローチでは、比較したい2つのグループに対して、一方のグループのサブグループをもう一方のグループの同じサブグループに徐々に置き換えていき、各段階で指標を再計算します。各段階での指標の変化は、各サブグループが全体の指標の差にどの程度影響しているかとして解釈できます。
例えば、2024年には指標が6.2%で、2025年には5.2%に低下したとします。ここでも性別に注目します。2024年のデータセット内の男性データを2025年の男性データに置き換え、指標を再計算します。その結果、指標は0.8%ポイント低下し、5.4%になったとします。この場合、男性グループが全体の指標に及ぼした影響は-0.8%ポイントです。次に、2024年の女性データを2025年の女性データに置き換えます。すると、データセットは完全に2025年のデータとなり、指標は0.2%ポイント低下し、5.2%になります。したがって、女性グループの影響は-0.2%ポイントです。
これを可視化すると次のようになります。
このグラフから、指標の低下は主に男性グループによって引き起こされていることがわかります。このグラフを「テセウスプロット」と名付けました。
TheseusPlot パッケージは、さまざまな属性に対するテセウスプロットを簡単に作成できるパッケージとして開発されました。
2. インストール
TheseusPlot パッケージは CRAN からインストールできます。
install.packages("TheseusPlot")
3. 基本的な使い方
3.1 データの準備
テセウスプロットを作成するには、共通の列をもつ 2つのデータフレームが必要です。
ここでは、nycflights13 パッケージに含まれる 2013年のニューヨーク市のフライトデータを使用します。割合指標として、定刻到着率(到着予定時刻から15分以内に到着したフライトの割合)を考えます。2013年11月と比較して、2013年12月の定刻到着率は大幅に低下しました。テセウスプロットを用いてその原因を調査します。
まず、データフレームに各フライトが定刻通りに到着したかどうかを示す列 on_time を作成します。次に、11月と12月のフライトを別々のデータフレームとして抽出します。定刻到着率は、11月は 82% でしたが、12月は 67% に低下しました。
library(dplyr) library(nycflights13) data <- flights |> filter(!is.na(arr_delay)) |> mutate(on_time = arr_delay <= 15) |> # 到着予定時刻から15分以内に到着したか left_join(airlines, by = "carrier") |> mutate(carrier = name) |> # 航空会社を正式名称にする select(year, month, day, origin, dest, carrier, dep_delay, on_time) data |> head() #> # A tibble: 6 × 8 #> year month day origin dest carrier dep_delay on_time #> <int> <int> <int> <chr> <chr> <chr> <dbl> <lgl> #> 1 2013 1 1 EWR IAH United Air Lines Inc. 2 FALSE #> 2 2013 1 1 LGA IAH United Air Lines Inc. 4 FALSE #> 3 2013 1 1 JFK MIA American Airlines Inc. 2 FALSE #> 4 2013 1 1 JFK BQN JetBlue Airways -1 TRUE #> 5 2013 1 1 LGA ATL Delta Air Lines Inc. -6 TRUE #> 6 2013 1 1 EWR ORD United Air Lines Inc. -4 FALSE data_Nov <- data |> filter(month == 11) data_Dec <- data |> filter(month == 12) data_Nov |> summarise(on_time_rate = mean(on_time)) |> pull(on_time_rate) #> [1] 0.8264803 data_Dec |> summarise(on_time_rate = mean(on_time)) |> pull(on_time_rate) #> [1] 0.6738712
3.2 プロットとテーブル
準備した2つのデータフレームを使って、まずは ship オブジェクトを作成します。この ship オブジェクトは、テセウスプロットを作成するために設計された R6 クラスのインスタンスです。
library(TheseusPlot) ship <- create_ship(data_Nov, data_Dec, y = on_time, labels = c("November", "December"))
ship オブジェクトの plot メソッドに列名を渡すことで、テセウスプロットを作成できます。例えば、出発空港 (origin) に対するテセウスプロットを作成するには、次のようにします。
ship$plot(origin)
ニューヨーク市には3つの主要空港があり、定時到着率の低下に最も大きく影響したのはニューアーク・リバティー国際空港 (EWR) であることがわかります。
ここで、各出発空港のフライト数は重要です。フライト数が多いほど全体の指標への影響が大きくなることが予想されるためです。そのため、テセウスプロットでは各サブグループ内の各グループのデータサイズを棒グラフで表示します。これにより、各出発空港のフライト数はほぼ同程度であることがわかり、影響を直接比較できることがわかります。
つまり、テセウスプロットは次の 2 つの要素で構成されます。
- 各サブグループが指標の変化にどれくらい影響を与えたかを示すウォーターフォールプロット
- 各サブグループ内の各グループのサンプルサイズを表す棒グラフ
ship オブジェクトは、テセウスプロットで使用される数値をデータフレームで取得するメソッド table も提供します。
ship$table(origin) #> # A tibble: 3 × 8 #> origin contrib n1 n2 x1 x2 rate1 rate2 #> <chr> <dbl> <int> <int> <int> <int> <dbl> <dbl> #> 1 EWR -0.0719 9603 9410 7995 5910 0.833 0.628 #> 2 JFK -0.0502 8645 8923 7290 6142 0.843 0.688 #> 3 LGA -0.0305 8723 8687 7006 6156 0.803 0.709
3.3 プロットの反転
定刻到着率の低下に対する航空会社 (carrier) の影響を調べたいとします。しかし、データ中には航空会社は 16 社含まれるため、通常のテセウスプロットだと文字が重なって見づらくなってしまいます。サブグループが多い場合、plot_flip メソッドを使って x 軸と y 軸を入れ替えることで、見やすくすることができます。
ship$plot_flip(carrier)
サブグループの数が多い場合、影響の小さいサブグループは自動的に1つにまとめられます。デフォルトでは、サブグループの数が10を超えるとこの処理が行われます。これは引数 n で調整できます。
ship$plot_flip(carrier, n = 6)
このグラフから、定時到着率の低下に最も大きい影響を与えている航空会社は、JetBlue Airways と United Air Lines であることがわかります。
3.4 連続変数の離散化
テセウスプロットは原理的には連続変数に対して描くことはできません。しかし、割合指標に対する連続変数の影響を調査したいことはよくあります。そこで、ship$plot() メソッドでは、連続値の入った列が指定された場合に、自動的に離散化を行います。例えば、出発が何分遅れたか (dep_delay) に対してテセウスプロットを作成すると次のようになります。
ship$plot_flip(dep_delay)
デフォルトでは、連続変数は、ビンの数が 10 で、各ビンのサンプルサイズがほぼ等しくなるように離散化されます。引数 continuous に continuous_config() を渡すことで、離散化の設定を変更できます。
ship$plot_flip(dep_delay, continuous = continuous_config(n = 3))
このグラフから結果を読み取るには、サンプルサイズを表わす棒グラフまで注意深く見る必要があります。このグラフは、定刻前に出発したフライトが減少したことと、遅延して出発したフライトが増加したことの両方が、定刻到着率の低下の原因であることを示しています。
4. まとめ
この記事では、割合指標の変化要因を可視化するパッケージ TheseusPlot について紹介しました。より詳しくは公式ドキュメントをご参照ください。このパッケージはリリースしたばかりでまだ使いづらいところが多いかと思います。改善してほしいところや不具合などありましたら、GitHubやこのブログのコメント欄までお気軽にお知らせください。
Enjoy!
補足
サブグループを入れ替える順番で影響度は変わらないの?
ナイーブな方法だと変わります。なので、実際は Shapley 値の近似値を計算して影響度としています。
SHAP とは違うの?
違います。SHAP とは解いている問題が違うので、どちらかがどちらかの代わりになるということはありません。
PDP とは違うの?
PDP は同様の可視化が可能です。PDP と比較して利点と欠点があると考えています。特に、TheseusPlot はモデルを作成する必要がないため、十分な特徴量がない場合にも使えます。
| TheseusPlot | PDP | |
|---|---|---|
| モデルを作成する必要がない | ○ | × |
| 連続変数を直接扱える | × | ○ |
| 出力変数と強く相関した変数が扱える | ○ | × |





