mirror of
https://gitlab.com/cief-data/dbt_cloud.git
synced 2026-08-19 04:14:00 +00:00
Shipping data model - survival analysis
This commit is contained in:
@@ -0,0 +1,183 @@
|
||||
-- Survival analysis code reference from https://www.crosstab.io/articles/sql-survival-curves/
|
||||
|
||||
-- AVAILABLE FILTER VALUE
|
||||
-- Source:preset_custom_filter Column:day_use_to_churn
|
||||
{% set day_use_to_churn = 120 %}
|
||||
|
||||
|
||||
-- IMPORT
|
||||
WITH companies AS (
|
||||
SELECT * FROM {{ ref('dim_shipping__companies') }}
|
||||
),
|
||||
|
||||
origin_warehouse_shipping_packing_lists AS (
|
||||
SELECT * FROM {{ ref('fct_shipping__origin_warehouse_shipping_packing_lists') }}
|
||||
),
|
||||
|
||||
|
||||
-- LOGIC
|
||||
duration_table AS (
|
||||
|
||||
SELECT
|
||||
companies.sub_company_id,
|
||||
companies.sub_company_marking_id,
|
||||
companies.sub_company_created_datetime,
|
||||
COUNT(origin_warehouse_shipping_packing_lists.origin_warehouse_shipping_packing_list_id) AS count_order,
|
||||
|
||||
COUNT(IFF(
|
||||
origin_warehouse_shipping_packing_lists.origin_warehouse_shipping_order_status IN ('APPROVED', 'COMPLETED'),
|
||||
origin_warehouse_shipping_packing_lists.origin_warehouse_shipping_packing_list_id,
|
||||
null
|
||||
)) AS count_completed_order,
|
||||
|
||||
MAX(origin_warehouse_shipping_packing_lists.origin_warehouse_shipping_order_created_datetime) AS last_order_datetime,
|
||||
COALESCE(last_order_datetime, sub_company_created_datetime) AS last_activity_datetime,
|
||||
IFF(last_order_datetime IS NULL, 1, 0) AS is_never_order_company,
|
||||
|
||||
-- if is_churn_company = 0, the data will be censored
|
||||
IFF(
|
||||
DATEDIFF(day, last_activity_datetime, CURRENT_DATE()) >= {{day_use_to_churn}},
|
||||
1,
|
||||
0
|
||||
) AS is_churn_company,
|
||||
|
||||
CASE
|
||||
WHEN is_churn_company = 1 THEN
|
||||
DATEDIFF(day, companies.sub_company_created_datetime, last_activity_datetime) + {{day_use_to_churn}}
|
||||
ELSE
|
||||
DATEDIFF(day, companies.sub_company_created_datetime, CURRENT_DATE())
|
||||
END AS survival_time_days --event duration
|
||||
|
||||
FROM companies
|
||||
|
||||
LEFT JOIN origin_warehouse_shipping_packing_lists
|
||||
ON (companies.sub_company_id = origin_warehouse_shipping_packing_lists.sub_company_id)
|
||||
|
||||
GROUP BY
|
||||
companies.sub_company_id,
|
||||
companies.sub_company_marking_id,
|
||||
companies.sub_company_created_datetime
|
||||
|
||||
HAVING
|
||||
-- companies without order will not be relevant to the analysis
|
||||
is_never_order_company = 0
|
||||
|
||||
),
|
||||
|
||||
-- the daily_tally cte count the total number of observations at each survival_time_day
|
||||
-- and the number of company that have churned at that survival_time_day
|
||||
daily_observation_tally AS (
|
||||
|
||||
SELECT
|
||||
survival_time_days,
|
||||
COUNT(survival_time_days) AS total_number_of_observations,
|
||||
SUM(is_churn_company) AS events -- considering only churned company
|
||||
|
||||
FROM duration_table
|
||||
|
||||
GROUP BY survival_time_days
|
||||
|
||||
ORDER BY survival_time_days
|
||||
|
||||
),
|
||||
|
||||
-- the cumulative_tally cte counts the number of subjects still at risk of experiencing churn
|
||||
cumulative_tally AS (
|
||||
|
||||
SELECT
|
||||
survival_time_days,
|
||||
events,
|
||||
total_number_of_observations,
|
||||
|
||||
( SELECT COUNT(DISTINCT(sub_company_id)) FROM duration_table ) AS total_number_of_subjects,
|
||||
|
||||
-- cumulative sum of observations at all previous survival_time_days SUBTRACTED by total_number_of_subjects
|
||||
total_number_of_subjects - COALESCE(
|
||||
SUM(total_number_of_observations) OVER (ORDER BY survival_time_days ROWS BETWEEN UNBOUNDED PRECEDING AND 1 PRECEDING)
|
||||
,0
|
||||
) AS at_risk
|
||||
|
||||
FROM daily_observation_tally
|
||||
|
||||
),
|
||||
|
||||
-- At each survival_time_day, count number of censored subject
|
||||
-- censored subjects = # subject at risk - churned - # subject at risk in the next duration
|
||||
compute_censored_subjects AS (
|
||||
|
||||
SELECT
|
||||
total_number_of_subjects,
|
||||
survival_time_days,
|
||||
at_risk,
|
||||
total_number_of_observations,
|
||||
events,
|
||||
at_risk - events - COALESCE(
|
||||
LEAD(at_risk, 1) OVER (ORDER BY survival_time_days)
|
||||
,0
|
||||
) AS censored
|
||||
|
||||
FROM cumulative_tally
|
||||
|
||||
-- Simply subtracting events from number of observations would incorrectly ignore subjects censored at durations that are dropped from the output table
|
||||
WHERE events > 0
|
||||
|
||||
),
|
||||
|
||||
compute_probability AS (
|
||||
|
||||
SELECT
|
||||
*,
|
||||
-- The survival probability represents the probability of customers that will not churn up to a specific tenure
|
||||
-- Example: survival_day = 96, survival_prob = 95%. For customers with 96 days of tenure, the customer has a 95% chance of not churning.
|
||||
EXP(SUM(
|
||||
-- When events / at_risk = 1, we replace value 1 with a value close to 1, to prevent log 0 which causes infinity value
|
||||
LN(CASE WHEN (1 - events / at_risk) = 0 THEN 0.999999 ELSE (1 - events / at_risk) END)
|
||||
) OVER (
|
||||
ORDER BY survival_time_days ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
|
||||
)
|
||||
) AS survival_probability,
|
||||
|
||||
100 * (1 - EXP(SUM(
|
||||
-- When events / at_risk = 1, we replace value 1 with a value close to 1, to prevent log 0 which causes infinity value
|
||||
LN(CASE WHEN (1 - events / at_risk) = 0 THEN 0.999999 ELSE (1 - events / at_risk) END)
|
||||
) OVER (
|
||||
ORDER BY survival_time_days ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
|
||||
)
|
||||
)) AS conversion_percentage,
|
||||
|
||||
SUM(events / at_risk)
|
||||
OVER (
|
||||
ORDER BY survival_time_days ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
|
||||
) AS cumulative_hazard,
|
||||
|
||||
'{{ modules.datetime.datetime.now(modules.pytz.timezone("Asia/Kuala_Lumpur")) }}' AS _dbt_ran_datetime
|
||||
|
||||
FROM compute_censored_subjects
|
||||
|
||||
),
|
||||
|
||||
|
||||
-- FINAL
|
||||
final__rep_shipping__survival_analysis AS (
|
||||
|
||||
SELECT
|
||||
-- dimension
|
||||
survival_time_days,
|
||||
at_risk,
|
||||
total_number_of_observations,
|
||||
events,
|
||||
censored,
|
||||
|
||||
-- measures
|
||||
survival_probability,
|
||||
conversion_percentage,
|
||||
cumulative_hazard,
|
||||
|
||||
-- metadata
|
||||
_dbt_ran_datetime
|
||||
|
||||
FROM compute_probability
|
||||
|
||||
)
|
||||
|
||||
SELECT * FROM final__rep_shipping__survival_analysis
|
||||
Reference in New Issue
Block a user