GA4において「エンゲージメント」という概念が取り入れられた。この「エンゲージメント」は一般的な用語としても使われ、GA4の指標の中にも「エンゲージメント」といった用語が付くものが多く、それぞれの「エンゲージメント」の付く指標の違いを理解しておく必要がある。また、この理解がないとコンテンツ単位のエンゲージメント分析をする際に間違った解釈をしてしまうこともあるため、先に「エンゲージメント」とは?の解説から入る必要がある。ここでいう「ページ」とは「コンテンツ」のことを指し、両者を同じ意味として扱う。
「エンゲージメント」の整理
GA4における「エンゲージメント」とは、時間のエンゲージメントと、肯定的なアクションについてのエンゲージメントに分けると理解しやすくなる。前者は「ユーザーエンゲージメント」と呼ばれる指標で、探索における説明では「サイトまたはアプリがユーザーのデバイスのフォアグラウンドで動作している時間の合計(秒)です」と表記される時間の値である。後者は肯定的なアクションのエンゲージメントのいずれかが発生した場合に、セッション単位で「エンゲージメントのあったセッション数」とカウントされる指標となる。肯定的なアクションとは「10秒を超えたサイト閲覧」「コンバージョン イベントが発生」「2回以上のページビューの発生」の3つを指す。
ざっくりまとめてしまうと、「ユーザーエンゲージメント」という時間的概念と、いずれかの肯定的なアクションを起こしたエンゲージメントを含むセッションが「エンゲージメントのあったセッション数」としてカウントされる指標となる。この両者は別物であり、この2つを2軸で整理すると理解がしやすくなる。
ページにエンゲージメントを適用することは可能か?
GA4の「探索」で「エンゲージのあったセッション数」や「エンゲージメント率」が選択できるため、これにディメンションに「ページ ロケーション」を選択すれば、ページごとのエンゲージメント率を出せるかと思われるかもしれないが、これはできない。
間違った「エンゲージメントの高いコンテンツの分析」
一見すると、ページごとのエンゲージメント率を正しく可視化できているように見えるかもしれないが、これは意図した結果とはなっていない。なぜか?
求めたい結果を得るためにはスコープの範囲を合わせる必要があるが、このケースでは「ページ ロケーション」のページに対して、セッション単位で計測される「エンゲージのあったセッション数」を掛け合わせているため、正しい計算ができていない。「エンゲージのあったセッション数」は、session_startイベントのsession_engagedパラメータの数値を集計したものであり、エンゲージメントのあったセッションでは、後続のイベントは「10秒以上の閲覧」の有無に関わらずsession_engagedパラメータには1が付く。そのため、ディメンションのページロケーションとエンゲージのあったセッション数の指標を掛け合わせてもページ単位のエンゲージメント率は算出できないし、そもそも、探索上でそのような組み合わせは表示・可視化できない。
また、GA4におけるエンゲージメントの条件である「CVの発生」と「2ページ目の遷移」もセッション単位での集計で成り立っているため、これをページと組み合わせることはできない。論理的に破綻する。
結果、探索においてコンテンツ単位・ページ単位のエンゲージメント率を算出することはできない。
BigQueryを活用したページ単位のエンゲージメント分析
BigQueryを活用することでページスコープの集計が可能になるため、ページ単位(コンテンツ)の分析が可能になる。また、BigQueryを活用することで「エンゲージメント」の条件もカスタマイズすることも可能になる。というよりも、ページ単位の分析では、そのままセッション単位である「エンゲージメント」の条件を当てはめることはできないため、カスタマイズする必要がある。
ページ単位「30秒以上の閲覧」でエンゲージメントとするクエリ
CREATE TEMP FUNCTION date_from() RETURNS STRING AS ('20240401');
WITH
ENG_Time AS (
SELECT
ymd,
page_location,
SUM(_flag_eng) AS _flag_eng,
SUM(_cnt_record) AS _cnt_record,
SUM(_flag_eng) / SUM(_cnt_record) AS rate_eng
FROM (
SELECT
ymd,
page_location,
batch_page_id,
SUM(FLOOR(SAFE_DIVIDE(COALESCE(_engagement_time_msec, 0), 1000))) AS _engagement_time_seconds,
IF(SUM(FLOOR(SAFE_DIVIDE(COALESCE(_engagement_time_msec, 0), 1000))) > 30, 1,0) AS _flag_eng,
1 AS _cnt_record
FROM (
SELECT
PARSE_DATE("%Y%m%d", event_date) AS ymd,
REGEXP_REPLACE((SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'page_location'), r"\?.*", "") AS page_location,
(SELECT value.int_value FROM UNNEST(event_params) WHERE key = 'batch_page_id') AS batch_page_id,
(SELECT value.int_value FROM UNNEST(event_params) WHERE key = 'engagement_time_msec') AS _engagement_time_msec
FROM
`<project>.<dataset>.events_*`
WHERE
_TABLE_SUFFIX BETWEEN date_from() AND FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 1 DAY))
)
GROUP BY
ymd,
page_location,
batch_page_id
)
GROUP BY
ymd,
page_location
)
SELECT
*
FROM
ENG_Time
ORDER BY
ymd ASC
クエリ解説
GA4のスコープには、「ユーザー」「セッション」「イベント」とあるが、このスコープを使ってページ単位の分析をすることはできない。ページ単位の分析とは、そのページに滞在しているそのスコープの中で分析する必要があり、「セッション」スコープを採用するとページ間の遷移まで含まれてしまうからだ。その問題を解決するために、他のスコープを採用する必要があり、それが「batch_page_id」となる。
BigQueryにエクスポートされるGA4のパラメータに「batch_page_id」が23年の11月頃に追加され、どのようなパラメータなのか情報が少なかったため調査した内容をメモとして残す。 batch_page_idとは GA4の公式ヘルプに記載がまだ見当たらなかったが、BigQueryにエクス...
IF(SUM(FLOOR(SAFE_DIVIDE(COALESCE(_engagement_time_msec, 0), 1000))) > 30, 1,0) AS _flag_eng
エンゲージメントとする閲覧時間の調整は上記の「30」秒の箇所の数値を変更することで、他の時間へと変更可能。
ページ単位「30秒閲覧」+「スクロールイベント」でエンゲージメントとするクエリ
ページ閲覧の中で、「30秒以上の滞在」+デフォルト指標である90%到達した「スクロール」イベントの2つを満たした際にエンゲージメントとする条件も「batch_page_id」を活用することで集計可能になる。
CREATE TEMP FUNCTION date_from() RETURNS STRING AS ('20240301');
WITH
Common_Table AS (
SELECT
ymd,
page_location,
event_name,
batch_page_id,
_engagement_time_msec
FROM (
SELECT
PARSE_DATE("%Y%m%d", event_date) AS ymd,
REGEXP_REPLACE((SELECT value.string_value FROM UNNEST(event_params) WHERE key = 'page_location'), r"\?.*", "") AS page_location,
event_name,
(SELECT value.int_value FROM UNNEST(event_params) WHERE key = 'batch_page_id') AS batch_page_id,
(SELECT value.int_value FROM UNNEST(event_params) WHERE key = 'engagement_time_msec') AS _engagement_time_msec
FROM
`<project>.<dataset>.events_*`
WHERE
_TABLE_SUFFIX BETWEEN date_from() AND FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 1 DAY))
)
WHERE
page_location IS NOT NULL --'page_location'がNULLを出力するカスタムイベントを定義している場合除外が必要
),
ENG_Time_01 AS (
SELECT
ymd,
page_location,
batch_page_id,
SUM(FLOOR(SAFE_DIVIDE(COALESCE(_engagement_time_msec, 0), 1000))) AS _engagement_time_seconds, --検証用
IF(SUM(FLOOR(SAFE_DIVIDE(COALESCE(_engagement_time_msec, 0), 1000))) > 30, 1,0) AS _flag_eng,
1 AS _cnt_record
FROM (
SELECT
*
FROM
Common_Table
)
GROUP BY
ymd,
page_location,
batch_page_id
),
ENG_scroll_01 AS (
SELECT
ymd,
page_location,
batch_page_id,
SUM(_flag_scroll) AS _flag_scroll
FROM (
SELECT
*,
IF(event_name = 'scroll',1,0) AS _flag_scroll
FROM
Common_Table
)
GROUP BY
ymd,
page_location,
batch_page_id
),
JOIN_Time_Scroll AS (
SELECT
t.ymd,
t.page_location,
t.batch_page_id,
t._flag_eng,
s._flag_scroll,
IF(t._flag_eng = 1 AND s._flag_scroll = 1,1, 0) AS _flag_total,
t._cnt_record
FROM
ENG_Time_01 AS t
LEFT JOIN
ENG_scroll_01 AS s
ON
t.ymd = s.ymd
AND
t.page_location = s.page_location
AND
t.batch_page_id = s.batch_page_id
),
Master_01 AS (
SELECT
ymd,
page_location,
SUM(_flag_eng) AS _flag_eng,
SUM(_flag_scroll) AS _flag_scroll,
SUM(_flag_total) AS _flag_total,
SUM(_cnt_record) AS _cnt_record
FROM
JOIN_Time_Scroll
GROUP BY
ymd,
page_location
)
SELECT
*
FROM
Master_01