# Seahorse respiration data: normalising OCR, mito stress test parameters and plates as replicates

> Normalise OCR to cell number (or protein or DNA) per well, subtract non-mitochondrial respiration, and compare genotypes with independent plates as n, on the log scale. Wells on one plate are technical replicates. Python and R code.

Canonical: https://www.licklider.ai/guides/seahorse-respiration-analysis/
Section: Guides › Normalised data and fold change
Author: Licklider · Published 2026-11-12 · Last reviewed 2026-11-12 (UTC)

> **Quick answer.** For a Seahorse mito stress test, first subtract **non-mitochondrial respiration** (the rate after rotenone/antimycin A), then **normalise each well's OCR to its cell number counted after the assay** (or protein or DNA, which can disagree with cell number), which is valid when OCR rises in proportion to cell number, and compare genotypes or treatments with **independent plates (experiments) as n**: for rates such as basal and maximal respiration, average the log values per plate and test the per-plate ratios; differences that can be near 0, such as spare capacity, stay on the original scale. Raw OCR confuses fewer cells with less respiration: in our simulation of knockout cells that grow more slowly but respire normally, raw OCR with plates as n was "significant" in 92.1% of studies. Wells on one plate are technical replicates: with normalised OCR and wells as n, 14.0% of studies with no real difference came out "significant", against 4.7% with plates as n.

**Terms used below.** *OCR*: oxygen consumption rate, in pmol O₂ per minute. *Mito stress test*: OCR measured at baseline and after oligomycin (blocks ATP synthase), FCCP (an uncoupler that allows maximal respiration) and rotenone with antimycin A (block the respiratory chain). *Non-mitochondrial respiration*: OCR after rotenone/antimycin A. *Basal respiration*: baseline OCR minus non-mitochondrial OCR. *ATP-linked respiration*: baseline OCR minus OCR after oligomycin. *Proton leak*: OCR after oligomycin minus non-mitochondrial OCR. *Maximal respiration*: OCR after FCCP minus non-mitochondrial OCR. *Spare respiratory capacity*: maximal minus basal respiration (also reported as maximal ÷ basal × 100 or as the difference ÷ basal × 100; say which you report). *Coupling efficiency*: ATP-linked ÷ basal respiration. *Normalisation*: dividing each well's rate by its cell number, protein or DNA; valid when OCR rises in proportion to the number of cells (a straight line through zero). *Block*: a factor (here the plate) whose differences are removed from the comparison. *Percentage points*: the difference between two percentages. *Plate*: one independent run, here seeded and measured on its own day. *Welch's t-test*: compares two groups without assuming equal spreads. *One-sample t-test*: tests whether the mean of several values (here the per-plate log ratios) differs from 0. *Background wells*: wells with medium but no cells, used to correct for drift. *95% confidence interval (CI)*: the range of values compatible with the data. *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 question

| Question | Analysis | Report |
| --- | --- | --- |
| Does a rate (basal or maximal respiration) differ between genotypes or treatments? | Per-well rate, normalised to cells; mean of logs per plate and group; one-sample t-test of the per-plate log ratios | Ratio with 95% CI, n plates, wells per plate |
| Do cell numbers differ? | Cells per well per plate, compared the same way | Ratio of cell numbers |
| A difference or proportion that can be near or below 0 (proton leak, spare capacity, coupling efficiency) | Compute per well, average per plate on the original scale, one-sample t-test of the per-plate differences between groups | Difference (for coupling efficiency, percentage points) with 95% CI |
| Several cell lines or treatments | Per-plate means for each group, then a model of those means with plate as a block (not of individual wells), with a correction across comparisons | Ratios with CIs |
| Only one plate | Report as descriptive; repeat on independent days | — |

Agilent's guidance explains that "normalization of XF assays can be applied on several levels, including cell number, genomic DNA, and total cellular protein" ([Agilent 2018](https://www.agilent.com/cs/library/technicaloverviews/public/Methods_and_Strategies_for_Normalizing_Tech_Overview_022118.pdf)). Brand and Nicholls list the parameters of cell respiratory control, "the rate of ATP production, the proton leak rate, the coupling efficiency, the maximum respiratory rate, the respiratory control ratio and the spare respiratory capacity", and note that "the meaning of results changes with normalization" ([Brand & Nicholls 2011](https://doi.org/10.1042/BJ20110162)). Divakaruni and Jastroch state that "mitochondrial respiration should always be corrected by subtracting the rate insensitive to respiratory chain inhibition" ([Divakaruni & Jastroch 2022](https://doi.org/10.1038/s42255-022-00619-4)). Yépez and colleagues found that "the noise of OCR is multiplicative" and "the inter-plate variation is greater than the intra-plate variation", that OCR was lower at the plate edges "by a median of up to 13.1%", that in their data "dividing OCR by cell count led to a higher coefficient of variation … between the replicate wells than without that correction", and that within-plate testing suffers from "an excess of … false positives" ([Yépez et al. 2018](https://doi.org/10.1371/journal.pone.0199938)).

## Worked example: knockout cells that grow more slowly

Constructed data: a mito stress test on 5 independent plates, each with 10 wild-type (WT) and 10 knockout (KO) wells. By design, **KO wells hold 25% fewer cells** (the cells grow more slowly), but **respiration per cell is the same** in both genotypes, and OCR is proportional to cell number, so dividing is valid here. Plates differ from each other (a standard deviation of 0.20 on the log scale), each plate's genotype difference varies a little (0.08), wells scatter (0.10), cell numbers per well vary (0.15) and each reading has its own noise (0.05). The random seed was the first one tried; the per-reading noise was added once, after a first run, to make the derived parameters realistic. Stored code and data: [seahorse.csv](/guides/seahorse-respiration-analysis/seahorse.csv), [model.py](/guides/seahorse-respiration-analysis/model.py), [make_data.py](/guides/seahorse-respiration-analysis/make_data.py), [analysis.py](/guides/seahorse-respiration-analysis/analysis.py), [analysis.R](/guides/seahorse-respiration-analysis/analysis.R) and [the analysis output](/guides/seahorse-respiration-analysis/analysis_output.txt); analysis.py and analysis.R print the same output.

Mean cells per well were 19,714 (WT) and 15,525 (KO). For maximal respiration:

| Analysis | KO ÷ WT | Result |
| --- | --- | --- |
| Raw OCR, wells as n (50 vs 50) | 0.70 | p = 9.3 × 10⁻⁹ |
| Raw OCR, plates as n | 0.70 (95% CI 0.55 to 0.89) | p = 0.014 |
| OCR per 1,000 cells, wells as n (50 vs 50) | 0.88 | p = 0.0072 |
| **OCR per 1,000 cells, plates as n (5 plates)** | **0.88 (95% CI 0.73 to 1.06)** | **p = 0.14** |

Raw OCR reports that knockout cells respire 30% less; most of that is simply fewer cells. Per cell, wells as n still gives a "significant" 12% decrease, because one plate's chance deviation (a per-plate ratio of 0.72) is counted ten times. With plates as n, the per-plate ratios (0.86, 0.72, 0.91, 0.86, 1.10) give an interval that includes no change. In 10,000 simulated studies of this design ([simulation.py](/guides/seahorse-respiration-analysis/simulation.py), [the simulation output](/guides/seahorse-respiration-analysis/simulation_output.txt)):

| Scenario | Raw OCR, wells as n | Per cell, wells as n | Raw OCR, plates as n | Per cell, plates as n |
| --- | --- | --- | --- | --- |
| Same respiration per cell (KO wells 25% fewer cells) | 99.8% | 14.0% | 92.1% | **4.7%** (false positives) |
| KO maximal respiration per cell 20% lower | 100.0% | 98.8% | 100.0% | **83.8%** (power) |

Without normalisation, almost every study "finds" a respiration defect that is only a difference in cell number. With normalisation but wells as n, false positives run at about three times the intended 5%. Normalised OCR with plates as n keeps false positives at 5% and, with 5 plates, detects a real 20% decrease in most studies.

The code expects one row per well with columns plate, genotype (WT or KO), well, cells (counted after the run) and the OCR at each stage (ocr_basal, ocr_oligomycin, ocr_fccp, ocr_rot_aa), built from the per-cycle rates exported by Wave: average the cycles of each stage (basal: the cycles before any injection; then the cycles after each injection), with background wells already subtracted; rename your columns and labels to match, and run the code in the folder that holds the CSV file. Python needs NumPy, pandas and SciPy; the R code uses base R only.

```python
# Condensed: analysis.py also shows raw OCR and wells-as-n comparisons
import numpy as np, pandas as pd
from scipy import stats

d = pd.read_csv("seahorse.csv")
d["max_per_k"] = (d.ocr_fccp - d.ocr_rot_aa) / d.cells * 1000           # maximal respiration per 1,000 cells
m = d.groupby(["plate", "genotype"]).max_per_k.apply(lambda v: np.log(v).mean()).unstack()   # mean log per plate
r = m["KO"] - m["WT"]                                                    # one log ratio per plate
t = stats.ttest_1samp(r, 0)
print(np.exp(r.mean()), np.exp(t.confidence_interval()), t.pvalue)       # 0.88, 0.73 to 1.06, p = 0.14
```

```r
d <- read.csv("seahorse.csv")
d$max_per_k <- (d$ocr_fccp - d$ocr_rot_aa) / d$cells * 1000             # maximal respiration per 1,000 cells
m <- tapply(log(d$max_per_k), list(d$plate, d$genotype), mean)           # mean log per plate
r <- m[, "KO"] - m[, "WT"]; tt <- t.test(r)                              # one log ratio per plate
exp(c(ratio = mean(r), tt$conf.int)); tt$p.value                         # 0.88, 0.73 to 1.06, p = 0.14
```

## Before you analyse

1. **Count cells (or measure protein or DNA) in every well after the run**, and normalise each well's rates; seeding density alone misses differences in growth and attachment. Plot OCR against cell number: if the line does not pass near zero, or groups differ greatly in cell number, adjust for cell number in a model instead of dividing ([normalising to body weight](/guides/normalizing-to-body-weight-ratio-or-ancova/) explains the same choice). Noisy cell counts add noise when you divide by them, so count carefully (for example by imaging the whole well).
2. **Subtract non-mitochondrial respiration** before computing basal, maximal, ATP-linked and spare capacity, and compute every parameter per well. Decide in advance whether maximal respiration is the highest FCCP cycle or the mean of the FCCP cycles (this page uses the mean).
3. **Plan independent plates**, with every group on every plate; the n is the number of plates or days ([what counts as n](/guides/what-counts-as-n/)). Five or more plates give a usable interval.
4. **Use the background wells and avoid the plate edges** for samples where you can, and balance groups across plate positions.
5. **Analyse positive rates on the log scale and report ratios**, since OCR noise is multiplicative; parameters that can be 0 or negative in a well (proton leak, spare capacity) stay on the original scale ([statistics on fold change](/guides/statistics-on-fold-change/)); check that the FCCP concentration was titrated, because too much or too little FCCP understates maximal respiration.

## Check what an AI assistant ran

1. **The assistant compared raw OCR between genotypes that grow differently.** Ask for OCR normalised to cell number, protein or DNA per well.
2. **The assistant used every well as n.** Ask for one value per plate and group, and a test across plates.
3. **The assistant computed maximal respiration without subtracting non-mitochondrial OCR.** Ask for the subtraction.
4. **The assistant reported spare capacity as a percentage without saying how it was calculated.** Ask whether it is maximal − basal or maximal ÷ basal.

## Report it

> OCR was measured with a mito stress test (oligomycin [your concentration] µM, FCCP [your titrated concentration] µM, rotenone/antimycin A [your concentration] µM) on [5] independent plates, [10] wells per genotype per plate. Non-mitochondrial OCR was subtracted and rates were normalised to the number of cells per well counted after the assay. Log-transformed rates were averaged per plate and genotype, and genotypes were compared with a one-sample t-test of the per-plate log ratios (plate as the unit; R [4.3.3]).

> Maximal respiration per cell did not differ clearly between genotypes (KO ÷ WT [0.88], 95% CI [0.73] to [1.06]; p = [0.14], n = [5] plates). KO wells contained fewer cells ([15,525] vs [19,714] per well).

Replace the bracketed values with your own.

## References

- Agilent Technologies (2018). Methods and strategies for normalizing XF metabolic data to cellular parameters. *Technical overview* (checked 12 November 2026). [agilent.com](https://www.agilent.com/cs/library/technicaloverviews/public/Methods_and_Strategies_for_Normalizing_Tech_Overview_022118.pdf)
- Brand MD, Nicholls DG (2011). Assessing mitochondrial dysfunction in cells. *Biochemical Journal* 435:297–312. [doi:10.1042/BJ20110162](https://doi.org/10.1042/BJ20110162)
- Divakaruni AS, Jastroch M (2022). A practical guide for the analysis, standardization and interpretation of oxygen consumption measurements. *Nature Metabolism* 4:978–994. [doi:10.1038/s42255-022-00619-4](https://doi.org/10.1038/s42255-022-00619-4)
- Yépez VA, Kremer LS, Iuso A, et al. (2018). OCR-Stats: robust estimation and statistical testing of mitochondrial respiration activities using Seahorse XF Analyzer. *PLOS ONE* 13:e0199938. [doi:10.1371/journal.pone.0199938](https://doi.org/10.1371/journal.pone.0199938)

## Related

- [What counts as n?](https://www.licklider.ai/guides/what-counts-as-n/)
- [Statistics on fold change and normalised data](https://www.licklider.ai/guides/statistics-on-fold-change/)
- [Normalising to body weight](https://www.licklider.ai/guides/normalizing-to-body-weight-ratio-or-ancova/)
- [Cell doubling time](https://www.licklider.ai/guides/cell-doubling-time/)

## How this page was made

- Written and published by Licklider. Recommendations cite peer-reviewed methods papers and manufacturer 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, base R), 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.
