
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 ANANFor 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 figureresult <- 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.