# How long does a manager last? Survival analysis

Source: https://www.footballdatascience.co.uk/learn/how-long-does-a-manager-last
Published: 2026-10-05

> Half of Scottish top-flight managers are gone within 465 days. Getting that number right means counting the managers who haven't left yet, which is what survival analysis is for. It also shows the danger never fades, and that a bad start matters less than fans think.

**On the terraces:** Fans say a new manager gets about a year. That's close: half are gone in about 15 months. This piece shows how to measure it fairly when some managers are still in the job, and checks whether a bad start really decides it.

## The football question

**How long does a new manager really get?**

Every appointment comes with talk of a long-term project, and every sacking with talk of short-termism. The question sounds easy: list the managers, work out how long each lasted, take the average. But it hides a trap that catches people who analyse patients, machines and customers as well as managers, and the method built to get round it is called **survival analysis**.

## The concept

The trap is the managers who **haven't left yet**. Rangers' Danny Röhl was appointed in October 2025 and was still in the job when 2025/26 ended. How long did his tenure last? We don't know. We only know it lasted **at least** seven months.

A tenure like that is **censored**: we stopped watching before the ending. There are three ways it happens in this data:

- **Still in post** at the end of 2025/26.
- **The club was relegated.** The data covers the top flight only, so a manager taken down is out of sight from then on.
- **2010/11.** The one season with no record of managerial changes, so nothing is known about who left or arrived until the following summer.

Censored tenures can't be thrown away or treated as finished. Drop them and you lose the managers who were doing well enough to stay; count them as ending on the day we stopped watching and you cut every one of them short. Both make managers look shorter-lived than they are.

## A football example

Five made-up managers, appointed on the same day:

| Manager | Days watched | What happened |
|---|---|---|
| A | 100 | Sacked |
| B | 200 | Still in post |
| C | 300 | Sacked |
| D | 400 | Sacked |
| E | 500 | Still in post |

Averaging only the three who left gives 100, 300 and 400 days: a typical tenure of **300 days**. That ignores the fact that B and E were still going.

The **Kaplan-Meier** method works through the departures in order and asks, each time, what share of the managers **still being watched** survived it:

$$S(t) = \prod \Big(1 - \frac{\text{left}}{\text{in post}}\Big)$$

<div class="plain" markdown="1">
In plain football

- **S(t)** is the share still in post after t days. **∏** means multiply: one bracket for every day someone left, up to day t.
- **Day 100:** five in post, A leaves. 4 in 5 survive: **80%**.
- **Day 200:** B is still in the job when we stop watching. Nobody leaves, so the curve doesn't drop, but from here on B isn't counted.
- **Day 300:** three still watched (C, D and E), C leaves. 80% × 2/3 = **53%**.
- **Day 400:** two still watched, D leaves. 53% × 1/2 = **27%**.
- So half are gone **between day 300 and day 400**, not at 300. B counted for as long as we saw him, and E for all 500 days.
</div>

That curve, the share still in post against time in the job, is the **survival curve**.

## Testing it on real managers

The real data is every permanent manager appointed by a Scottish top-flight club from 2000/01 to 2025/26: **158 tenures**, compiled from the managerial-changes tables on Wikipedia's season pages, each with the source the page cites. Caretakers and interim managers are left out. **121** tenures ended; **37** are censored (23 by relegation, 7 by the missing 2010/11 records, 7 still in post). The list is published at [manager-tenures-spfl.csv](/static/data/manager-tenures-spfl.csv).

Averaging only the finished tenures gives a median of **366 days**. Kaplan-Meier, which uses all 158, gives **465 days** (luck range 363 to 568). Ignoring the managers who hadn't left knocks about 100 days, more than three months, off the answer.

<figure class="rank-chart">
<div role="img" aria-label="Share of Scottish top-flight managers still in post against years in the job. The real curve falls to 57% after one year, 30% after two, 17% after three and 3% after five; half are gone by 1.27 years. A smooth curve for one steady departure rate of 0.56 a year follows it closely.">

</div>
<figcaption>Kaplan-Meier survival curve for 158 managers (mint), and the curve one steady departure rate of 0.56 a year would draw (gold, dashed).</figcaption>
</figure>

| Time in the job | Still in post |
|---|---|
| 6 months | 84% |
| 1 year | 57% |
| 2 years | 30% |
| 3 years | 17% |
| 5 years | 3% |

Fewer than one manager in three reaches a third season, and fewer than one in five a fourth. The longest in the data is Derek McInnes at Aberdeen, 2,905 days from 2013 to 2021; the shortest permanent spell is Wilfried Nancy's 32 days at Celtic in 2025/26.

## Does the danger fade?

You might expect a manager who survives a year to be safer: he's proved himself. Survival analysis measures that with the **hazard**, the rate at which managers still in post leave:

$$\text{hazard} = \frac{\text{departures}}{\text{manager-years in post}}$$

<div class="plain" markdown="1">
In plain football

- **Manager-years** add up time in the job across everyone still there: ten managers for half a year each is five manager-years.
- **A hazard of 0.5** means one departure for every two years managers spend in post, between them.
</div>

<figure class="rank-chart">
<div role="img" aria-label="Departures per manager-year by year in the job: year 1 0.52, year 2 0.64, year 3 0.59, year 4 0.46, all close to one steady rate of 0.56.">

</div>
<figcaption>Departures per manager-year in each year of the job, with luck margins, against one steady rate of 0.56.</figcaption>
</figure>

It doesn't fade. Year 1 runs at **0.52**, year 2 at **0.64**, year 3 at **0.59** and year 4 at **0.46**, and every one is within luck of a single steady rate of **0.56** a year. A manager in his third season is in about as much danger as one in his first.

That is exactly the "no memory" of the [exponential distribution](/learn/exponential-distribution), the one that describes the wait for the next goal. A steady rate of 0.56 gives a median of ln 2 ÷ 0.56 = **1.24 years**, almost exactly the 1.27 the real curve shows, and the dashed curve in the first chart sits on top of the real one. In any year, a manager has about a **43%** chance of going (1 − e<sup>−0.56</sup>), however long he's been there.

## Does a bad start decide it?

The terraces say the first few weeks set the tone. To test that fairly, judge every manager at the same point, his **10th league game**, and only those who got there: a manager sacked after six games never had a 10th-game record to judge. The clock then restarts from the 10th game. Statisticians call this a **landmark**: without it, the managers who lasted long enough to have a good start would be credited with time they'd already survived.

**147** managers reached their 10th league game.

- **Under a point a game in the first ten:** 43 managers, a median of **284** more days, against **427** for the other 104. That looks like a big gap, but the **log-rank test**, which compares how often each group left with how often it would if both had the same hazard, puts it at **p = 0.10**: the sort of gap luck alone produces one time in ten. Suggestive, not proven.
- **Below the club's own previous season:** a point a game means something different at Celtic and at a side just promoted, so compare like with like. Measured against what the club managed the season before, the 70 managers who started worse lasted a median of **329** more days against **427**, and **p = 0.33**: well within luck.

So a bad start is not the death sentence it's made out to be. Part of what looks like a bad-start effect is which clubs start badly. The same caution as the [new-manager bounce](/myth-or-maths/new-manager-bounce) applies: a run of results means less than it seems once you compare like with like.

### Do Old Firm managers last longer?

They seem to: a median of **635 days** at Celtic and Rangers against **414** elsewhere. But there are only 21 Old Firm tenures, and the log-rank test gives **p = 0.32**. The gap could easily be luck, and the data can't separate the two.

### How they leave

Of the 121 who left, **75** were sacked or left by mutual consent, **25** resigned and **17** left for another job; three saw their contract run out and one moved upstairs to director of football. For more than six in ten, the end of the curve is a sacking.

## Why it matters

Survival analysis is how medicine measures how long treatments keep patients well, how engineers estimate when parts fail, and how businesses work out how long customers stay. The football version is the same three ideas: **censoring**, so unfinished stories still count; the **survival curve**, the share still going at each point; and the **hazard**, whether the danger rises, falls or stays put. Whenever a question starts "how long until ...", and some of the clocks are still running, this is the method.

## Limitations

- **Relegation isn't neutral.** A manager taken down is often sacked that summer, so stopping the clock at relegation flatters the curve a little. The data can't follow clubs outside the top flight.
- **Compiled from Wikipedia.** The season tables cite BBC and club sources for most rows but nothing for some early ones; those rows link the season page instead. Three interim managers made permanent without a row of their own (Steven Naismith, Don Cowie, David Gray) were added from the clubs' announcements.
- **Who's left out.** Caretakers, interim spells and Martin O'Neill's "until the end of the season" spell in 2026; managers who arrived with a promoted club, whose start date is in the lower league; and anyone whose start fell in 2010/11.
- **Leaving isn't all alike.** Being sacked and being poached by a bigger club both end the curve. Separating them, "competing risks", needs more departures than 121.
- **A test is not a cause.** A log-rank p of 0.10 for a bad start says the gap isn't proven, not that it doesn't exist.

## Reproduce the analysis

Save [manager-tenures-spfl.csv](/static/data/manager-tenures-spfl.csv) and the Premiership results files (SC0) from football-data.co.uk, which the [download script](/data#get-the-files) fetches under the names the snippet expects, in one folder. It needs only Python 3 and takes about ten seconds:

```python
# From Football Data Science by Bryan McGuire. Free to use with credit.
# https://www.footballdatascience.co.uk/learn/how-long-does-a-manager-last
import csv
import random
from collections import Counter
from datetime import date, datetime
from math import erfc, log, sqrt
from statistics import mean, median

rnd = random.Random(2026)  # fixed, so the luck ranges repeat exactly
YEAR = 365.25
tenures = []
with open("manager-tenures-spfl.csv", encoding="utf-8") as f:
    for r in csv.DictReader(f):
        start = date.fromisoformat(r["appointed"])
        tenures.append({"club": r["club"], "start": start, "how": r["how"], "left": r["left"] == "1",
                        "days": (date.fromisoformat(r["until"]) - start).days})

def survival(group):  # Kaplan-Meier: at each departure, the share of managers still in post who survive it
    still, curve = 1.0, []
    for day in sorted({t["days"] for t in group if t["left"]}):
        at_risk = sum(t["days"] >= day for t in group)
        gone = sum(t["days"] == day and t["left"] for t in group)
        still *= 1 - gone / at_risk
        curve.append((day, still))
    return curve

def still_in_post(curve, day):
    return next((s for d, s in reversed(curve) if d <= day), 1.0)

def median_days(group):
    return next((d for d, s in survival(group) if s <= 0.5), None)

def log_rank(a, b):  # are two groups' departure rates different by more than luck? returns observed, expected, p
    seen = expected = var = 0
    for day in sorted({t["days"] for t in a + b if t["left"]}):
        na, nb = sum(t["days"] >= day for t in a), sum(t["days"] >= day for t in b)
        da = sum(t["days"] == day and t["left"] for t in a)
        d = da + sum(t["days"] == day and t["left"] for t in b)
        n = na + nb
        if n > 1:
            seen, expected = seen + da, expected + d * na / n
            var += d * (na / n) * (nb / n) * (n - d) / (n - 1)
    return seen, expected, erfc(sqrt((seen - expected) ** 2 / var / 2))

print(f"{len(tenures)} tenures, {sum(t['left'] for t in tenures)} ended; still running when we stopped watching:")
for how, n in Counter(t["how"] for t in tenures if not t["left"]).most_common():
    print(f"  {how}: {n}")

# 1. the wrong way: average only the tenures that have finished
done = [t["days"] for t in tenures if t["left"]]
print(f"\nfinished tenures only: mean {mean(done):.0f} days, median {median(done):.0f}")

# 2. the right way: Kaplan-Meier, which uses every tenure for as long as we watched it
curve = survival(tenures)
boots = sorted(median_days([tenures[rnd.randrange(len(tenures))] for _ in tenures]) for _ in range(1000))
print(f"Kaplan-Meier median {median_days(tenures)} days ({median_days(tenures) / YEAR:.2f} years), "
      f"luck range {boots[25]} to {boots[975]}")
for years in (0.5, 1, 2, 3, 5):
    print(f"  still in post after {years} years: {still_in_post(curve, years * YEAR):.0%}")

# 3. the hazard: departures per manager-year, year by year in the job
for y in range(4):
    a, b = y * YEAR, (y + 1) * YEAR
    gone = sum(a <= t["days"] < b and t["left"] for t in tenures)
    years = sum(max(0, min(t["days"], b) - a) for t in tenures) / YEAR
    print(f"  year {y + 1}: {sum(t['days'] >= a for t in tenures)} in post at the start, {gone} left, "
          f"{gone / years:.2f} ± {2 * sqrt(gone) / years:.2f} a year")
rate = sum(t["left"] for t in tenures) / (sum(t["days"] for t in tenures) / YEAR)
print(f"one steady rate for everyone: {rate:.2f} a year, so a median of ln 2 / {rate:.2f} = {log(2) / rate:.2f} years")

# 4. does a bad start shorten it? judged at the 10th league game, the clock restarting there
games, season_ppg = [], {}
for y in range(2000, 2026):
    pts, played = Counter(), Counter()
    with open(f"SC0_{y % 100:02d}{(y + 1) % 100:02d}.csv", encoding="latin-1") as f:
        for r in csv.DictReader(f):
            if r.get("FTR") not in ("H", "D", "A"):
                continue
            d = datetime.strptime(r["Date"], "%d/%m/%Y" if len(r["Date"]) == 10 else "%d/%m/%y").date()
            home = {"H": 3, "D": 1, "A": 0}[r["FTR"]]
            games.append((d, r["HomeTeam"], r["AwayTeam"], home, 3 - home if home != 1 else 1))
            for team, p in ((r["HomeTeam"], home), (r["AwayTeam"], games[-1][4])):
                pts[team] += p
                played[team] += 1
    season_ppg.update({(team, y): pts[team] / played[team] for team in pts})
games.sort()
judged = []
for t in tenures:
    first = [(d, hp if t["club"] == h else ap) for d, h, a, hp, ap in games if d > t["start"] and t["club"] in (h, a)][:10]
    tenth = (first[-1][0] - t["start"]).days if len(first) == 10 else None
    if tenth is not None and tenth <= t["days"]:
        season = t["start"].year if t["start"].month >= 6 else t["start"].year - 1
        judged.append({**t, "days": t["days"] - tenth, "start_ppg": sum(p for _, p in first) / 10,
                       "last_season": season_ppg.get((t["club"], season - 1))})
print(f"\n{len(judged)} managers reached their 10th league game")
for label, bad in (("under a point a game", lambda t: t["start_ppg"] < 1),
                   ("below the club's own last season", lambda t: t["start_ppg"] < t["last_season"])):
    group = [t for t in judged if t["last_season"] is not None] if "own" in label else judged
    a, b = [t for t in group if bad(t)], [t for t in group if not bad(t)]
    seen, expected, p = log_rank(a, b)
    print(f"  start {label}: {len(a)} managers, median {median_days(a)} more days; the rest {len(b)}, {median_days(b)}; "
          f"left {seen} v {expected:.1f} expected, p = {p:.2f}")

# 5. the Old Firm against the rest
old_firm = [t for t in tenures if t["club"] in ("Celtic", "Rangers")]
rest = [t for t in tenures if t["club"] not in ("Celtic", "Rangers")]
seen, expected, p = log_rank(old_firm, rest)
print(f"\nOld Firm {len(old_firm)} tenures, median {median_days(old_firm)} days; the rest {len(rest)}, "
      f"{median_days(rest)}; left {seen} v {expected:.1f} expected, p = {p:.2f}")

# 6. how they left
pushed = ("Sacked", "Mutual consent", "Contract terminated")
ways = Counter("sacked or mutual consent" if t["how"] in pushed else "resigned" if t["how"] == "Resigned"
               else "left for another job" if t["how"].startswith(("Signed by", "Joined")) else "other"
               for t in tenures if t["left"])
print("how they left: " + ", ".join(f"{k} {v}" for k, v in ways.most_common()))
```

## Further reading

- [Kaplan–Meier estimator](https://en.wikipedia.org/wiki/Kaplan%E2%80%93Meier_estimator), Wikipedia. The method, its history in medical research, and how the curve is built step by step.
- [Censoring (statistics)](https://en.wikipedia.org/wiki/Censoring_(statistics)), Wikipedia. The different ways a story can be cut off before its end, and why each matters.
- [Logrank test](https://en.wikipedia.org/wiki/Logrank_test), Wikipedia. The test used here to compare two survival curves.
