Mobile App Retention Analysis
Developed a survival analysis model to identify critical churn inflection points for a Fintech app with 2M+ daily active users, then built a predictive notification engine to intervene before users churned.
The Problem
A rapidly scaling fintech app was losing 34% of new users within the first 30 days. The product team had intuition about friction points but lacked statistical evidence. Marketing was blasting generic re-engagement emails with a 0.8% click-through rate. The business needed to understand WHEN users were most likely to churn and WHAT behaviors predicted it, so interventions could be precisely timed and personalized.
The Approach
- Ingested 18 months of event-level data (2.3B events) into BigQuery for cohort analysis
- Applied Kaplan-Meier survival curves to identify the 72-hour and Day-7 inflection points
- Built a Random Forest classifier on 147 behavioral features to score churn probability
- Designed a multi-armed bandit experiment to optimize nudge timing and messaging
- Integrated the prediction model into Braze for real-time triggered campaigns
Impact
Measurable Results
The survival model revealed that 68% of eventual churners showed disengagement signals within 72 hours of onboarding — far earlier than the product team assumed. By targeting users at 85% predicted churn probability with personalized in-app nudges, we reduced Day-30 churn by 24% and increased average customer lifetime value by 18%.
Technical Implementation
# Survival Analysis: Identifying Churn Inflection Points
from lifelines import KaplanMeierFitter
import pandas as pd
# Load user cohort data
cohorts = pd.read_gbq("""
SELECT user_id, days_since_signup, churned
FROM analytics.user_survival
WHERE signup_date >= '2023-01-01'
""")
# Fit Kaplan-Meier estimator
kmf = KaplanMeierFitter()
kmf.fit(durations=cohorts['days_since_signup'],
event_observed=cohorts['churned'])
# Identify critical drop-off windows
hazard_72h = 1 - kmf.survival_function_at_times(3).iloc[0]
hazard_d7 = 1 - kmf.survival_function_at_times(7).iloc[0]
print(f"72h churn risk: {hazard_72h:.1%}") # 68.3%
print(f"D7 churn risk: {hazard_d7:.1%}") # 81.2%
# Export survival curve for dashboard
kmf.plot_survival_function()
plt.savefig('survival_curve.png')
Tools & Stack
Key Learnings
- Early signals (first 72h) are far more predictive than late-stage behavior
- Model explainability (SHAP values) was critical for getting product buy-in
- Real-time scoring required moving from batch (daily) to streaming (event-driven)
- Multi-armed bandits outperformed fixed A/B tests for nudge optimization by 14%