What is RFM Analysis?
RFM analysis segments customers based on three behavioral dimensions: Recency (how recently), Frequency (how often), and Monetary (how much they spend).
The Three Dimensions
1. Recency (R) — How recently did customer make a purchase?
Customer A: Last order 2 days ago (HIGH recency)
Customer B: Last order 90 days ago (LOW recency)
Why it matters: Recent customers are more engaged (easier to retain, more responsive to marketing)
2. Frequency (F) — How often does customer purchase?
Customer A: 15 orders in last 6 months (HIGH frequency)
Customer B: 2 orders in last 6 months (LOW frequency)
Why it matters: Frequent customers are loyal (higher LTV, lower churn risk)
3. Monetary (M) — How much does customer spend?
Customer A: ₹15,000 total spend (HIGH monetary)
Customer B: ₹1,200 total spend (LOW monetary)
Why it matters: High-value customers drive revenue (should receive VIP treatment)
RFM Scoring
Each customer gets a score (1-5) for R, F, and M:
- 5 = Best (top 20%)
- 4 = Good (next 20%)
- 3 = Average (middle 20%)
- 2 = Below average (next 20%)
- 1 = Worst (bottom 20%)
Example:
Customer A: RFM = 5-5-5 (ordered yesterday, 15 orders, ₹15K spend) → "Champion"
Customer B: RFM = 1-1-2 (ordered 90 days ago, 2 orders, ₹1.2K) → "Lost Customer"
Customer C: RFM = 5-2-3 (ordered yesterday, 2 orders, ₹3K) → "New Customer"
Combined RFM Score: Concatenate or sum scores for segmentation.
- Champions: RFM = 5-5-5, 5-4-5, 5-5-4 (high on all dimensions)
- At Risk: RFM = 2-3-3, 2-2-4 (low recency, but previously valuable)
- Lost: RFM = 1-1-1, 1-1-2 (haven't ordered in months, low value)
Why RFM Analysis Matters
Without RFM Segmentation (Treat everyone equally):
Send 50% discount to ALL customers (1M users)
Cost: 1M × ₹500 avg discount = ₹50 Cr budget
Problem: Wasted discounts on:
- Champions (would've bought without discount)
- Lost customers (discount too small to win back)
With RFM Segmentation (Targeted campaigns):
Champions (100K, RFM 5-5-5): No discount (loyal, buy anyway) — Cost: ₹0
Loyal (200K, RFM 4-5-4): 10% discount (retain loyalty) — Cost: ₹1 Cr
At Risk (150K, RFM 2-3-3): 30% discount (re-engage) — Cost: ₹2.25 Cr
Lost (50K, RFM 1-1-1): 50% discount (win-back) — Cost: ₹1.25 Cr
New (100K, RFM 5-1-2): Free delivery (encourage 2nd order) — Cost: ₹40L
Total cost: ₹5 Cr (vs ₹50 Cr) — 10× more efficient
Better targeting → Higher ROI (lost customers respond to 50%, champions don't need discounts)
Real Example: Zomato RFM Segmentation
Goal: Personalize offers based on customer behavior.
RFM Calculation (6-month window):
WITH customer_rfm AS (
SELECT
customer_id,
DATEDIFF('day', MAX(order_date), CURRENT_DATE) AS recency_days,
COUNT(order_id) AS frequency,
SUM(order_value) AS monetary
FROM orders
WHERE order_date >= CURRENT_DATE - INTERVAL '6 months'
GROUP BY customer_id
)
SELECT
customer_id,
recency_days,
frequency,
monetary,
NTILE(5) OVER (ORDER BY recency_days ASC) AS R_score, -- Lower days = higher score
NTILE(5) OVER (ORDER BY frequency DESC) AS F_score,
NTILE(5) OVER (ORDER BY monetary DESC) AS M_score
FROM customer_rfm;Segments & Actions:
Champions (R=5, F=5, M=5): 50K users
- Strategy: VIP treatment (exclusive menu access, priority delivery)
- Campaign: "Thanks for being a Champion! 🏆 Free dessert on next order"
Loyal (R=4-5, F=4-5, M=4-5): 150K users
- Strategy: Retention (small perks, loyalty program)
- Campaign: "You've ordered 20× this month! Unlock Gold status (free delivery)"
At Risk (R=1-2, F=3-5, M=3-5): 80K users
- Strategy: Re-engagement (personalized offers)
- Campaign: "We miss you! 40% off your favorite restaurant (expires in 48hrs)"
Lost (R=1, F=1-2, M=1-2): 120K users
- Strategy: Win-back (aggressive discounts)
- Campaign: "Come back! 60% off + free delivery on first order back"
New Customers (R=5, F=1, M=2-3): 200K users
- Strategy: Onboarding (encourage repeat)
- Campaign: "Order again within 7 days → Get ₹150 off + free delivery"
Impact:
- 25% higher campaign response rate (vs generic campaigns)
- 15% increase in repeat orders (targeted new customer offers)
- ₹10 Cr saved annually (focused discounts on at-risk/lost, not champions)
RFM is like gym membership behavior. Champions = attend 5×/week, last visit yesterday (engaged). Loyal = attend 3×/week, last visit 3 days ago (consistent). At Risk = used to attend 4×/week, haven't been in 2 weeks (need nudge). Lost = paid for year, never attended (wasted membership). Gym should send retention offers to "At Risk" (free PT session), not "Champions" (already engaged).
RFM Analysis with SQL
Step 1: Calculate RFM Metrics
Schema:
orders (order_id, customer_id, order_date, order_value)Query:
WITH customer_metrics AS (
SELECT
customer_id,
MAX(order_date) AS last_order_date,
COUNT(DISTINCT order_id) AS frequency,
SUM(order_value) AS monetary_value
FROM orders
WHERE order_date >= CURRENT_DATE - INTERVAL '12 months' -- 12-month window
GROUP BY customer_id
),
rfm_calc AS (
SELECT
customer_id,
DATEDIFF('day', last_order_date, CURRENT_DATE) AS recency_days,
frequency,
monetary_value
FROM customer_metrics
)
SELECT * FROM rfm_calc;Output:
customer_id | recency_days | frequency | monetary_value
C001 | 2 | 18 | 15400
C002 | 87 | 3 | 2200
C003 | 5 | 25 | 28900
Step 2: Assign RFM Scores (Quintile-Based)
Quintile Scoring (5 = top 20%, 1 = bottom 20%):
WITH customer_metrics AS (
SELECT
customer_id,
DATEDIFF('day', MAX(order_date), CURRENT_DATE) AS recency_days,
COUNT(DISTINCT order_id) AS frequency,
SUM(order_value) AS monetary_value
FROM orders
WHERE order_date >= CURRENT_DATE - INTERVAL '12 months'
GROUP BY customer_id
),
rfm_scores AS (
SELECT
customer_id,
recency_days,
frequency,
monetary_value,
NTILE(5) OVER (ORDER BY recency_days ASC) AS R_score, -- Lower recency = better
NTILE(5) OVER (ORDER BY frequency DESC) AS F_score, -- Higher frequency = better
NTILE(5) OVER (ORDER BY monetary_value DESC) AS M_score -- Higher spend = better
FROM customer_metrics
)
SELECT
customer_id,
recency_days,
frequency,
monetary_value,
R_score,
F_score,
M_score,
CONCAT(R_score, '-', F_score, '-', M_score) AS RFM_segment
FROM rfm_scores;Output:
customer_id | recency | frequency | monetary | R | F | M | RFM_segment
C001 | 2 | 18 | 15400 | 5 | 5 | 5 | 5-5-5 (Champion)
C002 | 87 | 3 | 2200 | 1 | 2 | 2 | 1-2-2 (Lost)
C003 | 5 | 25 | 28900 | 5 | 5 | 5 | 5-5-5 (Champion)
C004 | 45 | 12 | 8500 | 3 | 4 | 4 | 3-4-4 (Loyal)
Step 3: Create Named Segments
Map RFM scores to business segments:
WITH rfm_scores AS (
-- (Previous RFM scoring query)
...
),
rfm_segments AS (
SELECT
customer_id,
R_score,
F_score,
M_score,
CASE
-- Champions (best on all dimensions)
WHEN R_score >= 4 AND F_score >= 4 AND M_score >= 4 THEN 'Champions'
-- Loyal (high frequency + monetary, any recency)
WHEN F_score >= 4 AND M_score >= 4 THEN 'Loyal Customers'
-- Potential Loyalists (recent + high spend, but low frequency)
WHEN R_score >= 4 AND M_score >= 3 AND F_score <= 2 THEN 'Potential Loyalists'
-- New Customers (very recent, low frequency)
WHEN R_score >= 4 AND F_score <= 2 THEN 'New Customers'
-- At Risk (low recency, but previously valuable)
WHEN R_score <= 2 AND F_score >= 3 AND M_score >= 3 THEN 'At Risk'
-- Hibernating (low recency + low frequency, but spent money)
WHEN R_score <= 2 AND F_score <= 2 AND M_score >= 3 THEN 'Hibernating'
-- Lost (low on all dimensions)
WHEN R_score <= 2 AND F_score <= 2 AND M_score <= 2 THEN 'Lost'
-- About to Sleep (recent, but low frequency + low spend)
WHEN R_score >= 3 AND F_score <= 2 AND M_score <= 2 THEN 'About to Sleep'
-- Need Attention (below average, not lost yet)
ELSE 'Need Attention'
END AS segment
FROM rfm_scores
)
SELECT
segment,
COUNT(*) AS customer_count,
ROUND(AVG(R_score), 1) AS avg_R,
ROUND(AVG(F_score), 1) AS avg_F,
ROUND(AVG(M_score), 1) AS avg_M
FROM rfm_segments
GROUP BY segment
ORDER BY customer_count DESC;Output:
segment | customer_count | avg_R | avg_F | avg_M
Champions | 45000 | 4.8 | 4.7 | 4.9
Loyal Customers | 120000 | 3.2 | 4.5 | 4.6
New Customers | 180000 | 4.9 | 1.2 | 2.8
At Risk | 75000 | 1.8 | 3.9 | 4.1
Lost | 95000 | 1.2 | 1.5 | 1.8
...
⚠️ CheckpointQuiz error: Missing or invalid options array
RFM Segmentation Strategies
Segment-Specific Campaigns
| Segment | Definition | Size | Strategy | Campaign Example | |---------|------------|------|----------|------------------| | Champions | R≥4, F≥4, M≥4 | 5-10% | Reward loyalty, VIP treatment | "Exclusive: Early access to sale + free shipping" | | Loyal | F≥4, M≥4 | 15-20% | Retain with perks | "You're Gold tier! Unlock 10% cashback" | | Potential Loyalists | R≥4, M≥3, F≤2 | 10-15% | Increase frequency | "Subscribe & Save 15% on every order" | | New Customers | R≥4, F=1 | 20-25% | Encourage 2nd order | "First-time discount used! 20% off order #2" | | Promising | R≥3, F=2-3, M=2-3 | 10-15% | Upsell, cross-sell | "Complete the look — 15% off accessories" | | Need Attention | R=3, F=2-3, M=2-3 | 10-15% | Re-engage with offers | "We noticed you haven't visited — 25% off" | | At Risk | R≤2, F≥3, M≥3 | 5-10% | Win-back (personalized) | "Your favorites miss you! 40% off [top category]" | | Hibernating | R≤2, F≤2, M≥3 | 5-10% | Aggressive win-back | "Exclusive comeback offer — 50% off + free delivery" | | Lost | R≤2, F≤2, M≤2 | 10-15% | Low-effort win-back | "We want you back — 60% off sitewide (3 days only)" |
Example: Flipkart's RFM Campaign Strategy
Champions (5-5-5, 5-4-5): 500K users
Strategy: Maximize LTV (upsell, cross-sell)
Campaign:
- "Flipkart Plus Super" tier (free delivery forever)
- Early access to Big Billion Days sale
- Exclusive product drops (OnePlus phone pre-order)
Cost per user: ₹200 (perks)
Expected ROI: 5× (Champions spend ₹50K/year, perks cost ₹200 → net ₹49.8K)
Loyal (4-5-4, 4-4-4): 1.2M users
Strategy: Retain loyalty (prevent churn to Amazon)
Campaign:
- 10% cashback on electronics (top category for this segment)
- "You've saved ₹5,000 with us this year!" (reinforce value)
Cost per user: ₹150
Expected ROI: 3× (Loyal spend ₹18K/year, cost ₹150 → net ₹17.85K)
New Customers (5-1-2): 2M users
Strategy: Encourage 2nd order (convert to Loyal)
Campaign:
- "Order again in 7 days → ₹200 off + free delivery"
- Personalized product recommendations (based on 1st order)
Cost per user: ₹100
Expected ROI: 2× (40% convert to 2nd order → ₹2.5K avg 2nd order → net LTV ₹3K)
At Risk (2-3-4, 1-4-4): 600K users
Strategy: Re-engage before lost (time-sensitive offers)
Campaign:
- "Flash sale just for you — 40% off [favorite category]"
- "Your cart misses you! Complete checkout → Extra 10% off"
Cost per user: ₹300 (higher discount to win back)
Expected ROI: 1.5× (30% respond → ₹4K order → net LTV ₹2.5K over 6 months)
Lost (1-1-1, 1-1-2): 1.5M users
Strategy: Minimal investment (low response rate expected)
Campaign:
- "One last offer — 60% off + ₹500 cashback (expires 48hrs)"
- Generic email (no personalization, low cost)
Cost per user: ₹50 (email only, no premium channels)
Expected ROI: 0.8× (10% respond → ₹2K order → break-even to slight loss, but some revive)
Total Campaign Budget:
Champions: 500K × ₹200 = ₹10 Cr
Loyal: 1.2M × ₹150 = ₹18 Cr
New: 2M × ₹100 = ₹20 Cr
At Risk: 600K × ₹300 = ₹18 Cr
Lost: 1.5M × ₹50 = ₹7.5 Cr
Total: ₹73.5 Cr
Expected Return: ₹220 Cr (3× ROI) — targeted campaigns vs generic ₹100 Cr spend (1.5× ROI)
Advanced RFM Techniques
1. Weighted RFM Scoring
Problem: Equal weights (R, F, M) might not reflect business priorities.
Solution: Assign custom weights based on business goals.
Example — PhonePe (Fintech):
Recency: 50% weight (active users = engaged)
Frequency: 30% weight (transaction frequency matters)
Monetary: 20% weight (transaction value less important for digital payments)
Weighted RFM = (0.5 × R) + (0.3 × F) + (0.2 × M)
Customer A: R=5, F=4, M=3 → (0.5×5) + (0.3×4) + (0.2×3) = 2.5 + 1.2 + 0.6 = 4.3
Customer B: R=3, F=5, M=5 → (0.5×3) + (0.3×5) + (0.2×5) = 1.5 + 1.5 + 1.0 = 4.0
Customer A ranked higher (recency weighted more)
Use Case: Adjust weights per industry — SaaS prioritizes recency (churn risk), e-commerce prioritizes monetary (revenue focus).
2. RFM + Demographic Segmentation
Combine RFM with demographics (age, location, device) for hyper-targeting.
Example — Myntra: Champions in Tier 2 Cities:
WITH rfm_scores AS (
-- (RFM calculation)
...
),
segmented AS (
SELECT
r.customer_id,
r.segment,
u.city_tier,
u.age_group,
r.R_score,
r.F_score,
r.M_score
FROM rfm_scores r
JOIN users u ON r.customer_id = u.user_id
WHERE r.segment = 'Champions'
)
SELECT
city_tier,
COUNT(*) AS champions_count,
AVG(monetary_value) AS avg_spend
FROM segmented
GROUP BY city_tier;Output:
city_tier | champions_count | avg_spend
Tier 1 | 35000 | ₹18,500
Tier 2 | 12000 | ₹14,200
Tier 3 | 3000 | ₹9,800
Insight: Tier 1 champions spend 30% more — focus high-value campaigns on Tier 1
Campaign: "Tier 1 Champions — Exclusive luxury brand drop (Gucci, Prada) — free styling session"
3. RFM Migration Analysis
Track how customers move between segments (month-over-month).
Query:
WITH rfm_jan AS (
SELECT customer_id, segment AS segment_jan
FROM rfm_analysis
WHERE analysis_date = '2026-01-31'
),
rfm_feb AS (
SELECT customer_id, segment AS segment_feb
FROM rfm_analysis
WHERE analysis_date = '2026-02-28'
)
SELECT
r_jan.segment_jan AS from_segment,
r_feb.segment_feb AS to_segment,
COUNT(*) AS customer_count
FROM rfm_jan r_jan
JOIN rfm_feb r_feb ON r_jan.customer_id = r_feb.customer_id
WHERE r_jan.segment_jan != r_feb.segment_feb -- Only migrations
GROUP BY from_segment, to_segment
ORDER BY customer_count DESC;Output:
from_segment | to_segment | customer_count
Loyal | At Risk | 12000 (WARNING: Loyal customers churning)
Promising | Loyal | 8500 (GOOD: Moving up)
At Risk | Lost | 6200 (BAD: Losing at-risk customers)
New Customers | Promising | 15000 (GOOD: New → Engaged)
Actions:
- Investigate Loyal → At Risk (12K migrations) — Why are loyal customers disengaging?
- Accelerate New → Promising (15K migrations) — Onboarding working, double down
- Prevent At Risk → Lost (6.2K migrations) — More aggressive win-back for At Risk
4. RFM Prediction (Next-Best Action)
Use RFM to predict next purchase and trigger campaigns.
Example — Swiggy: Predict Churn:
Customer RFM trend:
Month 1: 5-5-5 (Champion)
Month 2: 4-5-5 (Loyal)
Month 3: 3-4-4 (Need Attention)
Month 4: 2-3-3 (At Risk)
Pattern: R declining (5 → 2 over 4 months) = churn risk
Automated Trigger (Month 4):
- Recency drops to 2 → Send "We miss you! 40% off" within 24 hours
- Frequency drops from 5 → 3 → Offer "Order 2× this week → Unlock Gold status"
Result: 35% of triggered customers re-engage (vs 15% without trigger)
⚠️ FinalQuiz error: Missing or invalid questions array
⚠️ SummarySection error: Missing or invalid items array
Received: {"hasItems":false,"isArray":false}