ds@mcpanalytics.aiEmail your data and your question and get an answer about your own data, with the method shown.Email your data and question. Get an answer.

Churn and customer lifetime

By MCP Analytics Team | Published , updated

Upload your data and get a churn and customer lifetime report. Free.

We email you the report link.

Free up to 10,000 rows. Encrypted in transit.

📊
-
Rows
-
Columns
-
Numeric

Running churn and customer lifetime analysis...

Running churn and customer lifetime analysis...

✓

Sent to . Inside: interactive charts, statistical results, R code, and AI insights.

Sample report

Open the full sample →

View all case studies → See all free tools →
Why an analysis, not a chat answer

Ask twice, get two answers? Watch how we fix that.

The method, in full

The practical guide behind this tool: when it applies, how to read the output, and the traps that make it say the wrong thing.

Read the guide →

Also in this set: What counts as starting and coming back in cohort retention

A simulated example, with the code and its output

The numbers below come from made-up data, made by the code on this page. They show the method. They are not output from this tool and not a finding about your data.

Question: If some customers have not left yet, how do you work out a monthly churn rate, and can you tell two plans apart?

Inputs: Simulated data, not real customers. Seed 1 makes 200 Basic customers for the main example. Each customer is watched for a random 1 to 12 months, so many have not left by the time we stop looking. Built in by construction: the true churn rate is 0.06 per month for Basic and 0.03 per month for Pro (so Pro customers truly leave half as fast), and churn times follow a steady-rate (exponential) pattern. The power checks use seeds 1000 to 1299 (spread of the rate) and 5000 to 5299 (the plan test), 300 seeds each, with 30 or 200 customers per plan. In the 'no difference' runs both plans are set to 0.06.

Method: Survival-style analysis, the method the page describes: time to churn, counting customers who are still active. Each customer has a time seen and a flag: 1 if they churned, 0 if they were still active when we stopped watching. A still-active customer is 'censored': we know they lasted at least that long, but not how much longer. Three things are computed. (1) The share who churned, which ignores censoring. (2) The monthly churn rate, which is churn events divided by total customer-months watched, so still-active customers count for the time they were seen. A bootstrap interval is made by re-drawing the 200 customers with replacement 1,000 times and recomputing the rate; the middle 95% of those rates is the interval. (3) The Kaplan-Meier curve, which gives the share still active after a given month by multiplying 'share who survived' at each churn month. To compare plans, a log-rank test asks whether two curves differ more than chance would allow; the p-value is the chance of a gap this big if the plans were really the same. Power is the share of seeds where the test gives p below 0.05.

Result: In seed 1, 0.235 of the 200 Basic customers had churned when we stopped looking, a figure that ignores customers still active. Counting churn events over the months watched gives 0.046 a month, with a bootstrap 95% interval of 0.033 to 0.059, just under the built-in 0.06. The Kaplan-Meier chance of still being active after 6 months is 0.757, against 0.698 built in, which shows how far one run of 200 can land from the truth. Across 300 seeds the middle 95% of the rate ran from 0.03 to 0.11 with 30 customers and from 0.047 to 0.076 with 200. The log-rank test found the Pro versus Basic difference in 0.23 of seeds with 30 per plan and 0.93 with 200 per plan. With no difference built in it flagged 0.06 and 0.05 of seeds, near the 0.05 expected. So 30 customers per plan usually cannot tell the plans apart even though the difference was built in.

Limit: The data has one steady churn rate per plan and nothing else, while real customers churn at different speeds over their lifetime and for reasons this example does not model. It also does not show region effects, more than two groups, or how the tool reports its results.

Python script (numpy and scipy only):

import numpy as np
from scipy.stats import chi2
# Simulated data. Built in: monthly churn rate (hazard) 0.06 for Basic, 0.03 for Pro.
H = {"Basic": 0.06, "Pro": 0.03}

def make(rng, n, h):
    t = rng.exponential(1 / h, n)          # true month of churn
    f = rng.uniform(1, 12, n)              # months we watched this customer
    return np.minimum(t, f), (t <= f).astype(int)   # time seen, 1 = churned, 0 = still active

def km(time, ev, at):                       # Kaplan-Meier: share still active at month `at`
    s = 1.0
    for u in np.unique(time[(ev == 1) & (time <= at)]):
        s *= 1 - ((time == u) & (ev == 1)).sum() / (time >= u).sum()
    return s

def logrank_p(t1, e1, t2, e2):
    t, e, g = np.r_[t1, t2], np.r_[e1, e2], np.r_[np.zeros(len(t1)), np.ones(len(t2))]
    O = V = 0.0
    for u in np.unique(t[e == 1]):
        r = t >= u; n = r.sum(); n1 = (r & (g == 0)).sum(); d = ((t == u) & (e == 1)).sum()
        O += ((t == u) & (e == 1) & (g == 0)).sum() - d * n1 / n
        V += d * (n1 / n) * (1 - n1 / n) * (n - d) / max(n - 1, 1)
    return chi2.sf(O * O / V, 1)

rng = np.random.default_rng(1)
t, e = make(rng, 200, 0.06)
print("Seed 1, 200 Basic customers, built-in monthly rate 0.060")
print("Share who churned (ignores still-active):", round(e.mean(), 3))
rate = e.sum() / t.sum()
bs = []
for _ in range(1000):
    i = rng.integers(0, 200, 200); bs.append(e[i].sum() / t[i].sum())
print("Churn rate per month, events / months watched:", round(rate, 3),
      "bootstrap 95% interval:", np.round(np.percentile(bs, [2.5, 97.5]), 3))
print("Active after 6 months (Kaplan-Meier):", round(km(t, e, 6), 3), "built-in:", round(np.exp(-0.36), 3))

for n in (30, 200):
    r = []
    for s in range(300):
        g = np.random.default_rng(1000 + s); a, b = make(g, n, 0.06); r.append(b.sum() / a.sum())
    print(f"n={n}: rate across 300 seeds, mean {np.mean(r):.3f}, 2.5%-97.5% {np.round(np.percentile(r,[2.5,97.5]),3)}")

for n in (30, 200):
    for lab, hb in (("Pro 0.03 vs Basic 0.06", 0.03), ("no difference 0.06 vs 0.06", 0.06)):
        hit = 0
        for s in range(300):
            g = np.random.default_rng(5000 + s)
            hit += logrank_p(*make(g, n, 0.06), *make(g, n, hb)) < 0.05
        print(f"Log-rank p<0.05, {n} per plan, {lab}: {hit/300:.2f} of 300 seeds")

Printed output from running the code above:

Seed 1, 200 Basic customers, built-in monthly rate 0.060
Share who churned (ignores still-active): 0.235
Churn rate per month, events / months watched: 0.046 bootstrap 95% interval: [0.033 0.059]
Active after 6 months (Kaplan-Meier): 0.757 built-in: 0.698
n=30: rate across 300 seeds, mean 0.061, 2.5%-97.5% [0.03 0.11]
n=200: rate across 300 seeds, mean 0.061, 2.5%-97.5% [0.047 0.076]
Log-rank p<0.05, 30 per plan, Pro 0.03 vs Basic 0.06: 0.23 of 300 seeds
Log-rank p<0.05, 30 per plan, no difference 0.06 vs 0.06: 0.06 of 300 seeds
Log-rank p<0.05, 200 per plan, Pro 0.03 vs Basic 0.06: 0.93 of 300 seeds
Log-rank p<0.05, 200 per plan, no difference 0.06 vs 0.06: 0.05 of 300 seeds

What's in the report?

How many customers churn, which plans or regions separate the ones who leave, and how long customers last, counting those who are still active.

📊

Statistical Results

Complete analysis with charts, tables, and diagnostics.

🤖

AI Insights

Plain-English reading of the results.

Questions?

See our FAQ for pricing, privacy and how the analysis works.

How to cite this tool

MCP Analytics. Churn and customer lifetime. Free tool. https://mcpanalytics.ai/free/standard_churn_v2. Add the date you accessed it.

Your data has more stories to tell

Run it on your data

Sign up to save datasets and schedule runs

Powered by MCP Analytics