
Caso studio: marketing data science end-to-end
Progetto pratico: predire il churn e costruire una strategia data-driven di retention.
Cosa imparerai
- 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
Collegamenti
Caso studio: marketing data science end-to-end
È 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.
- Ingegnerizza 20 feature da transazioni, utilizzo, attrito e tenure.
- Addestra un Gradient Boosting per la probabilità di churn a 30 giorni.
- Incrocia rischio e valore per assegnare azione, canale e budget.
- Attiva scoring giornaliero con soglia di degrado AUC e Reverse ETL.
- 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é.
# Feature da dati transazionali
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']
# Trend di utilizzo
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)
# Segnali di attrito
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)
# Segnali temporali
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'] # definizione: nessun ordine nei 30gg successivi
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))
Risultato atteso: 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 Valore | Segmento | Azione | Canale | Budget stimato |
|---|---|---|---|---|
| Critical per VIP | Save our stars | Call personale del customer success entro 24h | Telefono, email dedicata | 50-100 euro per cliente |
| Critical per Low | Auto-save | Sequenza email automatica con offerta | Email, push | 5-10 euro per cliente |
| High per VIP | Proactive love | Invito esclusivo, sneak peek nuove collezioni | Email, direct mail | 20-30 euro per cliente |
| Low per VIP | Loyalty reward | Programma referral, accesso anticipato saldi | Email, in-app | 10-15 euro per cliente |
| Low-Medium per Low | Nurture | Contenuti educativi, social proof | Email, in-app | 2-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}")
La tabella 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.
Controllo di qualità
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
- Quale soglia di rischio attiva il contatto umano entro 24 ore?
- Quale feature pesa di più tra recency, trend di utilizzo e tenure?
- Quale degrado di AUC fa scattare l’allarme di retraining?
- Quale ROI in euro giustifica il programma sul segmento a valore VIP?
Bloccato su questo argomento o vuoi applicarlo al tuo caso? Prenota una call di 15 minuti con un analista esperto.
Percorso collegato
Lezioni da leggere insieme
Questi collegamenti portano la lezione dentro il resto del corso: basi da riprendere, passaggi successivi e connessioni tematiche tra moduli.