Capture-Recapture Basics: The Lincoln-Petersen Estimator

Author

Deon Roos

Published

July 24, 2026

Starting simple

On the last page we sorted out two things. Counting animals gives you a count, not a population size. And a non-detection is ambiguous in a few annoying ways. That’s a lot to fix in one go, so we won’t. We’ll start with the simplest tool that exists and build up from there.

That tool is the Lincoln-Petersen estimator. Frederick Lincoln landed on it in 1930 and C.G.J. Petersen got there first back in 1896 (Petersen beat him by three decades and somehow Lincoln still gets his name on the tin). It uses two sampling occasions, leans on some fairly heroic assumptions, and hands you a single number: an estimate of population size \(N\).

It won’t give you survival. It won’t touch temporary emigration. Frankly it barely does anything. But it does the one thing we need right now, which is show you how recaptures tell you about the animals you missed. Get that idea straight here, where the maths still fits on one line, and the rest of the tutorial gets a lot easier.

The logic in plain English

Say you want to count the fish in a lake. You can’t drain it. You can’t see every fish. But you can still get at the number, and it goes like this.

On day one you go out and catch as many fish as you can. Tag every single one, chuck them back in. Let’s say you tagged 50, so \(n_1 = 50\).

Now you wait a few days. Long enough for your tagged fish to mix back in with everyone else, but not so long that fish are being born or dying in any real numbers. Then you go back out.

On day two you catch another batch. You count two things: how many you caught in total (\(n_2\)), and how many of those are already wearing your tags (\(m_2\), for “marked in the second sample”). Say you caught 40 and 10 of them were tagged, so \(n_2 = 40\) and \(m_2 = 10\).

Have a think about what that recapture rate is quietly telling you. You put 50 tagged fish into the lake. When you came back, 10 of the 40 you caught had tags. So about \(\frac{10}{40} = 25\%\) of the fish you bumped into on day two were ones you’d already marked.

If 25% of day two’s catch was tagged, and you only tagged 50 fish in the whole lake, then those 50 must be roughly 25% of every fish in there. Which gives you:

\[\hat{N} = \frac{50}{0.25} = 200 \text{ fish}\]

That’s the whole Lincoln-Petersen estimator. Think of your tags as a splash of dye poured into the lake. How watered down the dye looks when you scoop out your second sample tells you how big the lake must have been.

The equation

More formally, the estimator is:

\[\hat{N} = \frac{n_1 \times n_2}{m_2}\]

where:

  • \(n_1\) is the number caught and marked on the first occasion

  • \(n_2\) is the number caught on the second occasion

  • \(m_2\) is the number in the second sample that were already marked

  • \(\hat{N}\) is our estimate of total population size

Plugging in our numbers:

\[\hat{N} = \frac{50 \times 40}{10} = 200\]

Let’s check that in R:

Code
n1 <- 50  # Tagged and released on day 1
n2 <- 40  # Caught on day 2
m2 <- 10  # Already tagged in day 2 sample

N_hat <- (n1 * n2) / m2
N_hat
[1] 200

200 fish. Done. Well, nearly done. A single number with no sense of how much to trust it is only half an answer, so we need to talk about uncertainty as well.

Where does the equation come from?

I don’t want you to just memorise a formula and plug numbers into it. You’ll forget it by next week (fair enough) and, worse, you won’t spot it when it starts lying to you. So let’s see where it actually comes from, because underneath it’s genuinely one idea.

If your tagged fish have mixed back in evenly, then the fraction of the whole lake that’s tagged has to match the fraction of your second sample that’s tagged:

\[\frac{n_1}{N} = \frac{m_2}{n_2}\]

Read the left side as: out of every fish in the lake (\(N\)), the tagged fraction is \(\frac{n_1}{N}\).

Read the right side as: out of the fish you actually caught on day two, the tagged fraction is \(\frac{m_2}{n_2}\).

If the mixing was any good, those two fractions are the same thing. Now just rearrange to get \(N\) on its own:

\[N = \frac{n_1 \times n_2}{m_2}\]

And there’s your LP estimator. It’s cross-multiplication and nothing cleverer. The whole thing leans on one big assumption: a tagged fish is exactly as catchable as an untagged one, and both are shuffled evenly through the lake by the time you go back for round two.

Uncertainty

Reporting \(\hat{N} = 200\) and walking away would be a mistake. That number came out of a sample, and samples wobble. Go out and do the exact same thing tomorrow and you’d catch a slightly different number of tagged fish, which spits out a slightly different \(N\). So how much should you actually trust 200?

A commonly used approximation for the variance of the Lincoln-Petersen estimator is:

\[\widehat{Var}(\hat{N}) = \frac{n_1^2 \times n_2 \times (n_2 - m_2)}{m_2^3}\]

From that we can pull a standard error and a rough 95% confidence interval:

Code
var_N <- (n1^2 * n2 * (n2 - m2)) / m2^3
se_N <- sqrt(var_N)

ci_lower <- N_hat - 1.96 * se_N
ci_upper <- N_hat + 1.96 * se_N

cat("Estimate:", round(N_hat),
    "\nSE:", round(se_N, 1),
    "\n95% CI:", round(ci_lower), "to", round(ci_upper))
Estimate: 200 
SE: 54.8 
95% CI: 93 to 307

So our best guess is 200 fish, but the confidence interval is uncomfortably wide: somewhere from about 90 up to 300. That width is the estimator being honest with you about itself. The whole answer hangs on 10 recaptures, and 10 is not many. The fewer tagged fish you get back, the shakier the estimate.

That’s a genuinely important point for designing a study, not just a maths footnote. If you want a tight estimate of \(N\), you need to tag plenty of animals, or detect them well enough to get a decent pile of recaptures, and ideally both. We’ll come back to this when we start thinking about how to design a robust design study properly.

Have a play with the calculator below. The defaults match the worked example above. Drop \(m_2\), the number of recaptures, and watch the confidence interval balloon. Then try cranking \(n_1\) and \(n_2\) up together while keeping the recapture rate (\(m_2 / n_2\)) fixed, and see whether that reins it back in.

Let’s simulate it

One worked example is fine, but it can’t show you how jumpy this estimator really is. So let’s run the whole Lincoln-Petersen procedure again and again on the same lake, a thousand times over, and watch how much the answer bounces around even though the true population never changes.

Code
library(ggplot2)

set.seed(1234)

N_true <- 200
n1 <- 50
n2 <- 40
n_sims <- 1000

lp_estimates <- replicate(n_sims, {
  # Each fish in the population is tagged or not
  tagged <- c(rep(1, n1), rep(0, N_true - n1))
  
  # Sample n2 fish on day 2, without replacement
  second_sample <- sample(tagged, size = n2, replace = FALSE)
  m2_sim <- sum(second_sample)
  
  # Avoid division by zero in rare cases
  if (m2_sim == 0) return(NA)
  
  (n1 * n2) / m2_sim
})

lp_df <- data.frame(N_hat = lp_estimates[!is.na(lp_estimates)])

ggplot(lp_df, aes(x = N_hat)) +
  geom_histogram(fill = "#d19527", colour = "white", bins = 40, alpha = 0.8) +
  geom_vline(xintercept = N_true, linetype = "dashed",
             colour = "#FF5733", linewidth = 1) +
  annotate("text", x = N_true + 15, y = Inf, vjust = 2,
           label = "True N = 200", colour = "#FF5733") +
  labs(
    x = "Lincoln-Petersen estimate of N",
    y = "Frequency",
    title = paste0("Distribution of ", n_sims, " Lincoln-Petersen estimates"),
    subtitle = "True N = 200, n1 = 50, n2 = 40"
  ) +
  theme_dark_site()

Two things are worth clocking in that histogram. First, the pile of estimates sits roughly over the true value of 200. Good. That tells us the estimator isn’t systematically wrong; it doesn’t consistently guess high or consistently guess low. Second, look at that long tail dribbling off to the right. Every so often the procedure hands you a wildly huge \(\hat{N}\). That happens when \(m_2\) comes out small by pure bad luck: recapture only 2 or 3 tagged fish and the formula panics and returns some ridiculous number. It’s exactly why Lincoln-Petersen falls apart when recaptures are thin on the ground.

The assumptions, and why they matter

Lincoln-Petersen stands on four assumptions. Break any one of them and your estimate goes wonky, sometimes spectacularly.

1. The population is closed. Nothing is born, nothing dies, nothing moves in or out between your two visits. If animals are coming and going between day one and day two, then the \(N\) you estimate is for a population that never actually existed at any single moment in time.

2. Every animal is equally catchable. If some fish are shy, or your tagged ones learn that traps are bad news and start dodging them, then the recapture rate in your second sample stops speaking for the lake as a whole, and your estimate tips over.

3. Tagging doesn’t change the animal. If the tag stresses a fish into an early grave, or makes it swim about differently, the logic falls apart underneath you.

4. Tags stay on and get read correctly. If a tag drops off, or you squint and misread the number, an animal that should have counted as a recapture gets logged as a brand new capture instead. That quietly pushes \(\hat{N}\) upward.

You will never meet all four perfectly. Nobody does. The real question is always whether you’ve met them well enough that the number you end up with is still worth something.

Closure is the one we’ll keep circling back to across this whole website thing. It earns that attention because the entire robust design, the thing this whole site is building towards, is basically one clever way of dodging it: grabbing closure where you need it and openness where you don’t. But that’s a good few pages off yet.

What Lincoln-Petersen can’t do

For all its charm, there’s a short list of things this estimator simply can’t hand you.

It gives you a snapshot of \(N\) at one moment. It says nothing about whether \(N\) is climbing or crashing. It says nothing about survival. It can’t tell the difference between a population that’s stable and one that’s plummeting but happens to still be big today. Those two look identical to it.

It also demands closure, which you can only really defend over very short windows. Stretch your study across weeks or months and fish are being born and dying the whole time, and pretending the population is fixed and closed starts to look a bit silly.

And with only two occasions, you’re on thin ice trying to estimate detection reliably. If \(m_2\) comes out low because you simply had a rotten day in the field, rather than because detection is genuinely poor, you’ll walk away convinced there are far more fish than there really are.

So no, none of this is me trying to put you off Lincoln-Petersen. Every single one of these gripes is a door into the next method. The models on the pages that follow are, one way or another, someone getting fed up with exactly these limitations and doing something about them.

Where we are headed

Lincoln-Petersen gives us \(\hat{N}\), but only if we hold the population still with the closure assumption. If we also want survival, \(\phi\), we have to let go of closure and allow the population to be open, so that animals are allowed to die between our visits. And if we get greedy and want \(\hat{N}\) and \(\phi\) out of the same study, we’ll have to get sneakier about how we lay out our sampling in the first place.

That sneakiness has a name: the Cormack-Jolly-Seber model. It’s next.