Compute ATS / ERS-compliant reference values, lower/upper limits of normal, and interpretive pattern classifications for pulmonary function tests in R.
Status
Research and education use only. This package is not a regulated medical device. The reference equations it implements come from published clinical standards, but the package itself has not been validated for diagnostic decision-making. All outputs require interpretation by a qualified clinician.
What it does
pft implements the Stanojevic 2022 ERS/ATS interpretive standard for pulmonary function tests. Reference values come from the GLI family of equations (GLI 2012 and GLI Global 2022 spirometry, GLI 2021 static lung volumes, GLI 2017 TLCO with the 2020 author correction). On top of those, the package computes z-scores and percent predicted, ATS pattern classification, severity grading, bronchodilator response, PRISm, conditional change scores and longitudinal slopes, GLI 2012 vs GLI Global 2022 reclassification, the Hughes & Pride diffusion category, lung age, and ggplot2-based single-patient and cohort figures.
Reference value functions
| Function | Computes | Source standard |
|---|---|---|
pft_spirometry() |
FEV1, FVC, FEV1/FVC, FEF25-75, FEF75 | GLI 2012 (Quanjer) or GLI Global 2022 (Bowerman) |
pft_volumes() |
FRC, TLC, RV, RV/TLC, ERV, IC, VC | GLI 2021 static lung volumes (Hall) |
pft_diffusion() |
TLCO/DLCO, KCO, VA (SI or traditional units) | GLI 2017 TLCO (Stanojevic, corrected 2020) |
Each reference function emits <measure>_pred, <measure>_lln, <measure>_uln. If a <measure>_measured column is also present, <measure>_zscore and <measure>_pctpred are appended automatically.
Spirometry outputs additionally carry a four-digit GLI-year suffix (e.g. fev1_pred_2012, fev1_pred_2022) so a single result frame can hold the GLI 2012 and GLI Global 2022 columns side-by-side, and so the suffix isn’t ambiguous when the default year changes in a future release. Lung-volume (Hall 2021) and diffusion (GLI 2017) outputs stay unsuffixed because only one standard currently ships for each — the same suffixing convention will be adopted there if competing standards emerge.
Interpretation functions
| Function | Purpose | Source |
|---|---|---|
pft_interpret() |
Single-call wrapper combining every primitive below | Stanojevic 2022 |
pft_classify() |
Normal / Non-specific / Obstructed / Restricted / Mixed | Stanojevic 2022 Fig 8, Tables 5/8 |
pft_volume_subpattern() |
Six lung-volume sub-patterns (Hyperinflation, Simple/Complex restriction, etc.) | Stanojevic 2022 Fig 10 |
pft_severity() |
normal / mild / moderate / severe per measure z-score | Stanojevic 2022 |
pft_diffusion_interpret() |
Parenchymal / Volume loss / Vascular / Mixed / Elevated KCO category | Hughes & Pride 2012 |
pft_bdr() |
>10% of predicted change in FEV1 or FVC | Stanojevic 2022 (BDR section) |
pft_prism() |
Preserved Ratio Impaired Spirometry flag | Stanojevic 2022 |
pft_change() |
Conditional change z-score for two-point serial measurements | Stanojevic 2022 |
pft_fev1q() |
FEV1Q adult-mortality index | Stanojevic 2022 Box 3 |
pft_dlco_hb_correct() |
Hemoglobin correction for DLCO/TLCO | Cotes 1972 / Stanojevic 2017 |
pft_quality() |
Spirometry quality grade (A-F) from a set of maneuvers | Graham 2019 |
pft_gold() |
COPD severity (GOLD 1-4) from FEV1 % predicted | GOLD reports |
pft_plot() |
Single-patient z-score lollipop with severity bands | — |
pft_long() |
Wide → long pivot; broom::tidy dispatches to it |
— |
All functions take a data frame and return a data frame, so they compose with dplyr pipelines.
Quick start
library(pft)
library(dplyr)
# Demographics for two patients
patients <- data.frame(
sex = c("M", "F"),
age = c(45, 60),
height = c(178, 165),
race = c("Caucasian", "AfrAm")
)
# Compute spirometry, lung volume, and diffusion reference values
patients |>
pft_spirometry(year = 2022) |>
pft_volumes() |>
pft_diffusion(SI.units = TRUE)
# Add measured values to also get z-scores and percent predicted
patients$fev1_measured <- c(3.2, 2.1)
patients$fvc_measured <- c(4.5, 2.8)
pft_spirometry(patients, year = 2022)
# -> adds fev1_pred_2022, fev1_lln_2022, fev1_uln_2022,
# fev1_zscore_2022, fev1_pctpred_2022, and equivalents for fvc.To classify a patient’s pattern, attach their measured values plus LLNs and pipe through pft_classify():
patient <- data.frame(
fev1 = 2.5, fev1_lln = 3.0,
fvc = 3.8, fvc_lln = 3.5,
fev1fvc = 0.66, fev1fvc_lln = 0.70,
tlc = 6.2, tlc_lln = 5.0
)
pft_classify(patient)
#> fev1 fev1_lln fvc fvc_lln fev1fvc fev1fvc_lln tlc tlc_lln ats_classification ats_pattern_combination
#> 1 2.5 3.0 3.8 3.5 0.66 0.70 6.2 5.0 Obstructed ANANEnd-to-end pipeline
For the canonical “interpret → visualise” workflow on a cohort:
cohort <- data.frame(
sex = c("M", "F"),
age = c(45, 60),
height = c(178, 165),
race = c("Caucasian", "AfrAm"),
fev1_measured = c(3.2, 1.8),
fvc_measured = c(4.5, 2.4),
tlc_measured = c(7.0, 4.8)
)
result <- pft_interpret(cohort) # spirometry + volumes + classify + severity
pft_plot(result[1, ]) # per-patient z-score figureCommon workflows
Cohort analysis with broom-style tidiers
result <- pft_interpret(cohort)
result |> pft_long() # one row per (patient, measure)Reference equations
Reference data is built reproducibly from official ERS / ATS source documents. The data-raw/build_*.R scripts read the published lookup-table workbooks (or, where unavailable, the equation tables printed in the article PDFs) and regenerate the package’s internal data. The source documents are copyrighted and not included in this repository; obtain them from the publishers if you want to regenerate the data.
| Package data | Build script | Source |
|---|---|---|
| GLI 2012 spirometry | data-raw/build_gli_2012.R |
ERS DC1 supplement workbook |
| GLI Global 2022 spirometry | data-raw/build_gli_2022.R |
ERS supplement workbook |
| GLI 2021 lung volumes | data-raw/build_gli_2021_volumes.R |
ERS supplement workbook + paper Table 3 |
| GLI 2017 TLCO | data-raw/build_gli_2017_diffusion.R |
ERS supplement workbooks + paper Table 2 (corrected) |
The validation tests anchor predicted, LLN, and ULN outputs against ground truth from the official GLI web calculator at gli-calculator.ersnet.org.
Citations
Reference equations:
- Quanjer PH, Stanojevic S, Cole TJ, et al. Multi-ethnic reference values for spirometry for the 3-95-yr age range: the global lung function 2012 equations. Eur Respir J. 2012;40(6):1324-1343. doi:10.1183/09031936.00080312
- Bowerman C, Bhakta NR, Brazzale D, et al. A race-neutral approach to the interpretation of lung function measurements. Am J Respir Crit Care Med. 2023;207(6):768-774. doi:10.1164/rccm.202205-0963OC
- Hall GL, Filipow N, Ruppel G, et al. Official ERS technical standard: Global Lung Function Initiative reference values for static lung volumes in individuals of European ancestry. Eur Respir J. 2021;57(3):2000289. doi:10.1183/13993003.00289-2020
- Stanojevic S, Graham BL, Cooper BG, et al. Official ERS technical standards: Global Lung Function Initiative reference values for the carbon monoxide transfer factor for Caucasians. Eur Respir J. 2017;50(3):1700010. doi:10.1183/13993003.00010-2017 (Author correction: doi:10.1183/13993003.50010-2017)
Pattern interpretation:
- Stanojevic S, Kaminsky DA, Miller MR, et al. ERS/ATS technical standard on interpretive strategies for routine lung function tests. Eur Respir J. 2022;60(1):2101499. doi:10.1183/13993003.01499-2021
- Pellegrino R, Viegi G, Brusasco V, et al. Interpretative strategies for lung function tests. Eur Respir J. 2005;26(5):948-968. doi:10.1183/09031936.05.00035205
Use citation("pft") to retrieve the package and dependent reference list as a bibentry for your own publications.
