UPDATE: 2022-10-28 19:52:55
funneljoinパッケージについて、パッケージのGithubと開発者のブログで紹介されている内容、使い方をまとめておく。funneljoinパッケージの目的は、人がどのように動いたのか、何を行ったのか、という行動ファネルを分析するのを手助けするためのパッケージ。下記を参考にしている。今度のOsakaRのネタにしようかどうか悩み中。
# library(remotes)
# install_github("robinsones/funneljoin")
library(tidyverse)
library(funneljoin)
パッケージのGithubにある下記の訪問履歴と登録履歴のデータをお借りして説明する。
<- landed %>%
landed rename(landed_at = timestamp, user_id_x = user_id)
<- registered %>%
registered rename(registered_at = timestamp, user_id_y = user_id)
list(
landed,
registered )
## [[1]]
## # A tibble: 9 × 2
## user_id_x landed_at
## <dbl> <date>
## 1 1 2018-07-01
## 2 2 2018-07-01
## 3 3 2018-07-02
## 4 4 2018-07-01
## 5 4 2018-07-04
## 6 5 2018-07-10
## 7 5 2018-07-12
## 8 6 2018-07-07
## 9 6 2018-07-08
##
## [[2]]
## # A tibble: 8 × 2
## user_id_y registered_at
## <dbl> <date>
## 1 1 2018-07-02
## 2 3 2018-07-02
## 3 4 2018-06-10
## 4 4 2018-07-02
## 5 5 2018-07-11
## 6 6 2018-07-10
## 7 6 2018-07-11
## 8 7 2018-07-07
dplyrパッケージのjoin関数と同じでafter_left_join()
やafter_inner_join()
などがあり、type
を変更することで紐付け方を変更する。引数は下記の通り。
引数 | 内容 |
---|---|
by_time |
各テーブルの時間カラムを指定。datetime型のまたはdate型のカラム。時間yが時間xの後、または時間xと同じかどうかなど、フィルタリングするために使用される。 |
by_user |
各テーブルのユーザまたはID列を指定。一致する行のペアは同一でなければならない。 |
type |
“first-first”、“last-first”、“any-firstafter”など、イベントペアを区別するために使用されるファネルのタイプを指定。 |
suffix |
dplyrのjoin関数と同様に、両方のテーブルにあるカラム名にサフィックスを指定。 |
type
は下記を指定できる。基本的には先なのか、後なのか、全部なのかなどをfirst
、last
、any
で指定する。下記のタイプが一般的。
first-first
:
参加前の各ユーザーの最も古いx
とy
を紐付ける。例えば、実験があったとして、最初の「参加」以降に、最初に「登録」した時を取得したい場合はこのタイプを利用する。この場合、登録して、実験に参加し、再び登録した場合は紐付かない。first-firstafter
:
これは参加前の各ユーザーの最も古いx
とy
を紐付ける。例えば、実験があったとして、最初の「参加」以降に、最初に「登録」した時を取得したい場合はこのタイプを利用する。lastbefore-firstafter
:
例えば、ラストクリック型の広告アトリビューションなんかで役に立つ。最初のCVの前で、最後にクリックした広告が必要なとき。any-firstafter
:
すべてのx
以降で最初のy
が続くものを取る。例えば、誰かがホームページを訪問した回数と、その後に訪問した最初の製品ページをすべて取得したい場合など。any-any
:
すべてのx
以降ですべてのy
が続くものを取る。例えば、誰かがホームページを訪問した回数と、その後に見たすべての製品ページを表示する。文字でみてもよくわからんので、上記の5タイプを実際に動かしていく。理解をすすめるために、私が作った画像をアップしているが、作った過程で誤りがあればごめんなさい。画像のタイトルにタイプを表記しているが、意図的に括弧をつけている。これは、lastbefore-firstafter
なんかはbefore
が前のアクションのデータで、after
がアクション後のデータと考えると、「アクション前の最後とアクション後の最初」を紐付けるタイプと解釈できるので、このようにしており、実際のタイプに無いものは空括弧にしている。
また、挙動を確認するために、簡単なサンプルのデータを作っている。
<- tibble::tibble(user_id_x = 1,
df_x landed_at = seq(as.Date("2020-05-05"), by = "day", length.out = 2)
)
<- tibble::tibble(user_id_y = 1,
df_y registered_at = seq(as.Date("2020-05-01"), by = "day", length.out = 9)
)
list(df_x,df_y)
## [[1]]
## # A tibble: 2 × 2
## user_id_x landed_at
## <dbl> <date>
## 1 1 2020-05-05
## 2 1 2020-05-06
##
## [[2]]
## # A tibble: 9 × 2
## user_id_y registered_at
## <dbl> <date>
## 1 1 2020-05-01
## 2 1 2020-05-02
## 3 1 2020-05-03
## 4 1 2020-05-04
## 5 1 2020-05-05
## 6 1 2020-05-06
## 7 1 2020-05-07
## 8 1 2020-05-08
## 9 1 2020-05-09
first-first
おさらいすると、これは参加前の各ユーザーの最も古いx
とy
を紐付ける。例えば、実験があったとして、最初の「参加」以降に、最初に「登録」した時を取得したい場合はこのタイプを利用する。この場合、登録して、実験に参加し、再び登録した場合は紐付かない、というもの。
%>%
landed after_left_join(registered,
by_user = c("user_id_x" = "user_id_y"),
by_time = c("landed_at" = "registered_at"),
type = "first-first") %>%
arrange(user_id_x)
## # A tibble: 6 × 3
## user_id_x landed_at registered_at
## <dbl> <date> <date>
## 1 1 2018-07-01 2018-07-02
## 2 2 2018-07-01 NA
## 3 3 2018-07-02 2018-07-02
## 4 4 2018-07-01 NA
## 5 5 2018-07-10 2018-07-11
## 6 6 2018-07-07 2018-07-10
サンプルデータで確認してみる。
%>%
df_x after_left_join(df_y,
by_user = c("user_id_x" = "user_id_y"),
by_time = c("landed_at" = "registered_at"),
type = "first-first")
## # A tibble: 1 × 3
## user_id_x landed_at registered_at
## <dbl> <date> <date>
## 1 1 2020-05-05 NA
first-firstafter
おさらいすると、これは参加前の各ユーザーの最も古いx
とy
を紐付ける。例えば、実験があったとして、最初の「参加」以降に、最初に「登録」した時を取得したい場合はこのタイプを利用する、というもの。
%>%
landed after_left_join(registered,
by_user = c("user_id_x" = "user_id_y"),
by_time = c("landed_at" = "registered_at"),
type = "first-firstafter") %>%
arrange(user_id_x)
## # A tibble: 6 × 3
## user_id_x landed_at registered_at
## <dbl> <date> <date>
## 1 1 2018-07-01 2018-07-02
## 2 2 2018-07-01 NA
## 3 3 2018-07-02 2018-07-02
## 4 4 2018-07-01 2018-07-02
## 5 5 2018-07-10 2018-07-11
## 6 6 2018-07-07 2018-07-10
サンプルデータで確認してみる。
%>%
df_x after_left_join(df_y,
by_user = c("user_id_x" = "user_id_y"),
by_time = c("landed_at" = "registered_at"),
type = "first-firstafter")
## # A tibble: 1 × 3
## user_id_x landed_at registered_at
## <dbl> <date> <date>
## 1 1 2020-05-05 2020-05-05
lastbefore-firstafter
おさらいすると、これは、例えば、ラストクリック型の広告アトリビューションなんかで役に立つ。最初のCVの前で、最後にクリックした広告が必要なとき、というもの。
%>%
landed after_left_join(registered,
by_user = c("user_id_x" = "user_id_y"),
by_time = c("landed_at" = "registered_at"),
type = "lastbefore-firstafter") %>%
arrange(user_id_x)
## # A tibble: 9 × 3
## user_id_x landed_at registered_at
## <dbl> <date> <date>
## 1 1 2018-07-01 2018-07-02
## 2 2 2018-07-01 NA
## 3 3 2018-07-02 2018-07-02
## 4 4 2018-07-01 2018-07-02
## 5 4 2018-07-04 NA
## 6 5 2018-07-10 2018-07-11
## 7 5 2018-07-12 NA
## 8 6 2018-07-07 NA
## 9 6 2018-07-08 2018-07-10
サンプルデータで確認してみる。このような5日と6日の場合、6日のみ6日と紐づくと思ったが、5日と6日の間に5日があるので、5日と5日が紐づく。
%>%
df_x after_left_join(df_y,
by_user = c("user_id_x" = "user_id_y"),
by_time = c("landed_at" = "registered_at"),
type = "lastbefore-firstafter")
## # A tibble: 2 × 3
## user_id_x landed_at registered_at
## <dbl> <date> <date>
## 1 1 2020-05-05 2020-05-05
## 2 1 2020-05-06 2020-05-06
パッケージののサンプルデータでidが6だけで検証してみると、思ったとおりに動く。
<- tibble::tibble(user_id_x = 1,
df_x2 landed_at = as.Date(c("2020-07-07", "2020-07-8"))
)
<- tibble::tibble(user_id_y = 1,
df_y2 registered_at = as.Date(c("2020-07-07", "2020-07-10")))
%>%
df_x2 after_left_join(df_y2,
by_user = c("user_id_x" = "user_id_y"),
by_time = c("landed_at" = "registered_at"),
type = "lastbefore-firstafter")
## # A tibble: 2 × 3
## user_id_x landed_at registered_at
## <dbl> <date> <date>
## 1 1 2020-07-07 2020-07-07
## 2 1 2020-07-08 2020-07-10
# 8日だと7日は紐付かず、8日が8日と紐づく。
<- tibble::tibble(user_id_x = 1,
df_x3 landed_at = as.Date(c("2020-07-07", "2020-07-8"))
)<- tibble::tibble(user_id_y = 1,
df_y3 registered_at = as.Date(c("2020-07-08", "2020-07-10")))
%>%
df_x3 after_left_join(df_y3,
by_user = c("user_id_x" = "user_id_y"),
by_time = c("landed_at" = "registered_at"),
type = "lastbefore-firstafter")
## # A tibble: 2 × 3
## user_id_x landed_at registered_at
## <dbl> <date> <date>
## 1 1 2020-07-07 NA
## 2 1 2020-07-08 2020-07-08
any-firstafter
おさらいすると、これは、すべてのx
以降で最初のy
が続くものを取る。例えば、誰かがホームページを訪問した回数と、その後に訪問した最初の製品ページをすべて取得したい場合などに役立つ、というもの。
%>%
landed after_left_join(registered,
by_user = c("user_id_x" = "user_id_y"),
by_time = c("landed_at" = "registered_at"),
type = "any-firstafter") %>%
arrange(user_id_x)
## # A tibble: 9 × 3
## user_id_x landed_at registered_at
## <dbl> <date> <date>
## 1 1 2018-07-01 2018-07-02
## 2 2 2018-07-01 NA
## 3 3 2018-07-02 2018-07-02
## 4 4 2018-07-01 2018-07-02
## 5 4 2018-07-04 NA
## 6 5 2018-07-10 2018-07-11
## 7 5 2018-07-12 NA
## 8 6 2018-07-07 2018-07-10
## 9 6 2018-07-08 2018-07-10
サンプルデータで確認してみる。
%>%
df_x after_left_join(df_y,
by_user = c("user_id_x" = "user_id_y"),
by_time = c("landed_at" = "registered_at"),
type = "any-firstafter")
## # A tibble: 2 × 3
## user_id_x landed_at registered_at
## <dbl> <date> <date>
## 1 1 2020-05-05 2020-05-05
## 2 1 2020-05-06 2020-05-06
any-any
おさらいすると、これはすべてのx
以降ですべてのy
が続くものを取る。例えば、誰かがホームページを訪問した回数と、その後に見たすべての製品ページを表示する、というもの。
%>%
landed after_left_join(registered,
by_user = c("user_id_x" = "user_id_y"),
by_time = c("landed_at" = "registered_at"),
type = "any-any") %>%
arrange(user_id_x)
## # A tibble: 11 × 3
## user_id_x landed_at registered_at
## <dbl> <date> <date>
## 1 1 2018-07-01 2018-07-02
## 2 2 2018-07-01 NA
## 3 3 2018-07-02 2018-07-02
## 4 4 2018-07-01 2018-07-02
## 5 4 2018-07-04 NA
## 6 5 2018-07-10 2018-07-11
## 7 5 2018-07-12 NA
## 8 6 2018-07-07 2018-07-10
## 9 6 2018-07-07 2018-07-11
## 10 6 2018-07-08 2018-07-10
## 11 6 2018-07-08 2018-07-11
サンプルデータで確認してみる。
%>%
df_x after_left_join(df_y,
by_user = c("user_id_x" = "user_id_y"),
by_time = c("landed_at" = "registered_at"),
type = "any-any")
## # A tibble: 9 × 3
## user_id_x landed_at registered_at
## <dbl> <date> <date>
## 1 1 2020-05-05 2020-05-05
## 2 1 2020-05-05 2020-05-06
## 3 1 2020-05-05 2020-05-07
## 4 1 2020-05-05 2020-05-08
## 5 1 2020-05-05 2020-05-09
## 6 1 2020-05-06 2020-05-06
## 7 1 2020-05-06 2020-05-07
## 8 1 2020-05-06 2020-05-08
## 9 1 2020-05-06 2020-05-09
funneljoinパッケージにはjoin関数以外にもファネル分析に特化した関数がいくつかある。サンプルデータは下記の通り。
<- tibble::tribble(
activity ~ "user_id", ~ "event", ~ "timestamp",
1, "landing", "2019-07-01",
1, "registration", "2019-07-02",
1, "purchase", "2019-07-07",
1, "purchase", "2019-07-10",
2, "landing", "2019-08-01",
2, "registration", "2019-08-15",
3, "landing", "2019-05-01",
3, "registration", "2019-06-01",
3, "purchase", "2019-06-04",
4, "landing", "2019-06-13"
)
activity
## # A tibble: 10 × 3
## user_id event timestamp
## <dbl> <chr> <chr>
## 1 1 landing 2019-07-01
## 2 1 registration 2019-07-02
## 3 1 purchase 2019-07-07
## 4 1 purchase 2019-07-10
## 5 2 landing 2019-08-01
## 6 2 registration 2019-08-15
## 7 3 landing 2019-05-01
## 8 3 registration 2019-06-01
## 9 3 purchase 2019-06-04
## 10 4 landing 2019-06-13
funnel_start()
は下記の引数をとる。
引数 | 内容 |
---|---|
tbl |
イベントのテーブル |
moment_type |
ファネルにおける最初のイベント |
moment |
moment_typeを示すカラム名 |
tstamp |
モーメントのタイムスタンプを持つカラムの名前 |
user |
そのモーメントを行ったユーザーを示すカラムの名前。 |
funnel_start()
は、user_ids
とtimestamp_{evrnt}
を持つテーブルを返す。
%>%
activity funnel_start(moment_type = "landing",
moment = "event",
tstamp = "timestamp",
user = "user_id")
## # A tibble: 4 × 2
## user_id timestamp_landing
## <dbl> <chr>
## 1 1 2019-07-01
## 2 2 2019-08-01
## 3 3 2019-05-01
## 4 4 2019-06-13
ファネルに更にモーメントを追加するには、funnel_step()
を使う。funnel_start()
で各パートに使用するカラムを指定したので、必要なのはmoment_type
とafter_join()
のタイプ。after_join()
を理解していると、わかり良い。1つのテーブルを条件ごとにテーブルを内部的に分け、after_join()
で結合する。
%>%
activity funnel_start(moment_type = "landing",
moment = "event",
tstamp = "timestamp",
user = "user_id") %>%
funnel_step(moment_type = "registration",
type = "first-firstafter")
## # A tibble: 4 × 3
## user_id timestamp_landing timestamp_registration
## <dbl> <chr> <chr>
## 1 3 2019-05-01 2019-06-01
## 2 4 2019-06-13 <NA>
## 3 1 2019-07-01 2019-07-02
## 4 2 2019-08-01 2019-08-15
ファネルに更にモーメントを追加するには、再度funnel_step()
を使う。
%>%
activity funnel_start(moment_type = "landing",
moment = "event",
tstamp = "timestamp",
user = "user_id") %>%
funnel_step(moment_type = "registration",
type = "first-firstafter") %>%
funnel_step(moment_type = "purchase",
type = "first-any")
## # A tibble: 5 × 4
## user_id timestamp_landing timestamp_registration timestamp_purchase
## <dbl> <chr> <chr> <chr>
## 1 3 2019-05-01 2019-06-01 2019-06-04
## 2 1 2019-07-01 2019-07-02 2019-07-07
## 3 1 2019-07-01 2019-07-02 2019-07-10
## 4 2 2019-08-01 2019-08-15 <NA>
## 5 4 2019-06-13 <NA> <NA>
funnel_step()
はモーメントをまとめて指定でき、summarize_funnel()
を使うことでファネル分析の結果が得られる。最後に、summaryize_funnel()
を使って、ファネルの各次のステップに何人の人が通過したのか、何%の人が通過したのかを理解することが可能。funnel_steps()
に切り替えてコードを少し短くすることもできる。各ステップのtype
を順に与える。
%>%
activity funnel_start(moment_type = "landing",
moment = "event",
tstamp = "timestamp",
user = "user_id") %>%
funnel_steps(moment_types = c("registration", "purchase"),
type = "first-firstafter") %>%
summarize_funnel()
## # A tibble: 3 × 4
## moment_type nb_step pct_cumulative pct_step
## <fct> <int> <dbl> <dbl>
## 1 landing 4 1 NA
## 2 registration 3 0.75 0.75
## 3 purchase 2 0.5 0.667
また、first-any
のように、ユーザーに対して1つのタイプの複数のモーメントを紐付けるタイプを使用した場合、より多くの行をユーザーごとに取得することが可能。例えば、ユーザー1は2回の購入をしているので、2行を持つことになる。timestamp_landing
とtimestamp_registration
はどちらの行も同じで、異なるtimestamp_purchase
を持つ。のべカウントか、ユニークカウントの違いに似ている。
%>%
activity funnel_start(moment_type = "landing",
moment = "event",
tstamp = "timestamp",
user = "user_id") %>%
funnel_steps(moment_types = c("registration", "purchase"),
type = "first-any")
## # A tibble: 5 × 4
## user_id timestamp_landing timestamp_registration timestamp_purchase
## <dbl> <chr> <chr> <chr>
## 1 3 2019-05-01 2019-06-01 2019-06-04
## 2 1 2019-07-01 2019-07-02 2019-07-07
## 3 1 2019-07-01 2019-07-02 2019-07-10
## 4 2 2019-08-01 2019-08-15 <NA>
## 5 4 2019-06-13 <NA> <NA>
%>%
activity funnel_start(moment_type = "landing",
moment = "event",
tstamp = "timestamp",
user = "user_id") %>%
funnel_steps(moment_types = c("registration", "purchase"),
type = "first-firstafter")
## # A tibble: 4 × 4
## user_id timestamp_landing timestamp_registration timestamp_purchase
## <dbl> <chr> <chr> <chr>
## 1 3 2019-05-01 2019-06-01 2019-06-04
## 2 1 2019-07-01 2019-07-02 2019-07-07
## 3 2 2019-08-01 2019-08-15 <NA>
## 4 4 2019-06-13 <NA> <NA>
以上で、funneljoinパッケージのまとめは終わり。ファネル分析に特化したパッケージではあるが、使い方によっては、時間経過をもつ履歴データであれば、前処理の部分で役立てることができるだし、fuzzyjoinパッケージを使って、不等号を使ってコネコネ結合していたものが、この関数で代替できる部分もあるかもしれない。