# Latency with a cut-off time: how to analyse trials that end before the animal responds

> A latency recorded at the cut-off is censored, not a real value. Averaging it biases means and group differences. Compare groups with survival methods, report how many trials reached the cut-off, and keep trial-level data. Python and R code.

Canonical: https://www.licklider.ai/guides/latency-with-a-cut-off-time/
Section: Guides › Correlation, regression and survival
Author: Licklider · Published 2026-11-11 · Last reviewed 2026-11-11 (UTC)

> **Quick answer.** When a trial ends at the cut-off (for example 60 s) before the animal responds, the true latency is only known to be **longer than the cut-off**: the value is **censored**. Treating 60 s as a real latency understates how long those animals would have taken, so that group's mean comes out too small, and the difference between groups shrinks when the slower group has more trials at the cut-off (as is usual). Compare groups with **survival (time-to-event) methods**: Kaplan–Meier curves to describe the data, and a model that gives an effect size, such as a log-normal accelerated failure time (AFT) model, which reports how many times longer latencies are in one group than in the other; with small groups (about 12 per group), read its confidence interval rather than a p-value near 0.05 (an interval for the time ratio that excludes 1 supports a difference; its width shows how precisely the study measured it). Report how many trials reached the cut-off in each group. For trials repeated over days, keep every trial and use a model for repeated censored data rather than averaging first.

**Terms used below.** *Latency*: the time until the animal does something (finds a platform, steps through, falls). *Cut-off*: the maximum trial time. *Censored value*: a trial that ended at the cut-off, so the latency is known only to exceed it. *Event*: the animal responded before the cut-off (coded 1); censored trials are coded 0. *Survival methods*: methods for time-to-event data that use censored values correctly. *Kaplan–Meier curve*: the share of animals that have not yet responded, over time. *Log-rank test*: a test that compares Kaplan–Meier curves between groups. *Accelerated failure time (AFT) model*: a survival regression whose effect is a ratio of times (for example, latencies 2 times longer). *Log-normal*: a skewed distribution whose logarithm is normal, common for latencies. *Cox model*: a survival regression whose effect is a hazard ratio. *Hazard ratio*: the ratio of the two groups' instantaneous rates of responding; it is not a ratio of times. *Time ratio*: how many times longer latencies are in one group than in the other, the AFT model's effect. *Right-censored*: known only to exceed a value (here the cut-off). *95% confidence interval (CI)*: the range of effects compatible with the data. *Cluster*: a set of trials from the same animal, which the model treats as related. *Weibull*: another distribution used for latencies in AFT models. *Log-likelihood and AIC*: measures of how well a model fits, used to compare distributions (higher log-likelihood or lower AIC fits better). *Robust standard errors*: standard errors that allow trials from the same animal to be related. *lifelines*: a Python package for survival analysis. *Mann–Whitney test*: a rank-based comparison of two groups. *Power*: the chance of detecting an effect that is really there. *False positive*: a "significant" result when there is no real effect.

## Which analysis for your data

| Your data | Analysis | Report |
| --- | --- | --- |
| One trial per animal, some at the cut-off | Kaplan–Meier curves (description) and a log-normal or Weibull AFT model (effect size); a log-rank test if you only need a test | Number censored per group; Kaplan–Meier median if the curve reaches 50%, otherwise the share at the cut-off; time ratio with 95% CI |
| One trial per animal, none at the cut-off | Usual two-group methods, preferably on log latency | Mean or median with CI |
| Several trials per animal (acquisition days) | A model for repeated censored data, with animal as a cluster or random effect, for example `survreg(Surv(latency_s, responded) ~ group + factor(day) + cluster(mouse), dist = "lognormal")` in R; the Python examples here cover one trial per animal ([Andersen et al. 2021](https://doi.org/10.1089/neu.2020.7089); [Young & Hoane 2021](https://doi.org/10.3758/s13420-020-00457-y)) | Number censored per group per day; the model's effect with CI |
| Many animals at the cut-off in one group | Survival methods are essential; consider reporting the proportion reaching the cut-off as well | Both |

Jahn-Eimermacher and colleagues note that "ANOVA cannot properly deal with animals not showing the performance measure within the trial time, potentially causing biased and misleading results", and propose survival analysis instead ([Jahn-Eimermacher et al. 2011](https://doi.org/10.1016/j.bbr.2011.03.007)). Andersen and colleagues describe the usual practice for water-maze data: censored trials are "truncated at the maximum swim duration prior to analysis", "yielding skewed distributions that yield downwardly biased estimates of the mean and variance" ([Andersen et al. 2021](https://doi.org/10.1089/neu.2020.7089)).

## Worked example: 12 control and 12 treated mice, 60-s cut-off

Constructed data: one trial per mouse. By construction latencies are log-normal, with a median of 25 s in controls and 1.6 times longer with treatment; 3 treated mice and no controls reached the 60-s cut-off. The random seed (8) was the first, counting upward from 1, that gave at most one censored control and three to five censored treated mice, the typical pattern (about a quarter of treated mice expected at the cut-off). Because the AFT model here matches how the data were generated, its advantage in this example is a best case; with real data, compare log-normal and Weibull fits and say which you used. Stored code and data: [latency.csv](/guides/latency-with-a-cut-off-time/latency.csv), [model.py](/guides/latency-with-a-cut-off-time/model.py) (the latency and cut-off model), [make_data.py](/guides/latency-with-a-cut-off-time/make_data.py), [analysis.py](/guides/latency-with-a-cut-off-time/analysis.py), [analysis.R](/guides/latency-with-a-cut-off-time/analysis.R) and [the analysis output](/guides/latency-with-a-cut-off-time/analysis_output.txt); analysis.py and analysis.R print the same output. Before entering your own data, read the data-entry rules under "Before you analyse".

| Analysis | Result |
| --- | --- |
| Trials at the cut-off | Control 0 of 12; treated 3 of 12 |
| Welch's t-test on recorded values (60 s treated as real) | Difference 18.33 s, p = 0.011 |
| Mann–Whitney test (60 s treated as real; cut-off values tie) | p = 0.0093 |
| Kaplan–Meier median latency | Control 21.2 s; treated 48.2 s |
| Log-rank test (compares the curves) | χ²(1) = 7.50, p = 0.0062 |
| **Log-normal AFT model** | **Latencies 2.22 times longer with treatment (95% CI 1.32 to 3.71)**, p = 0.0025 |

In this one dataset every test points the same way, and the recorded difference (18.33 s) happens to land near the true 18.0 s, while the AFT ratio (2.22) overshoots the true 1.6: single small datasets vary. The difference is in what each analysis assumes: the recorded mean difference treats the three censored trials as if those mice had responded at exactly 60 s, while the survival methods use the fact that they took longer. The simulation shows what happens on average. In 10,000 simulated experiments of this design ([simulation.py](/guides/latency-with-a-cut-off-time/simulation.py), [the simulation output](/guides/latency-with-a-cut-off-time/simulation_output.txt)), with a quarter of treated mice censored on average:

| | Welch's t-test on recorded values | Mann–Whitney | Log-rank | Log-normal AFT |
| --- | --- | --- | --- | --- |
| No treatment effect (false positives) | 5.1% | 4.8% | 6.7% | 7.4% |
| Latency 1.6 times longer (power) | 43.8% | 41.4% | 42.6% | **50.9%** |

All rates are for 12 mice per group, one 60-s trial each; power is for latencies 1.6 times longer. The recorded mean difference averaged **12.0 s against a true 18.0 s**, a third too small, while the AFT model's median estimated ratio was 1.60, the true value. The log-rank test was no more powerful than the t-test here: its value is that it is a valid test that respects censoring, not that it adds power. With only 12 mice per group, the log-rank and AFT tests gave 6.7% and 7.4% false positives against a nominal 5%; with small groups, report the effect and its CI rather than leaning on a p-value near 0.05. The more trials reach the cut-off, the more distorted the recorded-value analyses become.

```python
# Illustrative and condensed; analysis.py (authoritative) also prints the Kaplan-Meier medians, the log-rank test and its p-value
# Your file: columns mouse, group ("control"/"treated" or edit below), latency_s (enter 60 for censored trials), responded
import numpy as np, pandas as pd
from scipy import stats, optimize

d = pd.read_csv("latency.csv")                       # columns: mouse, group, latency_s, responded (1 = responded, 0 = cut-off)
y, ev = np.log(d.latency_s.to_numpy()), d.responded.to_numpy()
x = (d.group == "treated").to_numpy().astype(float)
def nll(p):                                          # log-normal AFT: a censored trial contributes P(latency > cut-off)
    z = (y - p[0] - p[1] * x) / np.exp(p[2])
    return -(ev * (stats.norm.logpdf(z) - p[2]) + (1 - ev) * stats.norm.logsf(z)).sum()
fit = optimize.minimize(nll, [np.log(30), 0, np.log(0.6)], method="BFGS")
assert fit.success and np.isfinite(fit.x).all(), "model did not converge"
E = np.eye(3) * 1e-4; p0 = fit.x                    # numerical Hessian -> standard error of the treated coefficient
H = np.array([[(nll(p0 + a + b) - nll(p0 + a - b) - nll(p0 - a + b) + nll(p0 - a - b)) / 4e-8 for b in E] for a in E])
se = np.sqrt(np.linalg.inv(H)[1, 1])
print(np.exp(fit.x[1]), np.exp(fit.x[1] + np.array([-1.96, 1.96]) * se))   # time ratio and 95% CI
```

```r
library(survival)
d <- read.csv("latency.csv"); d$group <- factor(d$group, levels = c("control", "treated"))
survfit(Surv(latency_s, responded) ~ group, data = d)            # Kaplan-Meier medians
survdiff(Surv(latency_s, responded) ~ group, data = d)           # log-rank test
aft <- survreg(Surv(latency_s, responded) ~ group, data = d, dist = "lognormal")
exp(cbind(coef(aft), confint(aft)))["grouptreated", ]          # time ratio with 95% CI
```

## Before you analyse

1. **Record the event indicator for every trial**: 1 if the animal responded, 0 if the trial ended at the cut-off; enter the cut-off time (60, not blank) as the latency of a censored trial, and decide in advance whether a response at exactly 60 s counts as an event. Keep the indicator alongside the recorded time, so censored trials stay marked rather than becoming ordinary 60-s values.
2. **Count the censored trials** per group (and per day). If none reached the cut-off, ordinary methods on log latency are fine (add a column with the natural log of latency and run the usual test on it); the more censored trials there are, the more survival methods matter. If fewer than half of a group responded, its Kaplan–Meier median does not exist; report the share reaching the cut-off instead.
3. **Keep trial-level data for repeated trials.** Averaging trials per animal before analysis loses which trials were censored; use a model that handles repeated censored latencies, with animal as a cluster or random effect ([what counts as n](/guides/what-counts-as-n/)).
4. **Check what the latency measures.** In the water maze, latency depends on swim speed as well as learning; Vorhees and Williams note that "measures recorded on probe trials are insensitive to swimming speed" ([Vorhees & Williams 2006](https://doi.org/10.1038/nprot.2006.116)). Report path length or probe-trial measures alongside latency where they apply.
5. **Choose the effect measure in advance**: a time ratio (AFT) or a hazard ratio (Cox model; [log-rank vs Cox](/guides/log-rank-vs-cox-regression/)). For an AFT model, fit log-normal and Weibull versions (`dist = "weibull"` in R) and compare their log-likelihoods or AIC; Young and Hoane found that the choice matters little when censoring is low but can matter with substantial censoring and small samples ([Young & Hoane 2021](https://doi.org/10.3758/s13420-020-00457-y)).

## Check what an AI assistant ran

1. **A t-test or ANOVA on latencies with cut-off values entered as real times.** Ask for the number censored and a survival analysis.
2. **A Mann–Whitney test on recorded latencies**, with the cut-off values as ties. It also ignores censoring; ask for a survival analysis.
3. **Censored trials deleted.** Removing the slowest animals biases the comparison; ask for them back with the event indicator set to 0 (censored).
4. **Trials averaged per animal before a repeated-measures ANOVA.** Ask for a trial-level model.
5. **Censoring coded the wrong way round.** In R's `Surv()` and in Python's lifelines package, 1 means the event (the animal responded) and 0 means censored.

## Report it

Methods:

> Latency to [reach the platform] was recorded in one trial per mouse with a cut-off of [60] s; trials still running at [60] s, including any response at exactly [60] s, were treated as right-censored. Groups were compared with Kaplan–Meier curves and a log-rank test, and the effect was estimated as a time ratio from a log-normal accelerated failure time model (survival package version 3.5-8, R 4.3.3; replace with your versions).

For several trials per mouse:

> Latencies from all [4] acquisition days were analysed at the trial level with a log-normal accelerated failure time model including group and day, with mouse as a cluster (robust standard errors); [n] trials per group per day reached the cut-off.

Results:

> [3] of [12] treated mice and [0] of [12] controls reached the cut-off. Kaplan–Meier median latency was [21.2] s in controls and [48.2] s with treatment (log-rank χ²(1) = [7.50], p = [0.0062]); latencies were [2.22] times longer with treatment (95% CI [1.32] to [3.71]).

Replace the bracketed values with your own.

## References

- Jahn-Eimermacher A, Lasarzik I, Raber J (2011). Statistical analysis of latency outcomes in behavioral experiments. *Behavioural Brain Research* 221:271–275. [doi:10.1016/j.bbr.2011.03.007](https://doi.org/10.1016/j.bbr.2011.03.007)
- Andersen CR, Wolf J, Jennings K, Prough DS, Hawkins BE (2021). Accelerated failure time survival model to analyze Morris water maze latency data. *Journal of Neurotrauma* 38:435–445. [doi:10.1089/neu.2020.7089](https://doi.org/10.1089/neu.2020.7089)
- Young ME, Hoane MR (2021). Mixed effects modeling of Morris water maze data revisited: Bayesian censored regression. *Learning & Behavior* 49:307–320. [doi:10.3758/s13420-020-00457-y](https://doi.org/10.3758/s13420-020-00457-y)
- Vorhees CV, Williams MT (2006). Morris water maze: procedures for assessing spatial and related forms of learning and memory. *Nature Protocols* 1:848–858. [doi:10.1038/nprot.2006.116](https://doi.org/10.1038/nprot.2006.116)

## Related

- [Values below the detection limit](https://www.licklider.ai/guides/values-below-detection-limit/)
- [Log-rank vs Cox regression](https://www.licklider.ai/guides/log-rank-vs-cox-regression/)
- [Mann–Whitney vs t-test](https://www.licklider.ai/guides/mann-whitney-vs-t-test/)
- [Repeated measures ANOVA vs mixed model](https://www.licklider.ai/guides/repeated-measures-anova-vs-mixed-model/)
- [What counts as n?](https://www.licklider.ai/guides/what-counts-as-n/)

## How this page was made

- Written and published by Licklider. Recommendations cite peer-reviewed methods papers and software documentation listed under References; cited passages were checked against the source text.
- The worked example uses constructed data. Its numbers were produced by stored code (Python 3.13 with SciPy 1.18.1; R 4.3.3 with survival 3.5-8), and the two languages print identical output; the simulation was run in Python.
- Separate review passes checked the statistics, the citations and readability before publication.
