Go to main content
Case study: end-to-end marketing data science - official lesson image on GinnyTech, created by AD

Case Study: End-to-End Marketing Data Science

Practical Project: Predict churn and build a data-driven retention strategy.

AD
Created byAndrii Dyshkantiuk
Lesson 91 / 236Level: AdvancedDuration: 28 minPrerequisites: 1

What you will learn

  • Costruire 20 feature di churn da transazioni, utilizzo, attrito e tenure
  • Addestrare un Gradient Boosting e incrociare rischio e valore per assegnare azioni
  • Presentare precisione nel segmento critico e ROI in euro al decisore

Case Study: End-to-End Marketing Data Science

È l’ultima lezione del modulo, e la tratto come si tratta una consegna vera: un progetto completo di retention, dalla prima feature all’ultima slide per il decisore. Tutto il lavoro si svolge su tabelle di feature, modelli tabellari e pipeline di scoring, e l’obiettivo è un ROI in euro, non un AUC da esibire. Seguiamo il caso fase per fase, una settimana alla volta.

Di cosa parla il caso

Il caso end-to-end collega feature, modello di churn, segmentazione per valore e attivazione quotidiana per decidere dove investire il budget di retention.

La sequenza di lavoro in cinque passaggi

Prima di entrare nel dettaglio, ecco la mappa del progetto. La seguiremo fase per fase, una settimana alla volta.

  1. Ingegnerizza 20 feature da transazioni, utilizzo, attrito e tenure.
  2. Addestra un Gradient Boosting per la probabilità di churn a 30 giorni.
  3. Incrocia rischio e valore per assegnare azione, canale e budget.
  4. Attiva scoring giornaliero con soglia di degrado AUC e Reverse ETL.
  5. Presenta precisione nel segmento critico e ROI in euro al decisore.

Fase 1: feature engineering in settimana 1

Si parte costruendo 20 feature da dati transazionali, navigazione, supporto e marketing. L’obiettivo è leggere il caso come una consegna di lavoro completa. Ogni feature deve spiegare quale comportamento cattura e quale segnale di rischio porta con sé.

# Features from transactional data
df['recency_days'] = (df['today'] - df['last_order_date']).dt.days
df['frequency_total'] = df.groupby('customer_id')['order_id'].transform('count')
df['monetary_total'] = df.groupby('customer_id')['amount'].transform('sum')
df['aov'] = df['monetary_total'] / df['frequency_total']

# Usage trends
df['active_days_last_14'] = df.groupby('customer_id')['active_day'].transform(
    lambda x: x.rolling(14).sum())
df['active_days_prev_14'] = df.groupby('customer_id')['active_day'].transform(
    lambda x: x.shift(14).rolling(14).sum())
df['usage_trend'] = df['active_days_last_14'] / df['active_days_prev_14'].clip(1)

# Churn signals
df['has_payment_failure'] = (df['failed_payments_30d'] > 0).astype(int)
df['support_tickets_30d'] = df['support_tickets_30d']
df['avg_session_minutes_trend'] = df['avg_session_7d'] / df['avg_session_prev_7d'].clip(1)

# Engagement depth
features_list = ['feature_try_on', 'feature_style_quiz', 'feature_wishlist',
                 'feature_reviews', 'feature_referral']
df['engagement_depth'] = df[features_list].sum(axis=1)

# Temporal signals
df['tenure_months'] = (df['today'] - df['signup_date']).dt.days / 30
df['is_month_1'] = (df['tenure_months'] <= 1).astype(int)

Fase 2: modello predittivo in settimana 2

Con le feature pronte, addestriamo un Gradient Boosting per stimare la probabilità di churn nel mese successivo. Definiamo il churn come assenza di ordini nei 30 giorni seguenti. Teniamo d’occhio quali feature pesano di più nella decisione del modello.

from sklearn.ensemble import GradientBoostingClassifier
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.metrics import roc_auc_score, classification_report

feature_cols = ['recency_days', 'frequency_total', 'monetary_total', 'aov',
    'usage_trend', 'has_payment_failure', 'support_tickets_30d',
    'avg_session_minutes_trend', 'engagement_depth', 'tenure_months',
    'is_month_1', 'days_since_last_login', 'email_open_rate_30d']

X = df[feature_cols].fillna(0)
y = df['churned_next_month']  # definition: no orders in the next 30 days

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, stratify=y)

model = GradientBoostingClassifier(
    n_estimators=200, max_depth=4, learning_rate=0.05, random_state=42)
model.fit(X_train, y_train)

probs = model.predict_proba(X_test)[:, 1]
auc = roc_auc_score(y_test, probs)
print(f"AUC: {auc:.3f}")  # Target: >0.82

# Feature importance
importances = pd.Series(model.feature_importances_, index=feature_cols)
print(importances.sort_values(ascending=False).head(10))

Expected result: AUC intorno a 0.84, con recency_days, usage_trend e tenure_months tra le tre feature più importanti.

Fase 3: segmentazione per azione in settimana 3

Un buon punteggio di rischio non basta. Dividi i clienti in quattro quadranti incrociando Churn Risk e Customer Value. La stessa probabilità di abbandono richiede risposte diverse a seconda del valore del cliente.

df['churn_risk'] = model.predict_proba(df[feature_cols].fillna(0))[:, 1]
df['risk_tier'] = pd.qcut(df['churn_risk'], 4, labels=['low','medium','high','critical'])
df['value_tier'] = pd.qcut(df['monetary_total'], 4, labels=['low','medium','high','vip'])

segments = df.groupby(['risk_tier', 'value_tier']).agg(
    customers=('customer_id', 'count'),
    avg_ltv=('monetary_total', 'mean')
).reset_index()

A ogni quadrante corrisponde un’azione, un canale e un budget stimato.

Rischio per ValoreSegmentActionChannelEstimated budget
Critical per VIPSave our starsPersonal customer success call within 24hPhone, dedicated email50-100 euro per cliente
Critical per LowAuto-saveAutomated email sequence with offerEmail, push5-10 euro per cliente
High per VIPProactive loveExclusive invitation, sneak peek of new collectionsEmail, direct mail20-30 euro per cliente
Low per VIPLoyalty rewardReferral program, early access to salesEmail, in-app10-15 euro per cliente
Low-Medium per LowNurtureEducational content, social proofEmail, in-app2-5 euro per cliente

Verdetto: il quadrante a rischio critico e valore VIP riceve contatto umano entro 24 ore, tutto il resto va in automazione pesata per costo.

Fase 4: deploy e monitoring in settimana 4

Il modello vive solo se entra in un flusso quotidiano e se qualcuno si accorge quando smette di funzionare. La pipeline assegna gli score ogni mattina. Un controllo mensile verifica che l’AUC non scenda sotto la soglia di degrado.

# Pipeline di scoring giornaliera
def daily_churn_scoring():
    new_data = extract_features_from_warehouse()
    new_data['churn_risk'] = model.predict_proba(new_data[feature_cols])[:, 1]
    write_to_activation_table(new_data[['customer_id', 'churn_risk', 'risk_tier']])

schedule.every().day.at("06:00").do(daily_churn_scoring)

# Monitoring AUC drift mensile
def check_auc_drift():
    recent_actuals = get_recent_outcomes()
    recent_preds = model.predict_proba(recent_actuals[feature_cols])[:, 1]
    current_auc = roc_auc_score(recent_actuals['churned'], recent_preds)
    if current_auc < 0.78:  # soglia di degrado
        send_alert(f"AUC drift detected: {current_auc:.3f}")

The table mrt_activation__customer_health viene sincronizzata via Reverse ETL su Braze e Salesforce. I customer success manager vedono la lista Save our stars direttamente nel CRM.

Fase 5: presentazione al decisore in 2 slide

La prima slide dimostra che il modello funziona. L’AUC di 0.84 significa che nell’84 per cento dei casi una coppia churner e non-churner viene ordinata correttamente. Prendendo il 20 per cento più a rischio catturiamo il 64 per cento dei churner reali. Nel segmento Critical la precision è del 71 per cento: chi predici come critical churna davvero nel 71 per cento dei casi.

La seconda slide traduce tutto in valore. La popolazione Critical per VIP conta 1800 clienti con LTV medio di 2400 euro. Salvandone il 40 per cento con intervento proattivo si preservano 720 clienti per 2400 euro, cioè 1.73 milioni di LTV. Il costo del programma è di 180000 euro all’anno, tra un FTE di customer success e i tool, per un ROI di 9.6x nel primo anno.

Quality control

Prima di usare questo lavoro in una decisione, controlla sempre completezza, duplicati, timezone, definizioni cambiate e segmenti esclusi. Molte analisi apparentemente sofisticate falliscono perché il dato di partenza misura un comportamento diverso da quello che il team crede di osservare. Chiediti infine: se il risultato fosse instabile, quale scelta sbaglieresti? Se la risposta non è concreta, manca ancora il collegamento tra analisi e azione.

Un caso che fa riflettere: Netflix

Nel primo trimestre 2022 Netflix perse 200000 abbonati a livello globale, il primo calo netto dopo oltre un decennio di crescita continua. La società attribuì il calo a saturazione, concorrenza e condivisione delle password fuori nucleo. La risposta fu operativa: stretta sulla condivisione account e lancio del piano con pubblicità. Il caso mostra un progetto retention end-to-end: segnale di churn, segmentazione per valore e azione commerciale misurabile.

Domande per l’autoverifica

  1. Quale soglia di rischio attiva il contatto umano entro 24 ore?
  2. Quale feature pesa di più tra recency, trend di utilizzo e tenure?
  3. Quale degrado di AUC fa scattare l’allarme di retraining?
  4. Quale ROI in euro giustifica il programma sul segmento a valore VIP?
Serve una mano concreta?

Bloccato su questo argomento o vuoi applicarlo al tuo caso? Prenota una call di 15 minuti con un analista esperto.

Book a call