Customer Lifetime Value
The Customer Worth Fighting For
A new loyalty member signs up at Target. They buy diapers and baby wipes. The analytics team flags this customer for special attention - not because they spent a lot on that first visit, but because diaper purchasers, historically, become among the most loyal customers Target has. A family with a new baby will buy diapers, formula, baby clothing, nursery furniture, and eventually toys - at high frequency for years. The lifetime value of this customer is not 5,000-$10,000 over the next 5 years.
Target's system - now famous from a 2012 New York Times article - identified pregnant customers from purchase patterns (unscented lotion, vitamin supplements, a specific combination of 25 products) and sent them targeted baby product offers before competitors could. The controversy about privacy aside, the insight was powerful: CLV is not what a customer has spent. It is what they will spend, informed by who they are and what life stage they are entering.
Every acquisition and retention decision in retail should flow from CLV. How much should you spend on a Google ad to acquire a customer? The answer depends on what that customer is worth over their lifetime with you. Should you offer a $20 loyalty credit to a churning customer? Depends on whether their expected future purchases justify the cost. Should you invest in customer service resolution for a complaint? Depends on whether this customer is a high-CLV one worth retaining.
Without CLV prediction, these decisions default to gut instinct, short-term revenue metrics, or uniform treatment of all customers - all of which systematically misallocate resources.
Why This Exists
The fundamental problem with retail marketing without CLV: you cannot tell the difference between a great customer and a lucky one.
A customer who spent 120 across four visits this year might be a rising loyal customer worth $3,000 over the next three years. Traditional reporting treats the first as a better customer. CLV-informed decisions correctly prioritize the second.
The financial case is stark. Research by Bain & Company found that increasing customer retention by 5% increases profits by 25-95% (depending on industry). The reason: customer acquisition costs are front-loaded (advertising, discounts, onboarding), while the revenue from a retained customer is ongoing and increasingly profitable as their behavior becomes predictable and you reduce service and acquisition costs.
For allocation decisions:
- Acquisition cost per customer should not exceed expected CLV * margin rate. If CLV = 150 to acquire that customer profitably.
- Retention investment should prioritize customers with high expected future CLV, not customers with high historical spend.
- Personalization quality should be calibrated to customer value - a high-CLV customer deserves a human call center agent, not a chatbot.
ML makes CLV prediction tractable because: (1) retail generates rich behavioral signals (purchase history, category affinity, seasonal patterns) that correlate with future behavior; (2) the heterogeneity in customer behavior is enormous and simple averages misrepresent most individuals; (3) the prediction horizon (2-5 years) is long enough that small improvements in forecast accuracy translate to meaningful allocation improvements.
Historical Context
The concept of customer lifetime value predates computers. Direct mail marketers in the 1960s and 1970s computed the "lifetime value" of a catalog customer based on their expected purchase frequency and order size, using this to set maximum allowable acquisition costs. The term "lifetime value" appears in direct marketing textbooks from the 1970s.
The probabilistic modeling tradition started with David Schmittlein, Donald Morrison, and Richard Colombo's BG/NBD precursor (the Pareto/NBD model, 1987). Their insight: model customer "death" (churn) explicitly as a latent event, rather than treating all customers as active. The Pareto/NBD separated the purchase frequency process from the churn process, allowing individual-level CLV predictions.
Peter Fader and Bruce Hardie simplified and extended this into the BG/NBD model (2005) and the Gamma-Gamma monetary value model (2007). Their open-source implementations made CLV prediction accessible to practitioners. The Python lifetimes library (2015) brought this to the data science community.
Deep learning entered CLV around 2018-2020. The key insight: purchase sequence data has temporal structure that RNNs and Transformers can exploit more effectively than statistical models. Papers from Amazon and JD.com showed LSTM-based CLV models outperforming BG/NBD by 15-25% on next-period purchase prediction.
The current state: probabilistic models (BG/NBD + Gamma-Gamma) remain the standard for interpretability and when data is sparse (fewer than 20 transactions per customer). Deep learning models win when data is rich (hundreds of transactions per customer, rich contextual features) and when calibrated uncertainty is less critical.
Core Concepts
RFM: The Foundation
RFM (Recency, Frequency, Monetary) is the oldest and simplest CLV framework. For each customer, compute:
- Recency (R): How recently did they last purchase? (days since last purchase)
- Frequency (F): How many times have they purchased in the observation period?
- Monetary (M): How much have they spent in the observation period?
Customers who are recent, frequent, and high-spend are your best customers. Customers who are distant, infrequent, and low-spend are your worst (or your churned ones).
RFM is powerful because it is intuitive, requires no model training, and produces segments that business teams can act on. Limitations: it treats all customers as stationary (does not account for trends), uses raw values that are sensitive to outliers, and does not produce calibrated probability predictions.
BG/NBD Model
The Buy-Till-You-Die (BG/NBD) model (Fader, Hardie, Lee, 2005) is the principled probabilistic approach to purchase frequency and churn modeling.
Key assumptions:
- While active, a customer makes purchases according to a Poisson process with rate (purchases per unit time)
- Each customer has an unobserved "lifetime" - after death, they make no more purchases. Death occurs after each transaction with probability , independently (Geometric death process)
- Heterogeneity in follows a Gamma distribution across the population
- Heterogeneity in follows a Beta distribution across the population
- and are independent across customers
Given a customer's observed history (recency, frequency, total observation time), the model estimates:
- : probability the customer is still an active customer
- : expected future purchases
The beauty of the model: a customer who was very active but stopped completely 6 months ago could either be churned (died) or just in a long dormant period. BG/NBD handles this uncertainty probabilistically rather than making a hard assignment.
Parameters estimated by MLE:
- : shape and rate of Gamma distribution for purchase rate
- : shape parameters of Beta distribution for churn probability
Gamma-Gamma Model
BG/NBD predicts purchase frequency. The Gamma-Gamma model (Fader, Hardie, 2013) predicts monetary value given that a purchase occurs.
Assumption: The average spend per transaction for a customer follows a Gamma distribution, and the average spend is independent of purchase frequency.
Combining BG/NBD + Gamma-Gamma gives the full CLV prediction:
Where is the discount rate (time value of money).
Deep Learning for CLV
For customers with rich transaction histories (50+ purchases), deep learning models outperform BG/NBD. The key advantage: they can incorporate contextual features beyond RFM - category browsing, seasonal patterns, marketing touchpoints, and demographic signals.
LSTM-based approach:
- Input sequence: each purchase event as a feature vector (time delta, amount, category, channel)
- LSTM processes the sequence, outputting a hidden state representing "where this customer is in their lifecycle"
- Output head: predict days until next purchase (regression) and expected transaction value (regression)
- Training: teacher forcing on historical sequences, predict next purchase from past
Transformer-based approach (more recent):
- Self-attention over purchase history is naturally suited for capturing long-range dependencies (a seasonal customer who bought last December is likely to buy again this December)
- BERT4Rec, SASRec, and BERT4CLV-style architectures have shown strong performance in published retail research
Practical Implementation
import pandas as pd
import numpy as np
from datetime import datetime, timedelta
import warnings
warnings.filterwarnings('ignore')
# ============================================================
# 1. RFM Feature Computation
# ============================================================
def compute_rfm(
transactions_df: pd.DataFrame,
customer_id_col: str = 'customer_id',
date_col: str = 'transaction_date',
revenue_col: str = 'revenue',
observation_end: datetime = None
) -> pd.DataFrame:
"""
Compute RFM features for each customer.
transactions_df: one row per transaction.
"""
if observation_end is None:
observation_end = transactions_df[date_col].max() + timedelta(days=1)
transactions_df = transactions_df.copy()
transactions_df[date_col] = pd.to_datetime(transactions_df[date_col])
rfm = transactions_df.groupby(customer_id_col).agg(
last_purchase=(date_col, 'max'),
frequency=(date_col, 'count'),
monetary=(revenue_col, 'mean'), # average order value
total_spend=(revenue_col, 'sum'),
first_purchase=(date_col, 'min'),
).reset_index()
rfm['recency_days'] = (observation_end - rfm['last_purchase']).dt.days
rfm['tenure_days'] = (rfm['last_purchase'] - rfm['first_purchase']).dt.days
return rfm
def rfm_segment(rfm_df: pd.DataFrame, n_quantiles: int = 5) -> pd.DataFrame:
"""
Assign RFM score quintiles (1-5) to each customer.
R: lower recency = higher score (recent buyers get 5)
F: higher frequency = higher score
M: higher monetary = higher score
"""
df = rfm_df.copy()
# Recency: lower = better (invert quantile)
df['R'] = pd.qcut(df['recency_days'], q=n_quantiles, labels=False, duplicates='drop')
df['R'] = n_quantiles - df['R'] # Invert: lower recency days = higher score
df['F'] = pd.qcut(df['frequency'], q=n_quantiles, labels=False, duplicates='drop') + 1
df['M'] = pd.qcut(df['monetary'], q=n_quantiles, labels=False, duplicates='drop') + 1
df['RFM_score'] = df['R'].astype(str) + df['F'].astype(str) + df['M'].astype(str)
df['RFM_numeric'] = (df['R'] + df['F'] + df['M'])
# Segment labels
def label(row):
if row['R'] >= 4 and row['F'] >= 4:
return 'Champions'
elif row['R'] >= 3 and row['F'] >= 3:
return 'Loyal Customers'
elif row['R'] >= 4 and row['F'] <= 2:
return 'Recent Customers'
elif row['R'] <= 2 and row['F'] >= 3:
return 'At Risk'
elif row['R'] <= 2 and row['F'] <= 2:
return 'Lost / Churned'
else:
return 'Potential Loyalists'
df['segment'] = df.apply(label, axis=1)
return df
# ============================================================
# 2. BG/NBD + Gamma-Gamma CLV with lifetimes library
# ============================================================
def compute_bgNBD_clv(
transactions_df: pd.DataFrame,
customer_id_col: str = 'customer_id',
date_col: str = 'transaction_date',
revenue_col: str = 'revenue',
observation_end: datetime = None,
prediction_period: int = 365,
discount_rate: float = 0.01,
profit_margin: float = 0.20
) -> pd.DataFrame:
"""
Compute CLV using BG/NBD + Gamma-Gamma models.
Requires: pip install lifetimes
"""
try:
from lifetimes import BetaGeoFitter, GammaGammaFitter
from lifetimes.utils import summary_data_from_transaction_data
except ImportError:
raise ImportError("Install with: pip install lifetimes")
if observation_end is None:
observation_end = transactions_df[date_col].max() + timedelta(days=1)
# Create BG/NBD summary data (recency, frequency, T in weeks)
summary = summary_data_from_transaction_data(
transactions_df,
customer_id_col=customer_id_col,
datetime_col=date_col,
monetary_value_col=revenue_col,
observation_period_end=observation_end,
freq='W', # Weekly frequency
)
# Remove rows with 0 purchases (only first-time purchasers, no repeat history)
summary = summary[summary['frequency'] > 0]
print(f"Fitting BG/NBD on {len(summary)} customers...")
# ---- BG/NBD: Fit purchase frequency and churn model ----
bgf = BetaGeoFitter(penalizer_coef=0.001)
bgf.fit(
summary['frequency'],
summary['recency'],
summary['T'],
verbose=False
)
# Probability alive for each customer
summary['prob_alive'] = bgf.conditional_probability_alive(
summary['frequency'],
summary['recency'],
summary['T']
)
# Expected transactions in next N weeks
n_weeks = prediction_period // 7
summary['predicted_purchases'] = bgf.conditional_expected_number_of_purchases_up_to_time(
n_weeks,
summary['frequency'],
summary['recency'],
summary['T']
)
# ---- Gamma-Gamma: Fit monetary value model ----
# Filter to customers with repeat purchases only (required by GGF)
repeat_customers = summary[summary['frequency'] > 1]
ggf = GammaGammaFitter(penalizer_coef=0.001)
ggf.fit(
repeat_customers['frequency'],
repeat_customers['monetary_value'],
verbose=False
)
# Expected average order value for all customers
summary['expected_aov'] = ggf.conditional_expected_average_profit(
summary['frequency'],
summary['monetary_value']
)
# ---- CLV Computation ----
# CLV = margin * sum of discounted future transaction values
# Using lifetimes' built-in CLV function
clv = ggf.customer_lifetime_value(
bgf,
summary['frequency'],
summary['recency'],
summary['T'],
summary['monetary_value'],
time=n_weeks,
discount_rate=discount_rate / 52, # Convert annual to weekly
freq='W'
) * profit_margin
summary['clv'] = clv
# CLV segments
clv_percentiles = summary['clv'].quantile([0.5, 0.8, 0.95])
def clv_tier(val):
if val >= clv_percentiles[0.95]:
return 'Tier 1 - High Value'
elif val >= clv_percentiles[0.80]:
return 'Tier 2 - Mid-High Value'
elif val >= clv_percentiles[0.50]:
return 'Tier 3 - Mid Value'
else:
return 'Tier 4 - Low Value'
summary['clv_tier'] = summary['clv'].apply(clv_tier)
return summary.reset_index()
# ============================================================
# 3. Cohort Analysis
# ============================================================
def cohort_retention_analysis(
transactions_df: pd.DataFrame,
customer_id_col: str = 'customer_id',
date_col: str = 'transaction_date'
) -> pd.DataFrame:
"""
Compute monthly retention rates by acquisition cohort.
Shows what percentage of each monthly cohort is still active M months later.
"""
df = transactions_df.copy()
df[date_col] = pd.to_datetime(df[date_col])
df['cohort_month'] = df.groupby(customer_id_col)[date_col].transform('min').dt.to_period('M')
df['activity_month'] = df[date_col].dt.to_period('M')
df['months_since_first'] = (df['activity_month'] - df['cohort_month']).apply(lambda x: x.n)
# Count unique customers per cohort per month
cohort_sizes = df.groupby('cohort_month')[customer_id_col].nunique()
cohort_activity = df.groupby(['cohort_month', 'months_since_first'])[customer_id_col].nunique().reset_index()
cohort_activity.columns = ['cohort_month', 'months_since_first', 'n_active']
# Compute retention rate
cohort_activity = cohort_activity.merge(
cohort_sizes.rename('cohort_size').reset_index(),
on='cohort_month'
)
cohort_activity['retention_rate'] = cohort_activity['n_active'] / cohort_activity['cohort_size']
# Pivot for heatmap-style analysis
retention_matrix = cohort_activity.pivot(
index='cohort_month',
columns='months_since_first',
values='retention_rate'
)
return retention_matrix
# ============================================================
# 4. LSTM-Based CLV Prediction
# ============================================================
import torch
import torch.nn as nn
class PurchaseSequenceEncoder(nn.Module):
"""
LSTM-based model that encodes a customer's purchase history
and predicts next purchase timing and value.
"""
def __init__(
self,
input_dim: int = 8, # features per transaction
hidden_dim: int = 128,
num_layers: int = 2,
dropout: float = 0.2
):
super().__init__()
self.lstm = nn.LSTM(
input_size=input_dim,
hidden_size=hidden_dim,
num_layers=num_layers,
batch_first=True,
dropout=dropout
)
# Output heads
self.days_until_next = nn.Sequential(
nn.Linear(hidden_dim, 64),
nn.ReLU(),
nn.Linear(64, 1),
nn.Softplus() # Positive-only output
)
self.next_value = nn.Sequential(
nn.Linear(hidden_dim, 64),
nn.ReLU(),
nn.Linear(64, 1),
nn.Softplus()
)
self.churn_prob = nn.Sequential(
nn.Linear(hidden_dim, 32),
nn.ReLU(),
nn.Linear(32, 1),
nn.Sigmoid()
)
def forward(self, x: torch.Tensor, lengths: torch.Tensor = None):
"""
x: (batch, seq_len, input_dim) - purchase sequence
lengths: (batch,) - actual sequence lengths for padding
"""
if lengths is not None:
# Pack padded sequences for efficiency
x = nn.utils.rnn.pack_padded_sequence(
x, lengths.cpu(), batch_first=True, enforce_sorted=False
)
lstm_out, (hidden, _) = self.lstm(x)
if lengths is not None:
lstm_out, _ = nn.utils.rnn.pad_packed_sequence(lstm_out, batch_first=True)
# Use last valid hidden state
final_hidden = hidden[-1] # (batch, hidden_dim)
return {
'days_until_next': self.days_until_next(final_hidden).squeeze(-1),
'next_value': self.next_value(final_hidden).squeeze(-1),
'churn_probability': self.churn_prob(final_hidden).squeeze(-1),
}
def build_transaction_features(
customer_history: pd.DataFrame,
max_seq_len: int = 50
) -> torch.Tensor:
"""
Convert a customer's transaction history into a feature tensor.
Features per transaction:
- log(days_since_previous_purchase)
- log(transaction_value + 1)
- log(cumulative_spend + 1)
- transaction_count_so_far (normalized)
- day_of_week (sin/cos encoded)
- month (sin/cos encoded)
"""
df = customer_history.sort_values('transaction_date').copy()
df['transaction_date'] = pd.to_datetime(df['transaction_date'])
df['days_delta'] = df['transaction_date'].diff().dt.days.fillna(0)
features = []
cumulative_spend = 0
for i, row in df.iterrows():
cumulative_spend += row['revenue']
dow = row['transaction_date'].dayofweek
month = row['transaction_date'].month
feat = [
np.log1p(row['days_delta']),
np.log1p(row['revenue']),
np.log1p(cumulative_spend),
len(features) / 100.0, # normalized transaction count
np.sin(2 * np.pi * dow / 7),
np.cos(2 * np.pi * dow / 7),
np.sin(2 * np.pi * month / 12),
np.cos(2 * np.pi * month / 12),
]
features.append(feat)
# Pad or truncate to max_seq_len
if len(features) > max_seq_len:
features = features[-max_seq_len:] # Keep most recent
elif len(features) < max_seq_len:
pad = [[0.0] * 8] * (max_seq_len - len(features))
features = pad + features
return torch.tensor(features, dtype=torch.float32)
# ============================================================
# 5. CLV-Based Marketing Allocation
# ============================================================
def compute_marketing_allocation(
customer_clv_df: pd.DataFrame,
total_retention_budget: float,
retention_cost_per_customer: dict = None
) -> pd.DataFrame:
"""
Allocate retention marketing budget based on CLV tiers.
Customers with higher CLV get higher retention investment.
customer_clv_df: must have columns: customer_id, clv, clv_tier
retention_cost_per_customer: dict mapping clv_tier to max retention spend
"""
if retention_cost_per_customer is None:
# Default: max retention investment = 10% of expected CLV
retention_cost_per_customer = {
'Tier 1 - High Value': 0.12, # 12% of CLV
'Tier 2 - Mid-High Value': 0.08,
'Tier 3 - Mid Value': 0.04,
'Tier 4 - Low Value': 0.01,
}
df = customer_clv_df.copy()
df['max_retention_spend'] = df.apply(
lambda r: r['clv'] * retention_cost_per_customer.get(r['clv_tier'], 0.05),
axis=1
)
# Allocate budget proportionally to max_retention_spend
total_max = df['max_retention_spend'].sum()
df['budget_allocation'] = (df['max_retention_spend'] / total_max) * total_retention_budget
# ROI estimate: if retention increases repeat purchase rate by 5%
df['expected_retention_roi'] = df['clv'] * 0.05 # incremental CLV from 5% better retention
df['roi_ratio'] = df['expected_retention_roi'] / df['budget_allocation'].clip(lower=1)
return df.sort_values('clv', ascending=False)
Architecture Diagrams
CLV Prediction Pipeline
Customer Lifecycle and CLV Stages
Production Engineering Notes
Model Validation for CLV
CLV models are hard to validate because the true CLV is not observed for years. Validate using:
Calibration/holdout split: Use transactions from months 1-12 to fit the model, then evaluate on months 13-18. Did predicted transaction counts match actual counts in the holdout period? Use MAE and MAPE per customer.
Segment-level calibration: The model should not just be accurate on average - it should be accurate across the CLV distribution. Check: does the top decile of predicted CLV actually generate top-decile revenue in the holdout? If your top predicted CLV customers are generating mid-tier actual revenue, your model has segmentation problems even if overall MAE is acceptable.
Stability over time: CLV predictions for a customer should change smoothly over time, not jump erratically. Large changes in predicted CLV for a customer between weekly model runs (more than 30%) are a warning sign of model instability.
The "Contractual" vs. "Noncontractual" Setting
BG/NBD is designed for the noncontractual setting: customers can churn at any time without notification. This is typical for e-commerce.
The contractual setting (subscriptions, memberships) is different: churn is observable (the subscription is cancelled or expires). For contractual customers, you know they are alive until they cancel. Use survival analysis (Cox proportional hazards, or Weibull accelerated failure time models) instead of BG/NBD.
Many retailers operate in a hybrid setting: a subscription loyalty program on top of a noncontractual purchase relationship. Model these separately.
Common Mistakes
:::danger Confusing Historical Spend with CLV CLV is future value, not historical value. A customer who spent 500 last year but has very low churn probability and increasing purchase frequency. Using historical spend as a proxy for CLV systematically over-invests in customers at the end of their lifecycle and under-invests in rising customers. Always use a forward-looking model. :::
:::danger Ignoring the Discount Rate A 100 today. A reasonable annual discount rate for retail CLV is 10-15%, reflecting the cost of capital and time value of money. Ignoring discounting overestimates long-term CLV (a 310 today at 10% discount rate). This matters most for decisions about acquisition costs: paying 300 but with discounting is $220 is a much tighter decision than it appears. :::
:::warning Using CLV for Real-Time Decisions Without Freshness Checks CLV models are typically retrained monthly or quarterly. A customer who was Tier 1 six months ago might have churned. Using stale CLV predictions in real-time decisions (prioritizing customer service queue, deciding whether to offer a discount) requires a freshness layer: augment the static CLV prediction with real-time signals (days since last login, current session depth, recent purchase recency). A customer with Tier 1 CLV and a real-time "dormant for 90 days" signal needs urgent retention action, not standard Tier 1 treatment. :::
Interview Questions and Answers
Q1: Explain the BG/NBD model and why it handles customer churn better than a simple active/inactive classification.
A: The BG/NBD (Beta-Geometric/Negative Binomial Distribution) model acknowledges that we never directly observe customer death - we only observe purchases and their absence. A customer who last bought 6 months ago could either be churned (dead) or simply in a long inter-purchase interval (alive but slow buyer). A binary classification ("no purchase in 90 days = churned") forces a hard threshold that ignores the statistical distribution of inter-purchase times. BG/NBD instead maintains a posterior probability of being alive: P(alive | recency, frequency, T). A customer with high historical frequency who went quiet recently gets a high alive probability (the gap is unusual but they have a strong prior of being active). A customer who always bought infrequently and last bought 6 months ago gets a lower alive probability (their gap is within their normal behavior). This probabilistic output is exactly what you need: multiply P(alive) by expected purchase rate for active customers to get expected future transactions. The result is a continuous, well-calibrated CLV prediction rather than a stepped function with arbitrary thresholds.
Q2: How do you validate a CLV model when the true CLV is not observed for years?
A: Use a calibration-holdout approach. Split customer history at a cutoff date: use transactions before the cutoff to fit the model, and use transactions after the cutoff (the holdout period) to evaluate. Evaluate at multiple horizons: 3-month predicted CLV should match 3-month holdout revenue, 6-month prediction should match 6-month holdout. Metrics: at the individual level, compute MAE and MAPE on transaction counts per customer. At the segment level, check if the top decile of predicted CLV generated the top decile of actual holdout revenue - this segment-level calibration is more actionable for business decisions. Also check bias: is the model systematically over-predicting CLV for any customer segment? Plot predicted vs actual CLV by tenure, acquisition channel, and product category. Systematic over-prediction for a segment (say, customers acquired via discount promotions) indicates the model needs segment-specific features or training.
Q3: When would you choose BG/NBD over a deep learning CLV model?
A: BG/NBD wins in three situations. First, sparse data: for customers with fewer than 5 transactions, there is not enough sequence information to train a meaningful LSTM. BG/NBD's population-level priors allow reasonable individual-level predictions even with 1-2 data points. Second, interpretability requirements: BG/NBD gives you explicit, interpretable outputs - probability of being alive, expected purchase rate, expected monetary value - that business stakeholders can understand and audit. "This customer has 73% probability of being alive and is expected to purchase 2.3 times in the next 6 months" is actionable. Third, stability: BG/NBD is a statistical model with known theoretical properties. It does not overfit to recent patterns and does not change dramatically between weekly retraining runs. Deep learning CLV wins when you have rich transaction histories (50+ purchases per customer), when you have contextual features beyond RFM (browsing behavior, marketing touchpoints, product categories), and when you need to capture complex temporal patterns (weekly seasonality, annual gift-buying cycles) that the stationary assumptions of BG/NBD miss.
Q4: How would you use CLV to make an acquisition bid decision for paid search?
A: The economic framework: you should bid up to CLV * margin_rate in total customer acquisition cost (CAC). If predicted CLV = 240. But total CAC includes all marketing touchpoints, not just paid search. If email marketing averages 240 - $10) / expected_paid_search_contribution. The ML refinement: CLV varies significantly by customer segment. Use predictive features available at acquisition time (search query, geographic market, landing page, device type, time of day) to predict the CLV of a customer who will result from a given click. Segment CLV models by acquisition characteristics. A customer acquired via "wedding dress near me" will have different CLV than one acquired via "cheap dress." Bid more aggressively for high-expected-CLV segments, less for low. In practice: train a CLV-at-acquisition model that predicts first-year CLV from acquisition context, use this to set segment-level bid multipliers in your DSP, and update the model quarterly as CLV outcomes are observed.
Q5: Describe how you would build a win-back campaign targeting churned customers using CLV modeling.
A: Win-back campaigns are high-cost interventions (personalized email, phone calls, discount offers) justified only for customers with high residual value potential. The framework: (1) Define "churned" for your business context - for most retailers, no purchase in 12+ months is a reasonable threshold for non-contractual customers. (2) Compute pre-churn CLV for all churned customers - what was their estimated CLV before they went inactive? High pre-churn CLV customers are better win-back candidates. (3) Predict reactivation probability using features: time since last purchase, pre-churn purchase frequency, category of last purchase, reason for churn if known (returned an order, had a customer service complaint), and current promotional offer sensitivity. Train a binary classifier on historical win-back campaigns. (4) Compute expected value of win-back: P(reactivation) * post-reactivation CLV - cost of win-back effort. Only contact customers where expected value is positive. (5) Personalize the win-back offer based on last purchase category and inferred reason for churn: a customer who left after a bad return experience gets an apology + free return shipping guarantee, not a discount. (6) A/B test offers: random 10% of selected customers get no win-back contact (control). Measure incremental reactivation rate and CLV of reactivated customers vs. control.
