Apa Itu Cohort Analysis dan Retention?
Cohort analysis adalah teknik mengelompokkan pengguna berdasarkan karakteristik yang sama pada periode tertentu — paling umum, kapan mereka pertama kali melakukan transaksi atau mendaftar. Dengan cohort, kita bisa membandingkan perilaku kelompok yang berbeda secara "apple to apple", tanpa noise dari pertumbuhan user baru yang terus masuk.
Retention rate mengukur berapa persen dari cohort tersebut yang tetap aktif (melakukan pembelian, login, atau aksi yang relevan) pada periode berikutnya. Ini adalah metrik kesehatan produk paling jujur: akuisisi bisa dipalsukan dengan iklan, tapi retensi tidak bisa berbohong.
Retention M1 (bulan 2 setelah join): 20–40% dianggap baik
Retention M3: 15–25%
Retention M6+: 10–20%
Angka di bawah ini sinyal churn yang memerlukan perhatian produk.
Dataset Latihan
Kita akan menggunakan tabel orders sederhana dengan data transaksi e-commerce.
Semua query menggunakan standard SQL yang kompatibel dengan MySQL 8.0+ dan
PostgreSQL 14+.
CREATE TABLE orders ( id BIGINT PRIMARY KEY, user_id INT NOT NULL, order_date DATE NOT NULL, amount DECIMAL(10,2) NOT NULL ); -- Contoh data (1000 user, 3 tahun transaksi) -- user_id 1: cohort Jan 2023, aktif 6 bulan -- user_id 2: cohort Jan 2023, hanya beli sekali -- user_id 3: cohort Feb 2023, aktif 12 bulan -- dst... -- Di SQL Lab BASEGROUND, jalankan: -- SELECT * FROM orders LIMIT 10;
Langkah 1: Menentukan Cohort Bulanan
Cohort bulan adalah bulan pertama kali seorang user melakukan order. Kita cari tanggal order minimum per user, lalu truncate ke awal bulan.
WITH cohort AS ( SELECT user_id, DATE_FORMAT(MIN(order_date), '%Y-%m-01') AS cohort_month -- PostgreSQL: DATE_TRUNC('month', MIN(order_date))::DATE FROM orders GROUP BY user_id ) SELECT cohort_month, COUNT(*) AS cohort_size FROM cohort GROUP BY cohort_month ORDER BY cohort_month;
Hasil yang diharapkan:
| cohort_month | cohort_size |
|---|---|
| 2023-01-01 | 142 |
| 2023-02-01 | 118 |
| 2023-03-01 | 135 |
| 2023-04-01 | 127 |
| ... | ... |
Langkah 2: Tabel Aktivitas per Periode
Setelah mengetahui cohort setiap user, kita perlu tahu di bulan mana saja mereka aktif (melakukan order). Kita join data order dengan data cohort, lalu hitung month_number — jarak bulan antara bulan aktif dan bulan cohort.
WITH cohort AS ( SELECT user_id, DATE_FORMAT(MIN(order_date), '%Y-%m-01') AS cohort_month FROM orders GROUP BY user_id ), activity AS ( SELECT DISTINCT user_id, DATE_FORMAT(order_date, '%Y-%m-01') AS active_month FROM orders ) SELECT c.user_id, c.cohort_month, a.active_month, TIMESTAMPDIFF(MONTH, c.cohort_month, a.active_month) AS month_number -- PostgreSQL: EXTRACT(YEAR FROM AGE(a.active_month, c.cohort_month)) * 12 -- + EXTRACT(MONTH FROM AGE(a.active_month, c.cohort_month)) FROM cohort c JOIN activity a ON c.user_id = a.user_id ORDER BY c.user_id, month_number LIMIT 10;
| user_id | cohort_month | active_month | month_number |
|---|---|---|---|
| 1 | 2023-01-01 | 2023-01-01 | 0 |
| 1 | 2023-01-01 | 2023-02-01 | 1 |
| 1 | 2023-01-01 | 2023-04-01 | 3 |
| 2 | 2023-01-01 | 2023-01-01 | 0 |
| 3 | 2023-02-01 | 2023-02-01 | 0 |
| 3 | 2023-02-01 | 2023-03-01 | 1 |
Perhatikan user_id 1: dia aktif di M0, M1, M3 — namun tidak di M2. Artinya dia "hilang" sebulan lalu kembali. Ini informasi penting yang bisa dit-gali lebih lanjut.
Langkah 3: Menghitung Retention Rate
Kita gabungkan semua CTE sebelumnya untuk menghasilkan retention rate per cohort per month_number.
WITH cohort AS ( SELECT user_id, DATE_FORMAT(MIN(order_date), '%Y-%m-01') AS cohort_month FROM orders GROUP BY user_id ), activity AS ( SELECT DISTINCT user_id, DATE_FORMAT(order_date, '%Y-%m-01') AS active_month FROM orders ), cohort_activity AS ( SELECT c.cohort_month, TIMESTAMPDIFF(MONTH, c.cohort_month, a.active_month) AS month_number, COUNT(DISTINCT c.user_id) AS active_users FROM cohort c JOIN activity a ON c.user_id = a.user_id GROUP BY c.cohort_month, month_number ), cohort_size AS ( SELECT cohort_month, COUNT(*) AS total_users FROM cohort GROUP BY cohort_month ) SELECT ca.cohort_month, ca.month_number, ca.active_users, cs.total_users, ROUND(ca.active_users / cs.total_users * 100, 1) AS retention_pct FROM cohort_activity ca JOIN cohort_size cs ON ca.cohort_month = cs.cohort_month ORDER BY ca.cohort_month, ca.month_number;
Langkah 4: Pivot Retention Table
Data retention yang berguna untuk dibaca adalah format tabel pivot:
baris = cohort, kolom = M0, M1, M2, M3, ...
MySQL tidak punya PIVOT built-in, tapi kita bisa mensimulasikannya dengan
MAX(CASE WHEN ...).
WITH retention_raw AS ( -- ... salin query retention rate dari langkah 3 di sini ... -- atau gunakan sebagai subquery ) SELECT cohort_month, MAX(CASE WHEN month_number = 0 THEN CONCAT(retention_pct, '%') END) AS `M0`, MAX(CASE WHEN month_number = 1 THEN CONCAT(retention_pct, '%') END) AS `M1`, MAX(CASE WHEN month_number = 2 THEN CONCAT(retention_pct, '%') END) AS `M2`, MAX(CASE WHEN month_number = 3 THEN CONCAT(retention_pct, '%') END) AS `M3`, MAX(CASE WHEN month_number = 4 THEN CONCAT(retention_pct, '%') END) AS `M4`, MAX(CASE WHEN month_number = 5 THEN CONCAT(retention_pct, '%') END) AS `M5`, MAX(CASE WHEN month_number = 6 THEN CONCAT(retention_pct, '%') END) AS `M6` FROM retention_raw GROUP BY cohort_month ORDER BY cohort_month;
Contoh output yang dihasilkan:
| cohort_month | M0 | M1 | M2 | M3 | M4 | M5 | M6 |
|---|---|---|---|---|---|---|---|
| 2023-01-01 | 100% | 42.3% | 31.7% | 26.1% | 22.5% | 19.8% | 17.6% |
| 2023-02-01 | 100% | 38.1% | 28.8% | 24.4% | 20.3% | 18.6% | 16.1% |
| 2023-03-01 | 100% | 44.4% | 33.3% | 27.4% | 23.0% | 20.7% | — |
| ... | ... | ... | ... | ... | ... | ... | ... |
Funnel Analysis
Funnel mengukur konversi di setiap langkah alur pengguna: dari melihat produk → menambahkan ke keranjang → checkout → pembelian. Teknik ini menggunakan conditional COUNT DISTINCT dalam satu query.
-- Asumsi: tabel events (user_id, event_name, event_date) SELECT COUNT(DISTINCT CASE WHEN event_name = 'view_product' THEN user_id END) AS s1_lihat, COUNT(DISTINCT CASE WHEN event_name = 'add_to_cart' THEN user_id END) AS s2_keranjang, COUNT(DISTINCT CASE WHEN event_name = 'checkout' THEN user_id END) AS s3_checkout, COUNT(DISTINCT CASE WHEN event_name = 'purchase' THEN user_id END) AS s4_beli, ROUND( COUNT(DISTINCT CASE WHEN event_name = 'add_to_cart' THEN user_id END) * 100.0 / NULLIF(COUNT(DISTINCT CASE WHEN event_name = 'view_product' THEN user_id END), 0), 1 ) AS lihat_ke_keranjang_pct, ROUND( COUNT(DISTINCT CASE WHEN event_name = 'purchase' THEN user_id END) * 100.0 / NULLIF(COUNT(DISTINCT CASE WHEN event_name = 'view_product' THEN user_id END), 0), 1 ) AS konversi_total_pct FROM events WHERE event_date BETWEEN '2024-01-01' AND '2024-12-31';
NULLIF(divisor, 0) untuk menghindari division by zero error.
Hasilnya akan NULL jika tidak ada user di langkah pertama, yang lebih mudah
dideteksi daripada runtime error.
LTV (Customer Lifetime Value) per Cohort
LTV mengukur total pendapatan rata-rata yang dihasilkan satu user sepanjang hidupnya. Dianalisis per cohort, kita bisa melihat apakah cohort yang lebih baru menghasilkan LTV lebih tinggi atau lebih rendah — sinyal efektivitas produk atau akuisisi.
WITH cohort AS ( SELECT user_id, DATE_FORMAT(MIN(order_date), '%Y-%m-01') AS cohort_month FROM orders GROUP BY user_id ) SELECT c.cohort_month, COUNT(DISTINCT c.user_id) AS cohort_size, COUNT(o.id) AS total_orders, ROUND(SUM(o.amount), 2) AS total_revenue, ROUND(SUM(o.amount) / COUNT(DISTINCT c.user_id), 2) AS ltv_per_user, ROUND(AVG(o.amount), 2) AS avg_order_value, ROUND(COUNT(o.id) * 1.0 / COUNT(DISTINCT c.user_id), 1) AS avg_orders_per_user FROM cohort c JOIN orders o ON c.user_id = o.user_id GROUP BY c.cohort_month ORDER BY c.cohort_month;
| cohort_month | cohort_size | ltv_per_user | avg_order_value | avg_orders |
|---|---|---|---|---|
| 2023-01-01 | 142 | Rp 847.500 | Rp 185.200 | 4.6 |
| 2023-02-01 | 118 | Rp 792.300 | Rp 179.800 | 4.4 |
| 2023-03-01 | 135 | Rp 918.700 | Rp 192.600 | 4.8 |
| ... | ... | ... | ... | ... |
Latihan Mandiri
Lima soal latihan untuk memperdalam pemahaman. Gunakan dataset orders di SQL Lab:
Weekly Cohort Retention
Ubah analisis cohort dari bulanan menjadi mingguan. Gunakan
YEARWEEK(order_date) di MySQL atau DATE_TRUNC('week', ...) di PostgreSQL.
Hitung retention untuk 4 minggu pertama (W0, W1, W2, W3).
Cohort dengan Retention M3 Tertinggi
Dari hasil pivot retention table, temukan cohort mana yang memiliki retention rate tertinggi di bulan ke-3 (M3). Apa artinya jika cohort bulan tertentu memiliki M3 jauh di atas rata-rata?
LTV Kumulatif 6 Bulan Pertama
Modifikasi query LTV agar hanya menghitung order yang terjadi dalam 6 bulan pertama sejak cohort_month. Ini membuat perbandingan LTV antar cohort menjadi lebih adil (apple-to-apple).
Churn Rate Bulanan
Hitung churn rate sebagai 1 - retention_rate.
Buat query yang menampilkan churn rate per cohort per bulan dalam format yang sama dengan
pivot tabel retention. Cohort mana yang memiliki churn tertinggi di M1?
Funnel per Cohort
Jika tersedia tabel events, hitung conversion rate funnel view → purchase untuk setiap cohort. Apakah cohort yang lebih baru memiliki conversion rate yang lebih baik? Ini bisa menjadi indikator efektivitas perubahan UX.