Computes predicted values and lower/upper limits of normal
for pulmonary function tests according to American Thoracic Society
('ATS') and European Respiratory Society ('ERS') reference
standards. Supports spirometry (Global Lung Function Initiative
'GLI' 2012, Quanjer et al. (2012)

Compute ATS / ERS-compliant reference values, lower/upper limits of normal, and interpretive pattern classifications for pulmonary function tests in R.
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.
# install.packages("remotes")
remotes::install_github("overdodactyl/pft")
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.
| 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.
| 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.
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 ANAN
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 figure
result <- pft_interpret(cohort)
result |> pft_long() # one row per (patient, measure)
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.
Reference equations:
Pattern interpretation:
Use citation("pft") to retrieve the package and dependent reference list as
a bibentry for your own publications.
MIT. See LICENSE.
The numerical reference values reproduced inside this package are facts derived from published statistical models and are not themselves copyrightable. The source publications are © their respective publishers; copies are not redistributed with this package.
Bugs, feature requests, and clinical-validation feedback welcome via the issue tracker. For changes that affect classification logic or reference equation implementations, please include a citation to the relevant standards document in the PR description.