連載:SQLのウィンドウ関数を利用した分析―継続率を利用したアクセスログの分析
はじめに
みなさん、こんにちは! DATUM STUDIOの宇佐見です。
普段の業務ではデータアナリストとして、他の企業さまから依頼を受けてデータ分析のお手伝いやデータ分析基盤の構築をさせていただいております。SQLを利用した様々な分析に携わることが多いため、そのノウハウを前回に引き続き紹介できればと思います。
やること紹介
本記事では3回にわけてウィンドウ関数を利用したSQLのケーススタディを紹介いたします。実際のアクセスログを利用して、実践ですぐ活用できることがコンセプトです。前提知識としてはSQLを利用した基礎的な集計を行えるレベルを想定しています。
第2回目は、「継続率を利用したアクセスログの分析」です。ユーザごとの週次のアクセス状況から新規、継続、復活のユーザの比率を求めます。実務でも非常に多く利用されていますが、ウィンドウ関数を利用すると比較的簡単に求めることができます。
以下を新規、継続、復活、離脱の定義とします。
- 新規:以前の週に利用がないが、集計対象の週には利用があるユーザ
- 継続:集計対象の前週に利用があり、集計対象の週にも利用があるユーザ
- 復活:集計対象の前々週には利用があり前週に利用がないが、集計対象の週にも利用があるユーザ
なお集計を開始した週については全ユーザが新規扱いとなります。
環境とデータソース
実行環境は前回に引き続きAWSのRedshiftを利用します。
データソースに関しても前回と同様にDATUMSTUDIOのホームページのアクセスログを利用します。アクセスログにおけるIPアドレスをハッシュ化し、それをユーザIDと見なしています。
以下のようなデータ形式となっております。
access_time | user_id ---------------------+---------------------------------- 2017-05-21 03:11:18 | 3f3fcac868404d0ae7e8ad90be6fc4d8 2017-05-21 03:11:21 | 3f3fcac868404d0ae7e8ad90be6fc4d8 2017-05-21 03:11:21 | 3f3fcac868404d0ae7e8ad90be6fc4d8 2017-05-21 03:23:50 | e07e844ba0c20d195f4701fb4f14451e 2017-05-21 03:24:26 | efcdf099ce3f9be9a0f9444f06e2679d 2017-05-21 03:24:27 | efcdf099ce3f9be9a0f9444f06e2679d 2017-05-21 03:24:28 | efcdf099ce3f9be9a0f9444f06e2679d 2017-05-21 03:28:11 | 5cba30f1752bae2a77f05007311b95c3 2017-05-21 03:28:11 | 5cba30f1752bae2a77f05007311b95c3 2017-05-21 03:28:13 | 5cba30f1752bae2a77f05007311b95c3 2017-05-21 03:28:19 | 50dda73283d5a5d3bdc3e01c787a03a0 2017-05-21 03:28:22 | 5da679a7d0ce86eed996bb4c568765f7
ウィンドウ関数の説明については前回の投稿を参照してください。
・連載:SQLのウィンドウ関数を利用した分析―セッションを利用したアクセスログの分析
継続率を利用したアクセスログの分析
前回と同様に本記事では、簡単なクエリからスタートしその過程を説明しながら、ゴールである「継続率を利用したアクセスログの分析」のクエリを完成させます。
まずはページビューごとのデータから、ユーザごとの週次のアクセス情報に集約します。DATE_TRUNC関数を利用して日付をその週の月曜日になるよう切り捨てます。更にCAST関数でTIMESTAMP型からDATE型に変換します。
SELECT
CAST(DATE_TRUNC('week', access_time) AS DATE) AS access_week,
user_id,
COUNT(*)
FROM
datum_access_log
GROUP BY
CAST(DATE_TRUNC('week', access_time) AS DATE),
user_id
;
access_week | user_id | count -------------+----------------------------------+------- 2017-05-15 | 3f3fcac868404d0ae7e8ad90be6fc4d8 | 135 2017-05-15 | 94206ce9bf9de4874af016067d414d7e | 1 2017-05-15 | d1522ce8a41506346c24c6b1579566dd | 25 2017-05-15 | e07e844ba0c20d195f4701fb4f14451e | 14 2017-05-15 | efcdf099ce3f9be9a0f9444f06e2679d | 3 2017-05-15 | 5cba30f1752bae2a77f05007311b95c3 | 3 2017-05-15 | 50dda73283d5a5d3bdc3e01c787a03a0 | 4 2017-05-15 | 5da679a7d0ce86eed996bb4c568765f7 | 14
先程のクエリは副問い合わせにまとめておきます。
続いてウィンドウ関数を利用して各ユーザが初めて利用した週、前回利用した週を取得します。初めて利用した週についてはFIRST_VALUE関数を、前回利用した週についてはLAG関数を用います。LAG関数の結果NAになることを防ぐためにNVL関数も適応しました。
WITH weekly_access_data AS (
SELECT
CAST(DATE_TRUNC('week', access_time) AS DATE) AS access_week,
user_id,
COUNT(*)
FROM
datum_access_log
GROUP BY
CAST(DATE_TRUNC('week', access_time) AS DATE),
user_id
)
SELECT
*,
FIRST_VALUE(access_week) OVER(PARTITION BY user_id) AS first_access_week,
NVL(LAG(access_week) OVER (PARTITION BY user_id ORDER BY access_week), '1970-01-01') AS previous_access_week
FROM
weekly_access_data
;
access_week | user_id | count | first_access_week | previous_access_week -------------+----------------------------------+-------+-------------------+---------------------- 2017-06-05 | 00001cc54dfaf28cd922158caaa3ad1f | 25 | 2017-06-05 | 1970-01-01 2017-06-12 | 00023345b78c5b5ad98aaffc99a9528e | 21 | 2017-06-12 | 1970-01-01 2017-06-05 | 0004e74949a9e134f8e14638c181dd95 | 3 | 2017-06-05 | 1970-01-01 2017-06-19 | 001533de27d9a51c5359d885a59c6d58 | 1 | 2017-05-29 | 2017-06-12 2017-06-12 | 001533de27d9a51c5359d885a59c6d58 | 3 | 2017-05-29 | 2017-06-05 2017-06-05 | 001533de27d9a51c5359d885a59c6d58 | 1 | 2017-05-29 | 2017-05-29 2017-05-29 | 001533de27d9a51c5359d885a59c6d58 | 2 | 2017-05-29 | 1970-01-01 2017-05-29 | 001e411e16969083a7abf1434dc77500 | 3 | 2017-05-29 | 1970-01-01 2017-06-05 | 002e402a04b53a4f6d500078f9deb99b | 3 | 2017-06-05 | 1970-01-01
初めて利用した週と今週を比較して、等価ならばTRUE、非等価ならばFALSEを記録する新規フラグを立てます。
同様に前回利用した週と前週を比較して、等価ならばTRUE、非等価ならばFALSEを記録する前週利用フラグを立てます。
WITH weekly_access_data AS (
SELECT
CAST(DATE_TRUNC('week', access_time) AS DATE) AS access_week,
user_id,
COUNT(*)
FROM
datum_access_log
GROUP BY
CAST(DATE_TRUNC('week', access_time) AS DATE),
user_id
)
SELECT
*,
access_week = FIRST_VALUE(access_week) OVER(PARTITION BY user_id) AS first_access_flag,
DATEADD(week, -1, access_week) = NVL(LAG(access_week) OVER (PARTITION BY user_id ORDER BY access_week), '1970-01-01') AS previous_access_flag
FROM
weekly_access_data
;
access_week | user_id | count | first_access_flag | previous_access_flag -------------+----------------------------------+-------+-------------------+---------------------- 2017-06-05 | 00001cc54dfaf28cd922158caaa3ad1f | 25 | t | f 2017-06-12 | 00023345b78c5b5ad98aaffc99a9528e | 21 | t | f 2017-06-05 | 0004e74949a9e134f8e14638c181dd95 | 3 | t | f 2017-05-29 | 0012c93b674f56d085fd2b17161829ab | 3 | t | f 2017-06-19 | 001533de27d9a51c5359d885a59c6d58 | 1 | f | t 2017-06-12 | 001533de27d9a51c5359d885a59c6d58 | 3 | f | t 2017-06-05 | 001533de27d9a51c5359d885a59c6d58 | 1 | f | t 2017-05-29 | 001533de27d9a51c5359d885a59c6d58 | 2 | t | f 2017-05-29 | 001e411e16969083a7abf1434dc77500 | 3 | t | f
可読性を上げるために先程のクエリを副問い合わせにまとめます。そして新規フラグ、前週利用フラグを利用して継続フラグ、復活フラグを立てます。継続フラグは前週利用フラグがTRUEの場合に立てます。復活フラグは新規フラグがFALSE、前週利用フラグがFALSEの場合に立てます。これでユーザ1人ごとの1週間の動向が分かります。
WITH weekly_access_data AS (
SELECT
CAST(DATE_TRUNC('week', access_time) AS DATE) AS access_week,
user_id,
COUNT(*)
FROM
datum_access_log
GROUP BY
CAST(DATE_TRUNC('week', access_time) AS DATE),
user_id
), weekly_access_flag AS (
SELECT
*,
access_week = FIRST_VALUE(access_week) OVER(PARTITION BY user_id) AS first_access_flag,
DATEADD(week, -1, access_week) = NVL(LAG(access_week) OVER (PARTITION BY user_id ORDER BY access_week), '1970-01-01') AS previous_access_flag
FROM
weekly_access_data
)
SELECT
access_week,
user_id,
first_access_flag,
previous_access_flag AS countinue_access_flag,
(first_access_flag = FALSE AND previous_access_flag = FALSE) AS return_access_flag
FROM
weekly_access_flag
;
access_week | user_id | first_access_flag | countinue_access_flag | return_access_flag -------------+----------------------------------+-------------------+-----------------------+-------------------- 2017-06-05 | 00001cc54dfaf28cd922158caaa3ad1f | t | f | f 2017-06-12 | 00023345b78c5b5ad98aaffc99a9528e | t | f | f 2017-06-05 | 0004e74949a9e134f8e14638c181dd95 | t | f | f 2017-05-29 | 0012c93b674f56d085fd2b17161829ab | t | f | f 2017-06-19 | 001533de27d9a51c5359d885a59c6d58 | f | t | f 2017-06-12 | 001533de27d9a51c5359d885a59c6d58 | f | t | f 2017-06-05 | 001533de27d9a51c5359d885a59c6d58 | f | t | f 2017-05-29 | 001533de27d9a51c5359d885a59c6d58 | t | f | f 2017-05-29 | 001e411e16969083a7abf1434dc77500 | t | f | f
最後に新規フラグ、継続フラグ、復活フラグごとに週次で集約を行います。
WITH weekly_access_data AS (
SELECT
CAST(DATE_TRUNC('week', access_time) AS DATE) AS access_week,
user_id,
COUNT(*)
FROM
datum_access_log
GROUP BY
CAST(DATE_TRUNC('week', access_time) AS DATE),
user_id
), weekly_access_flag AS (
SELECT
*,
access_week = FIRST_VALUE(access_week) OVER(PARTITION BY user_id) AS first_access_flag,
DATEADD(week, -1, access_week) = NVL(LAG(access_week) OVER (PARTITION BY user_id ORDER BY access_week), '1970-01-01') AS previous_access_flag
FROM
weekly_access_data
), weekly_access_flag_2 AS (
SELECT
access_week,
user_id,
first_access_flag,
previous_access_flag AS countinue_access_flag,
(first_access_flag = FALSE AND previous_access_flag = FALSE) AS return_access_flag
FROM
weekly_access_flag
)
SELECT
access_week,
SUM(CASE WHEN first_access_flag = TRUE THEN 1 ELSE 0 END) first_access_user_count,
SUM(CASE WHEN countinue_access_flag = TRUE THEN 1 ELSE 0 END) countinue_user_count,
SUM(CASE WHEN return_access_flag = TRUE THEN 1 ELSE 0 END) return_user_count
FROM
weekly_access_flag_2
GROUP BY
access_week
;
access_week | first_access_user_count | countinue_user_count | return_user_count -------------+-------------------------+----------------------+------------------- 2017-05-15 | XXXX | XXX | XXX 2017-05-22 | XXXX | XXX | XXX 2017-05-29 | XXXX | XXX | XXX 2017-06-05 | XXXX | XXX | XXX 2017-06-12 | XXXX | XXX | XXX
※具体的なユーザ数が分かってしまうため、数字は伏せさせていただきます。
離脱数についてはエクセルなどを用いて「前週の総利用者数 – 今週の継続者数」から算出することをオススメします。 (クエリでは、データのない項目を集計することは難しいため)
最後に
今回もシンプルなクエリで表現することができました。次回はソーシャルゲームで利用されるFQ5の集計を紹介できればと思います。
データはあるけど集計環境が作れない、複雑な集計を計画できても実行できないなどでお困りの場合は、ぜひDATUM STUDIOにお声がけください。
DATUM STUDIOは、クライアントの事業成長と経営課題解決を最適な形でサポートする、データ・ビジネスパートナーです。
データ分析の分野でお客様に最適なソリューションをご提供します。まずはご相談ください。
Contact
Explore Jobs
関連記事