Package {pft}


Type: Package
Title: Pulmonary Function Test Interpretation per ERS/ATS 2022
Version: 1.0.1
Description: 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) <doi:10.1183/09031936.00080312>; and the race-neutral 'GLI' 2022 / 'GLI Global' equations, Bowerman et al. (2023) <doi:10.1164/rccm.202205-0963OC>), static lung volumes ('GLI' 2021, Hall et al. (2021) <doi:10.1183/13993003.00289-2020>), and the carbon monoxide transfer factor / diffusion capacity ('GLI' 2017, Stanojevic et al. (2017) <doi:10.1183/13993003.00010-2017>, including the 2020 author correction <doi:10.1183/13993003.50010-2017>). Also assigns interpretive pattern labels (Normal, Non-specific, Obstructed, Restricted, Mixed) from spirometry and lung-volume measurements following the 'ERS'/'ATS' 2022 interpretation algorithm, Stanojevic et al. (2022) <doi:10.1183/13993003.01499-2021>.
License: MIT + file LICENSE
URL: https://github.com/overdodactyl/pft
BugReports: https://github.com/overdodactyl/pft/issues
Encoding: UTF-8
LazyData: true
RoxygenNote: 7.3.2
Depends: R (≥ 4.1)
Imports: rlang, tibble
Suggests: broom, covr, dplyr, ggplot2, knitr, rmarkdown, testthat (≥ 3.0.0)
VignetteBuilder: knitr
Config/testthat/edition: 3
NeedsCompilation: no
Packaged: 2026-07-04 18:12:32 UTC; m208076
Author: Pat Johnson ORCID iD [aut, cre], Scott Helgeson ORCID iD [aut], Zach Quicksall [ctb], Jairo Pena [ctb]
Maintainer: Pat Johnson <johnson.pat@mayo.edu>
Repository: CRAN
Date/Publication: 2026-07-11 08:30:13 UTC

pft: Pulmonary Function Test Interpretation per ERS/ATS 2022

Description

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, severity grading, ATS pattern classification, bronchodilator response per the 2022 criterion, PRISm screening, conditional change scores for serial measurements, GOLD COPD severity grading, Graham 2019 spirometry quality grades, and a ggplot2-based clinical lollipop figure.

Input data format

Every reference function (pft_spirometry(), pft_volumes(), pft_diffusion(), pft_interpret()) reads from canonical column names (sex, age, height, and – for GLI 2012 – race) by default. If your data frame names them differently, point at them explicitly via tidyverse-style column references:

pft_spirometry(d, sex = Sex, age = Age_y,
                  height = Ht_cm, race = Ancestry)

Bare names, strings (sex = "Sex"), and rlang injection (sex = !!my_var) are all accepted. The user's original column names are preserved in the output.

Optional ⁠<measure>_measured⁠, ⁠<measure>_pre⁠, and ⁠<measure>_post⁠ columns unlock z-score, percent-predicted, and bronchodilator-response outputs respectively; these are looked up by literal name (not overridable). See vignette("input-format") for a walkthrough.

Conventions used throughout

To support unambiguous clinical and research use, the following conventions hold across every exported function:

Implemented reference standards

Scope and limitations

pft does not currently cover:

Outputs are intended for research and education. Clinical decisions should be made by qualified clinicians using validated tools; pft is not FDA-cleared, not a medical device, and not validated for diagnostic decision-making.

Reproducibility

Every internal reference-equation coefficient and spline-table value in R/sysdata.rda is reproducibly built from the corresponding official ERS / AJRCCM source document via the scripts in data-raw/build_gli_*.R. The source PDFs and supplement workbooks are not redistributed with the package (they are copyrighted publisher content); the build scripts document where to obtain them.

Citation

Run citation() (i.e. citation("pft")) to retrieve a bibentry for the package alongside the source reference standards.

Author(s)

Maintainer: Pat Johnson johnson.pat@mayo.edu (ORCID)

Authors:

Other contributors:

See Also

Useful links:


Bronchodilator response per the ERS/ATS 2022 criterion

Description

Classifies bronchodilator response (BDR) by the percent change in the measured value relative to the patient's predicted value, as recommended by Stanojevic et al. ERJ 2022. Significant BDR is defined as a post-bronchodilator increase of more than 10% of the predicted value in either FEV1 or FVC. This replaces the 2005 standard, which used a >=12% AND >=200 mL change from baseline.

Usage

pft_bdr(pre, post, predicted, threshold = BDR_THRESHOLD_PCT_PRED)

Arguments

pre, post

Numeric vectors of pre- and post-bronchodilator measurements (same units, same length).

predicted

Numeric vector of predicted (median) values for the same measure, typically the ⁠<measure>_pred⁠ column from a previous call to pft_spirometry().

threshold

Percent-of-predicted change considered significant. Defaults to 10 (the Stanojevic 2022 criterion).

Value

A data frame with one row per input observation and columns:

Column naming

This function's pct_pred_change column is percent-of-predicted change (the 2022 criterion). The predecessor pft_bdr_2005() emits a similarly-named but different column, pct_change, which is percent-of-baseline change ((post - pre) / pre * 100, the 2005 criterion). The two functions deliberately use distinct column names so a result frame can carry both without ambiguity.

References

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. See the "Bronchodilator responsiveness testing" section.

See Also

pft_spirometry() to obtain the predicted FEV1 / FVC values used as the denominator. pft_interpret() runs BDR automatically when ⁠<measure>_pre⁠ and ⁠<measure>_post⁠ columns are present.

Examples

pft_bdr(pre = 2.5, post = 3.0, predicted = 4.0)
# -> 12.5% of predicted change, is_significant = TRUE


Bronchodilator response per the Pellegrino 2005 standard

Description

Classifies bronchodilator response (BDR) by the Pellegrino et al. ERJ 2005 dual criterion: significant if both the relative change from baseline is at least 12% AND the absolute change is at least 200 mL. Replaced in 2022 by pft_bdr()'s simpler "> 10% of predicted" rule.

Usage

pft_bdr_2005(pre, post)

Arguments

pre, post

Numeric vectors of pre- and post-bronchodilator measurements, in litres, same length.

Value

A data frame with one row per input observation and three columns: pct_change (i.e. (post - pre) / pre * 100), abs_change (i.e. post - pre in litres), and is_significant (logical, TRUE when pct_change > 12 AND abs_change > 0.2, both inequalities strict per the paper's wording on p. 959: "(>12% of control and >200 mL)"). NA propagates wherever either of pre / post is NA.

Column naming

This function's pct_change column is percent-of-baseline change (the 2005 criterion). The 2022 pft_bdr() emits a similarly-named but different column, pct_pred_change, which is percent-of-predicted change ((post - pre) / predicted * 100, the 2022 criterion). The two functions deliberately use distinct column names so a result frame can carry both without ambiguity.

References

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. Criterion stated in the "Bronchodilator response" section (p. 958) and disambiguated on p. 959.

See Also

pft_bdr() for the current Stanojevic 2022 criterion (>10% of predicted). Unlike the 2022 form, the 2005 version does not need the patient's predicted FEV1 / FVC – only the pre and post measurements.

Examples

pft_bdr_2005(pre = c(2.5, 2.0), post = c(2.8, 2.1))
# -> first row significant (>=12% AND >=200 mL),
#    second row not (only 5% and 100 mL increase)


Conditional change score for serial PFT measurements

Description

pft_change() computes the conditional change score (CCS) defined in Box 2 of the Stanojevic et al. ERS/ATS 2022 interpretation standard. The CCS evaluates whether the change between two FEV1 z-scores is larger than would be expected from within-subject variability and regression to the mean alone.

Formula (paper Box 2 p. 12):

CCS = (z_2 - r \cdot z_1) / \sqrt{1 - r^2}

Where the autocorrelation r is itself a function of the time interval between measurements and the patient's age at the first time point:

r = 0.642 - 0.04 \cdot time(years) + 0.020 \cdot age(years)

Changes within ⁠+/- 1.96⁠ change scores are considered within the normal limits per the paper.

This formula was derived from a children/young-people cohort (Stanojevic 2022 references the underlying study and notes the approach has "yet to be validated, extended to adults" but permits its use as "a reasonable tool to facilitate interpretation"). For adults the 2022 standard alternatively recommends FEV1Q (Box 3); see pft_fev1q().

Usage

pft_change(z1, z2, age_t1 = NULL, time_years = NULL, r = NULL)

Arguments

z1, z2

Numeric vectors of FEV1 z-scores at time 1 and time 2.

age_t1

Numeric. Patient age (in years) at the first measurement.

time_years

Numeric. Elapsed time between measurements in years (e.g. 0.25 for 3 months, 4 for 4 years).

r

Optional. Numeric in ⁠(-1, 1)⁠. If supplied, used directly in place of the paper's age/time formula – useful for callers who have a population-specific autocorrelation estimate. If NULL (the default), r is computed from age_t1 and time_years via the Box 2 formula.

Value

A data frame with columns:

References

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. Box 2 (p. 12).

See Also

pft_spirometry() to produce the FEV1 z-scores at each time point.

Examples

# Stanojevic 2022 Box 2 worked example: a 14-year-old male whose
# FEV1 z-score dropped from -0.78 to -1.60 over 3 months.
pft_change(z1 = -0.78, z2 = -1.60, age_t1 = 14, time_years = 0.25)
# -> r_used = 0.912, ccs ~= -2.17, is_significant = TRUE

# Same drop spread over 4 years
pft_change(z1 = -0.78, z2 = -1.60, age_t1 = 14, time_years = 4)
# -> r_used = 0.762, ccs ~= -1.55, is_significant = FALSE


Classify ATS spirometry patterns from spirometry and lung-volume measurements

Description

pft_classify() assigns ATS patterns using spirometry and lung volume data. By default it applies the Stanojevic et al. ERS/ATS 2022 algorithm (Figure 8); pass standard = "2005" to apply the predecessor Pellegrino et al. ERJ 2005 algorithm.

Typically called via pft_interpret() as part of the one-call workflow; exported for callers who want to apply the classifier to pre-computed columns directly.

Usage

pft_classify(
  data,
  standard = c("2022", "2005"),
  year = 2022,
  fev1 = fev1,
  fev1_lln = NULL,
  fvc = fvc,
  fvc_lln = NULL,
  fev1fvc = fev1fvc,
  fev1fvc_lln = NULL,
  tlc = tlc,
  tlc_lln = tlc_lln
)

Arguments

data

A data frame containing the six spirometry input columns (fev1, fev1_lln, fvc, fvc_lln, fev1fvc, fev1fvc_lln) and optionally tlc and tlc_lln. TLC columns are optional – when either is absent from data, the classifier routes via the spirometry-only fallback (see "Missing TLC" below).

standard

Which interpretive standard's classifier to apply. "2022" (default) follows Stanojevic et al. ERJ 2022 Figure 8 and recognises five labels: Normal, Non-specific, Obstructed, Restricted, Mixed. "2005" follows Pellegrino et al. ERJ 2005 Figure 2 and recognises four labels (Normal, Obstructed, Restricted, Mixed). The 2005 algorithm only consults TLC when FVC is below LLN; when FVC is normal it routes directly to Normal or Obstructed regardless of TLC. This is the dominant source of 2005 -> 2022 reclassification: rows with low TLC but normal FVC (NNNA, ANNA) become Restricted under 2022 but stay Normal under 2005; rows with low FEV1/FVC and low TLC but normal FVC (NNAA, ANAA) become Mixed under 2022 but stay Obstructed under 2005; the isolated-low-FVC cells (NANN, AANN) become Non-specific under 2022 but Normal under 2005.

year

GLI year suffix to use when looking up the spirometry LLN columns (fev1_lln, fvc_lln, fev1fvc_lln). Defaults to 2022 (GLI Global, race-neutral). Set to match the year argument used in the upstream pft_spirometry() / pft_interpret() call. The TLC columns (volumes reference) are unsuffixed and are not affected by year.

fev1, fev1_lln, fvc, fvc_lln, fev1fvc, fev1fvc_lln, tlc, tlc_lln

Column references for the eight inputs. Defaults are the canonical names (fev1, fev1_lln, ...); override with a bare name, a string, or !!var (see "Column-name overrides" below).

Value

The original data frame with two appended columns:

Column-name overrides

Each column-reference argument accepts three forms:

Defaults are the canonical pft column names, so callers whose data already follows the convention pass no extra arguments. The two TLC references (tlc, tlc_lln) are optional: when either resolves to a column not present in data, the spirometry-only fallback triggers without raising an error.

Missing TLC (spirometry-only fallback)

When the three spirometry inputs (fev1, fvc, fev1fvc) and their LLNs are all present but TLC is missing, pft_classify() falls back to a spirometry-only branch instead of returning NA. Under both standards, an "Obstructed" row is still recognisable from FEV1/FVC < LLN alone (Mixed would require TLC to distinguish but Mixed is itself an obstructive defect, so the row is labelled "Obstructed"). Under the 2005 standard, rows with FVC \ge LLN classify deterministically because the 2005 flowchart does not consult TLC in that branch (so "Normal" is emitted for normal spirometry). Cells where TLC would have been the disambiguating input (Normal vs Restricted, Non-specific vs Restricted under 2022; Normal vs Restricted, Obstructed vs Mixed under 2005) remain NA. Rows where any spirometry input is itself missing always return NA. See pft_prism() for the spirometry-only PRISm screen which is reported as a separate logical column.

References

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. The 2022 classifier follows the spirometry interpretation flowchart in Figure 8 and the pattern definitions in Tables 5 and 8.

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. The 2005 classifier follows Figure 2.

See Also

pft_prism() for the spirometry-only PRISm screen (no TLC required). pft_severity() / pft_severity_2005() grade per-measure severity. pft_interpret() runs the classifier as part of the one-call workflow and also accepts the standard argument for end-to-end reclassification.

Examples

data <- data.frame(fev1 = c(3.453, 2.385),
                             fev1_lln_2022 = c(3.303, 3.384),
                             fvc = c(4.733, 3.485),
                             fvc_lln_2022 = c(4.214, 4.24),
                             fev1fvc = c(0.600, 0.827),
                             fev1fvc_lln_2022 = c(0.681, 0.700),
                             tlc = c(1.5, 2.3),
                             tlc_lln = c(2, 2.5))
          pft_classify(data)
          pft_classify(data, standard = "2005")

          # Column-name override: data using non-canonical names.
          alt <- data.frame(my_fev1 = 3.0, my_fev1_lln = 2.5,
                            fvc = 4.0, fvc_lln_2022 = 3.5,
                            fev1fvc = 0.65, fev1fvc_lln_2022 = 0.70,
                            tlc = 6.0, tlc_lln = 5.0)
          pft_classify(alt, fev1 = my_fev1, fev1_lln = my_fev1_lln)


Compute carbon monoxide diffusion capacity or transfer factor reference values for given demographics

Description

pft_diffusion() computes ATS-compliant upper and lower normal limits for carbon monoxide measured diffusion capacity and European equivalents including DLCO (or TLCO), KCO, and VA.

Usage

pft_diffusion(data, SI.units = FALSE, sex = sex, age = age, height = height)

Arguments

data

A data frame containing columns for sex ("M","F"), age (in years, in the range 5-90 per the GLI 2017 spline tables) and height (in centimeters). If data also contains a ⁠<measure>_measured⁠ column for any of the active measures (tlco, kco_si, va under SI units; dlco, kco_tr, va under traditional), the measured value is used to compute z-score and percent-predicted (see Value).

SI.units

A boolean. Returns the reference values in SI units if TRUE . and Traditional units if FALSE.

sex, age, height

Column references. By default pft_diffusion() reads from sex, age, and height. Override via a bare name (sex = Sex), a string (sex = "Sex"), or an rlang injection (sex = !!my_var). The user's original column names are preserved in the output.

Value

The original data frame with extra columns appended for each measure:

References

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, applied here.)

See Also

pft_spirometry() and pft_volumes() for the analogous reference-value functions. pft_severity() grades DLCO impairment severity from the z-score column produced here. pft_interpret() composes all three reference functions in one call.

Examples

data <- data.frame(sex=c("M","F"),
                   age=c(30,5.1),
                   height=c(178,50))
pft_diffusion(data)


Classify a diffusion result into a clinical pattern category

Description

Takes per-patient dlco_zscore, va_zscore, and kco_zscore columns (the outputs of pft_diffusion() when ⁠_measured⁠ columns are supplied) and assigns a clinical interpretive category per the Hughes & Pride 2012 framework adopted by the ERS/ATS Stanojevic 2017 task force.

Usage

pft_diffusion_interpret(
  data,
  SI.units = FALSE,
  dlco = NULL,
  va = NULL,
  kco = NULL
)

Arguments

data

A data frame containing the three z-score input columns.

SI.units

Logical, default FALSE. Selects the default column names for dlco and kco. Traditional units (FALSE): dlco_zscore and kco_tr_zscore. SI units (TRUE): tlco_zscore and kco_si_zscore. va defaults to va_zscore in both unit systems.

dlco, va, kco

Column references for the three z-score inputs. dlco and kco default to NULL, which means: pick the canonical column name based on SI.units. Pass a bare name, a string, or !!var to override (see "Column-name overrides").

Details

The classifier consumes z-scores only and is unit-agnostic, but the default input column names differ between unit systems. Set SI.units = TRUE to pick up the SI-units column set (tlco_zscore, va_zscore, kco_si_zscore); otherwise the traditional-units column set (dlco_zscore, va_zscore, kco_tr_zscore) is used. Override individual column names via the dlco / va / kco arguments.

Typically called via pft_interpret() as part of the one-call workflow; exported for callers who want to apply the classifier to pre-computed z-score columns directly.

Value

The original data frame with a single appended column: diffusion_category. Possible values:

"Normal"

All three z-scores above LLN.

"Parenchymal"

Low DLCO, low KCO, normal VA.

"Volume loss"

Low DLCO, low VA, normal or elevated KCO.

"Mixed"

Low DLCO, low VA, low KCO.

"Vascular (suggested)"

Low DLCO, normal VA, low or elevated KCO.

"Elevated KCO"

Normal DLCO with elevated KCO (z > +1.645).

"Other"

Combination not matching any of the above patterns (e.g., low VA in isolation).

NA

Required z-score columns missing.

The category labels describe the z-score pattern only; differential diagnosis is left to the clinician (see the Hughes & Pride 2012 source paper for clinical interpretation).

Column-name overrides

Each column-reference argument accepts three forms:

dlco and kco default to NULL, which selects the canonical name based on SI.units (traditional or SI). Passing an explicit reference overrides this selection.

References

Hughes JM, Pride NB. Examination of the carbon monoxide diffusing capacity (DL(CO)) in relation to its KCO and VA components. Am J Respir Crit Care Med. 2012;186(2):132-139. doi:10.1164/rccm.201112-2160CI.

Stanojevic S, Graham BL, Cooper BG, et al. ERS/ATS technical standard: Global Lung Function Initiative reference values for the carbon monoxide transfer factor for Caucasians. Eur Respir J. 2017;50:1700010. doi:10.1183/13993003.00010-2017. (Provides the z-score reference standard whose LLN at z = -1.645 is used here. The clinical interpretation framework is from Hughes & Pride 2012, adopted by the 2017 task force.)

See Also

pft_diffusion() to compute the input z-scores; pft_interpret() for the one-call workflow that auto-runs this classifier when diffusion outputs are present.

Examples

# Three patients: normal, parenchymal (low DLCO/KCO, normal VA),
# and volume loss (low DLCO/VA, normal KCO).
d <- data.frame(
  dlco_zscore  = c(-0.5, -2.0, -2.0),
  va_zscore    = c(-0.5, -0.5, -2.0),
  kco_tr_zscore = c(-0.5, -2.0, -0.5)
)
pft_diffusion_interpret(d)

# SI units (TLCO / KCO_SI). Pass SI.units = TRUE.
d_si <- data.frame(
  tlco_zscore   = c(-0.5, -2.0),
  va_zscore     = c(-0.5, -0.5),
  kco_si_zscore = c(-0.5, -2.0)
)
pft_diffusion_interpret(d_si, SI.units = TRUE)


Adjust a measured DLCO / TLCO for the patient's hemoglobin

Description

Applies the Cotes 1972 hemoglobin correction to a measured carbon monoxide transfer factor (DLCO or TLCO), returning the value that would have been measured if the patient's hemoglobin equaled the age- and sex-specific reference. The correction is intended for clinical interpretation: after applying it, the corrected DLCO can be passed to pft_diffusion() (whose reference equations assume the reference Hb).

Usage

pft_dlco_hb_correct(dlco, hemoglobin, sex, age = NA_real_)

Arguments

dlco

Numeric vector of measured DLCO or TLCO values, any unit system (the correction is multiplicative and unit-agnostic).

hemoglobin

Numeric vector of measured hemoglobin in g/L (routine clinical adult range 120-160 g/L). Pass g/L directly; the function does not detect or convert g/dL inputs.

sex

Character vector ("M"/"F"). Soft-corrected via the internal normalize_sex_vec() helper so "Male", "female", etc. work.

age

Optional numeric vector. When supplied, males aged < 15 yr use the female / child reference (134 g/L) per Stanojevic 2017 p. 11. When omitted (the default NA_real_), males default to the adult reference (146).

Details

Stanojevic et al. ERJ 2017 explicitly recommends (Table 4 p. 9) that the published GLI TLCO reference values be uncorrected for Hb, with Hb levels considered separately during interpretation. This function provides that interpretive step.

Formula (Cotes 1972; reformulated from Stanojevic 2017 p. 9, with constants 1.7 and 0.7 reflecting the membrane / capillary diffusing capacity ratio of 0.7 mL min^-1 mmHg^-1 mL-blood^-1):

TLCO_{Hb} = TLCO_{measured} \times \frac{1.7 \cdot Hb_{ref}}{Hb + 0.7 \cdot Hb_{ref}}

Reference Hb levels (Stanojevic 2017 p. 11):

When hemoglobin == Hb_ref the correction factor is exactly 1 (no adjustment). Anaemic patients (hemoglobin < Hb_ref) receive an UPWARD correction reflecting that less Hb means less CO uptake and a depressed measured value.

Value

Numeric vector of Hb-corrected DLCO / TLCO values, same units as dlco. NA propagates from any input.

References

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. Hb correction is discussed on p. 9 and the Cotes formula context on p. 11.

Cotes JE, Dabbs JM, Elwood PC, et al. Iron-deficiency anaemia: its effect on transfer factor for the lung (diffusing capacity) and ventilation and cardiac frequency during sub-maximal exercise. Clin Sci. 1972;42:325-335.

See Also

pft_diffusion() for the reference-value computation that consumes the Hb-corrected DLCO.

Examples

# An anaemic adult male (Hb = 100 g/L vs reference 146 g/L) with
# measured DLCO of 20 mL/min/mmHg has a Hb-adjusted DLCO of
# 20 x (1.7 x 146) / (100 + 0.7 x 146) ~= 24.55.
pft_dlco_hb_correct(dlco = 20, hemoglobin = 100,
                    sex = "M", age = 40)

# No-op when Hb equals reference.
pft_dlco_hb_correct(20, hemoglobin = 146, sex = "M")

# Children use the female / child reference (134 g/L) regardless
# of sex.
pft_dlco_hb_correct(20, hemoglobin = 134, sex = "M", age = 10)


Synthetic example PFT cohort

Description

A small synthetic cohort of 20 patients spanning the clinical patterns the package classifies (Normal, Obstructed, Restricted, Mixed, Non-specific, PRISm) plus a few bronchodilator-response cases. Demographics are random within plausible ranges; measured values were hand-picked so that each row lands in its target pattern after the GLI reference equations are applied.

Usage

pft_example

Format

A tibble with 20 rows and 11 columns:

patient_id

Integer 1-20. Synthetic identifier.

sex

Character: "M" or "F".

age

Numeric, years.

height

Numeric, centimetres.

race

One of "Caucasian", "AfrAm", "NEAsia", "SEAsia".

fev1_measured

Measured FEV1 in litres. May be NA.

fvc_measured

Measured FVC in litres.

fev1fvc_measured

Measured FEV1/FVC ratio.

tlc_measured

Measured TLC in litres. May be NA for rows intended to demonstrate spirometry-only workflows.

fev1_pre

Pre-bronchodilator FEV1 in litres. NA when not a BDR scenario.

fev1_post

Post-bronchodilator FEV1 in litres. NA when not a BDR scenario.

Details

No real patient data. Generated by data-raw/build_pft_example.R. Provided for examples, vignettes, and quick experimentation.

Source

data-raw/build_pft_example.R (synthetic; no real patient data).

Examples

data(pft_example)
head(pft_example)
pft_interpret(pft_example)

FEV1Q: ratio of FEV1 to a sex-specific survivable lower limit

Description

Computes the FEV1Q survival index proposed by Miller & Pedersen (ERJ 2010) and discussed as an adult alternative to the conditional change score in Box 3 (p. 13) of the Stanojevic et al. ERJ 2022 interpretation standard. FEV1Q expresses FEV1 in relation to a "bottom line" required for survival, rather than how far an individual's result is from their predicted value; the race-neutral evidence base was consolidated by Balasubramanian et al. (ERJ 2024).

Usage

pft_fev1q(fev1, sex, age = NA_real_)

Arguments

fev1

Numeric vector of FEV1 measurements in litres.

sex

Character vector of patient sex. Accepts the soft- correctable variants from pft_spirometry() ("Male", "female", etc.); unrecognized values yield NA.

age

Optional numeric vector. When supplied, rows with age < 18 return NA_real_ per the paper's "not appropriate for children and adolescents" caveat. Default NA_real_ skips the guard.

Details

Formula (Box 3 verbatim):

FEV1Q = FEV1 / Q_{sex}

where Q_{male} = 0.5 L and Q_{female} = 0.4 L are the sex-specific 1st percentiles of the FEV1 distribution in adult lung-disease populations. The index approximates the number of turnovers remaining of a lower survivable limit of FEV1; values closer to 1 indicate greater risk of death.

The 2022 standard cautions (running text on p. 13, immediately preceding Box 3): "FEV1Q is not appropriate for children and adolescents." When age is supplied, rows with age < 18 return NA_real_. When age is omitted, the age guard is skipped and the caller is responsible for restricting input to adults.

For longitudinal interpretation in adults the 2022 standard suggests FEV1Q as an alternative to the conditional change score (see pft_change()): under normal circumstances 1 unit of FEV1Q is lost approximately every 18 years (every ~10 years in smokers and the elderly).

Value

Numeric vector of FEV1Q ratios, same length as fev1. NA propagates from any input.

References

Miller MR, Pedersen OF. New concepts for expressing forced expiratory volume in 1 s arising from survival analysis. Eur Respir J. 2010;35(4):873-882. doi:10.1183/09031936.00025809. Original proposal of the FEV1Q index and the sex-specific 1st-percentile denominators (0.5 L male, 0.4 L female).

Balasubramanian A, Wise RA, Stanojevic S, Miller MR, McCormack MC. FEV1Q: a race-neutral approach to assessing lung function. Eur Respir J. 2024;63(4):2301622. doi:10.1183/13993003.01622-2023. Race-neutral validation of the FEV1Q index.

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. Discusses FEV1Q in Box 3 (p. 13) as an adult alternative to the conditional change score.

See Also

pft_change() for the conditional change score (the children / young-people sibling); pft_severity() for the z-score-based severity grading.

Examples

# Stanojevic 2022 Box 3 worked example: a 70-year-old woman with
# FEV1 of 0.9 L has FEV1Q of 0.9 / 0.4 = 2.25.
pft_fev1q(0.9, "F", age = 70)

# Vectorised across sex.
pft_fev1q(c(1.0, 1.0), c("M", "F"))

# Adolescents return NA when age is supplied.
pft_fev1q(1.0, "F", age = 10)


Grade COPD severity by GOLD criteria

Description

Returns the GOLD spirometric severity grade (1-4) for one or more patients given their FEV1 expressed as a percent of predicted, optionally enforcing the GOLD-mandated prerequisite of confirmed airflow obstruction (FEV1/FVC < 0.7).

Usage

pft_gold(fev1_pctpred, fev1fvc = NA_real_)

Arguments

fev1_pctpred

Numeric vector of FEV1 % predicted values (e.g. the fev1_pctpred column from pft_spirometry() when measured values are supplied).

fev1fvc

Optional numeric vector of post-bronchodilator FEV1/FVC ratios (e.g. the fev1fvc_measured column). When supplied, rows with fev1fvc >= 0.7 are returned as NA_character_ – per GOLD 2026 Figure 2.10, the grading applies only "In patients with COPD (FEV1/FVC < 0.7)". When omitted (the default NA_real_) or all-NA, no prerequisite check is performed and a grade is returned for every non-NA fev1_pctpred.

Details

GOLD severity grades for airflow obstruction (Figure 2.10 of the GOLD 2026 report, content page 38):

Grade Severity FEV1 % predicted
GOLD 1 Mild ⁠>= 80⁠
GOLD 2 Moderate ⁠>= 50 and < 80⁠
GOLD 3 Severe ⁠>= 30 and < 50⁠
GOLD 4 Very severe ⁠< 30⁠

GOLD specifies the prerequisite "In patients with COPD (FEV1/FVC < 0.7)" explicitly above Figure 2.10's grade table; the surrounding text (content p. 37) repeats this requirement. Supplying fev1fvc enforces the GOLD fixed-cutoff prerequisite. Callers wanting an LLN-based prerequisite instead should use pft_classify() to identify obstructed patients and mask pft_gold() output by hand.

Value

Character vector with values "GOLD 1", "GOLD 2", "GOLD 3", "GOLD 4", or NA. NA is returned for rows with missing fev1_pctpred OR (when fev1fvc is supplied) rows that fail the airflow-obstruction prerequisite.

References

Global Initiative for Chronic Obstructive Lung Disease (GOLD). Global Strategy for the Diagnosis, Management and Prevention of Chronic Obstructive Pulmonary Disease, 2026 Report. Figure 2.10. https://goldcopd.org.

See Also

pft_classify() for LLN-based airflow obstruction identification (Stanojevic 2022); pft_severity() for the z-score-based severity scheme (which differs from GOLD's percent-predicted scheme).

Examples

# Without prerequisite check (backward-compatible): one grade per
# non-NA input.
pft_gold(c(85, 65, 40, 25))
# -> "GOLD 1" "GOLD 2" "GOLD 3" "GOLD 4"

# With prerequisite check: the third patient has FEV1/FVC = 0.75
# (no airflow obstruction) and is returned NA.
pft_gold(c(85, 65, 40, 25), fev1fvc = c(0.65, 0.60, 0.75, 0.55))
# -> "GOLD 1" "GOLD 2" NA "GOLD 4"


Comprehensive ERS/ATS 2022 PFT interpretation in one call

Description

pft_interpret() is a single-call workflow that combines every interpretation primitive in this package into a complete clinical report per the Stanojevic et al. ERJ 2022 standard. It auto-detects which computations are possible from the input columns and skips anything it cannot do:

This is the recommended entry point for clinical-style reporting; the individual reference and interpretation functions are exported for callers who need finer-grained control.

Usage

pft_interpret(
  data,
  year = 2022,
  SI.units = FALSE,
  standard = c("2022", "2005"),
  sex = sex,
  age = age,
  height = height,
  race = race
)

Arguments

data

A data frame containing whatever inputs are available. See Details for the column-name conventions.

year

GLI spirometry equation year. Defaults to 2022 (GLI Global, race-neutral). See pft_spirometry().

SI.units

Whether to report diffusion in SI units. See pft_diffusion().

standard

Interpretive standard whose downstream rules to apply: "2022" (default) uses Stanojevic et al. ERJ 2022 for pattern classification, severity grading, and BDR; "2005" uses the Pellegrino et al. ERJ 2005 predecessor. The selected standard does not affect the GLI reference equations (those are controlled by year) – only the downstream interpretive logic. Useful for reclassification analyses comparing the two standards on the same cohort.

sex, age, height, race

Column references. By default pft_interpret() reads from sex, age, height, and (for year = 2012) race. Override via a bare name (sex = Sex), a string (sex = "Sex"), or an rlang injection (sex = !!my_var). The ⁠_measured⁠, ⁠_pre⁠, and ⁠_post⁠ columns are still auto-detected by name and not overridable.

Details

To trigger z-scores and percent-predicted on a measure, include the corresponding ⁠<measure>_measured⁠ column in data (e.g. fev1_measured, frc_measured, dlco_measured). To trigger BDR, include ⁠<measure>_pre⁠ and ⁠<measure>_post⁠ columns for any of FEV1, FVC, FEV1/FVC.

All outputs trace to a specific equation, table, or figure in Stanojevic et al. ERJ 2022 or the underlying GLI reference papers; see the ⁠@references⁠ blocks on the individual functions.

Value

The original data frame with every applicable reference value, z-score, percent predicted, severity grade, pattern label, PRISm flag, and BDR result appended.

References

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.

Examples

patient <- data.frame(
  sex = "M", age = 45, height = 178, race = "Caucasian",
  fev1_measured = 2.5, fvc_measured = 3.8, fev1fvc_measured = 2.5/3.8,
  tlc_measured  = 6.0
)
pft_interpret(patient)


Pivot a pft_result to long form

Description

Reshapes a wide pft_interpret() / pft_spirometry() / pft_volumes() / pft_diffusion() output (one row per patient, one column per measure × statistic) into long form (one row per ⁠(patient, measure, year)⁠ with columns for each statistic). This is the natural shape for dplyr / ggplot2 faceting, cohort modelling, and broom-style downstream workflows.

Usage

pft_long(x, ...)

Arguments

x

A data frame; typically a pft_result but any data frame with ⁠<measure>_pred[_<year>]⁠ columns works. Named x (rather than data) to match the S3 first-argument convention shared with print.pft_result, plot.pft_result, and the other pft_result methods.

...

Currently unused; reserved for forward compatibility.

Details

Discovery is keyed off ⁠<measure>_pred⁠ columns; the four-digit GLI year is extracted from the column suffix and recorded in the year column. Spirometry outputs from pft_spirometry() / pft_interpret() always carry a year suffix (fev1_pred_2012, fev1_pred_2022, ...) and produce a populated year; lung-volume (Hall 2021) and diffusion (GLI 2017) outputs are unsuffixed and produce year = NA until a competing standard ships and the same suffixing convention is adopted there. Columns whose suffix does not match a recognised statistic are ignored, so id / demographic columns are dropped (use the .patient integer to join back).

Value

A tibble with columns .patient (integer row position), measure, year (character; NA for non-suffixed outputs), pred, lln, uln, measured, zscore, pctpred, and severity. Missing statistics fill with NA of the appropriate type.

See Also

pft_interpret() to produce the wide-form input.

Examples

patient <- data.frame(
  sex = c("M","F"), age = c(45, 60), height = c(178, 165),
  race = "Caucasian",
  fev1_measured = c(2.5, 1.8), fvc_measured = c(3.8, 2.4)
)
result <- pft_interpret(patient)
pft_long(result)


Clinical visualisation for a single PFT result

Description

pft_plot() draws a single-patient z-score figure: one row per measure, points at the patient's z-score, shaded reference bands for the four Stanojevic 2022 severity grades returned by pft_severity(): normal (z >= -1.645), mild (-2.5 <= z < -1.645), moderate (-4 <= z < -2.5), and severe (z < -4). The normal band extends symmetrically above zero to the upper limit of normal; values above the ULN are shown but are not a 2022 severity grade.

Requires the ggplot2 package (a Suggested dependency).

Usage

pft_plot(data)

Arguments

data

A single-row data frame produced by pft_interpret(), pft_spirometry(), pft_volumes(), or pft_diffusion() with measured values supplied (i.e. at least one ⁠<measure>_zscore⁠ column present). Errors if nrow(data) != 1.

Value

A ggplot object.

See Also

pft_interpret() for the input data shape.

Examples


patient <- data.frame(
  sex = "M", age = 45, height = 178, race = "Caucasian",
  fev1_measured    = 2.5,
  fvc_measured     = 3.8,
  fev1fvc_measured = 2.5 / 3.8,
  tlc_measured     = 6.0
)
pft_plot(pft_interpret(patient))


Screen for Preserved Ratio Impaired Spirometry (PRISm)

Description

PRISm is the spirometry-only manifestation of the "non-specific" pattern when TLC is not available: a low FEV1, a low FVC, and a preserved (normal) FEV1/FVC ratio. The 2022 ERS/ATS interpretation standard (Stanojevic et al.) classifies it in Table 5 with row "Non-specific pattern" (FEV1 reduced, FVC reduced, FEV1/FVC normal).

Typically called via pft_interpret() as part of the one-call workflow; exported for callers who want to apply the screen to pre-computed columns directly.

This function adds a prism logical column to the data frame. PRISm is a spirometry-only screen and does not require a TLC measurement.

Usage

pft_prism(
  data,
  year = 2022,
  fev1 = fev1,
  fev1_lln = NULL,
  fvc = fvc,
  fvc_lln = NULL,
  fev1fvc = fev1fvc,
  fev1fvc_lln = NULL
)

Arguments

data

A data frame containing the six input columns named below.

year

GLI year suffix used when looking up the LLN columns (fev1_lln, fvc_lln, fev1fvc_lln). Defaults to 2022. Set to match the year argument used in the upstream pft_spirometry() / pft_interpret() call.

fev1, fev1_lln, fvc, fvc_lln, fev1fvc, fev1fvc_lln

Column references for the six required columns. Defaults are the canonical names (fev1, ⁠fev1_lln_<year>⁠, ...); override with a bare name, a string, or !!var (see "Column-name overrides" below).

Value

The original data frame with a prism logical column appended. NA propagates from any of the six input columns.

Column-name overrides

Each column-reference argument accepts three forms:

Defaults are the canonical pft column names, so callers whose data already follows the convention pass no extra arguments.

References

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. PRISm appears in Table 5 as the spirometry-only form of the non-specific pattern.

See Also

pft_classify() for the full ATS pattern classification when TLC is available; pft_interpret() runs both PRISm and full classification automatically when the relevant columns are present.

Examples

d <- data.frame(fev1    = 2.0, fev1_lln_2022    = 2.5,
                fvc     = 2.6, fvc_lln_2022     = 3.0,
                fev1fvc = 0.80, fev1fvc_lln_2022 = 0.70)
pft_prism(d)

# Column-name override: data using non-canonical names.
d2 <- data.frame(my_fev1 = 2.0, my_fev1_lln = 2.5,
                 fvc = 2.6, fvc_lln_2022 = 3.0,
                 fev1fvc = 0.80, fev1fvc_lln_2022 = 0.70)
pft_prism(d2, fev1 = my_fev1, fev1_lln = my_fev1_lln)


Grade spirometry quality per ATS/ERS 2019

Description

Assigns one of grades A-F to a set of acceptable spirometry maneuvers for a single measure (FEV1 or FVC) per the Graham et al. ATS/ERS 2019 technical standard, Table 10. Grades depend on the number of acceptable maneuvers and the difference between the best two values.

Usage

pft_quality(values, age = NA_real_)

Arguments

values

Numeric vector of measurements (litres) from each acceptable maneuver for ONE patient and ONE measure. Length 0 is allowed and yields grade "F".

age

Patient age, in years. The repeatability thresholds tighten for children aged 6 or younger; the threshold is the greater of the absolute child value (0.100 / 0.150 / 0.200 L for A / C / D) and 10% of the highest measured value, per Table 10's footnote. Defaults to NA_real_, which uses the adult thresholds.

Details

Grade definitions (Table 10, paper p. e83). Adult thresholds in parentheses; child (age <= 6) thresholds are ⁠max(absolute, 0.10 · max(values))⁠:

Grade U ("0 acceptable AND >= 1 usable") from Table 10 is NOT currently distinguished from F. Implementing U would require extending the API to take a separate vector of usable-but-not- acceptable maneuvers; with zero acceptable values, the function returns F unconditionally.

Value

A length-1 character with value "A", "B", "C", "D", "E", or "F".

References

Graham BL, Steenbruggen I, Miller MR, et al. Standardization of Spirometry 2019 Update. An Official American Thoracic Society and European Respiratory Society Technical Statement. Am J Respir Crit Care Med. 2019;200(8):e70-e88. doi:10.1164/rccm.201908-1590ST.

See Also

pft_interpret() for the downstream interpretation once acceptable maneuvers have been selected.

Examples

pft_quality(c(3.20, 3.12, 3.10))              # Grade A (n>=3 within 0.150)
pft_quality(c(3.20, 3.12))                    # Grade B (n=2 within 0.150)
pft_quality(c(3.20, 3.02))                    # Grade C (n>=2 within 0.200)
pft_quality(c(3.20, 2.97))                    # Grade D (n>=2 within 0.250)
pft_quality(c(3.20, 2.80))                    # Grade E (n>=2 diff > 0.250)
pft_quality(c(3.20))                          # Grade E (only 1)
pft_quality(numeric(0))                       # Grade F (none)


Grade severity of lung function impairment from a z-score

Description

Assigns one of four severity categories ("normal", "mild", "moderate", "severe") to a z-score per the Stanojevic et al. ERS/ATS 2022 interpretation standard. The same grading applies uniformly to spirometry, lung-volume, and diffusion measures.

Boundary conventions (matching the function's implementation):

Grade z-score
normal z >= -1.645
mild ⁠-2.5 <= z < -1.645⁠
moderate ⁠-4 <= z < -2.5⁠
severe z < -4

Usage

pft_severity(zscore)

Arguments

zscore

Numeric vector of z-scores.

Value

Character vector the same length as zscore with values "normal", "mild", "moderate", "severe", or NA.

References

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. The cut points are taken from the "Severity of lung function impairment" section.

See Also

pft_classify() for the pattern label that severity sits alongside; pft_gold() for COPD-specific severity from FEV1 percent predicted; pft_interpret() applies this grading to every z-score column in one call.

Examples

pft_severity(c(0, -1.7, -3, -5))
# -> "normal" "mild" "moderate" "severe"


Severity grading per the Pellegrino 2005 standard

Description

Assigns a five-band severity grade from FEV1 percent predicted, per the Pellegrino et al. ERJ 2005 standard (the predecessor to the 2022 z-score-based grading implemented by pft_severity()).

Boundary conventions (matching the function's implementation):

Grade FEV1 % predicted
mild ⁠>= 70%⁠
moderate ⁠60% - 69%⁠
moderately severe ⁠50% - 59%⁠
severe ⁠35% - 49%⁠
very severe ⁠< 35%⁠

Note that unlike pft_severity(), the 2005 grading has no "normal" tier – the grades describe the severity of an impairment that has already been identified, and "normal" lung function is indicated by the pattern classifier returning "Normal" rather than by the severity grade itself. Pass only percent-predicted values from patients with an identified impairment.

Usage

pft_severity_2005(pctpred)

Arguments

pctpred

Numeric vector of FEV1 percent predicted values (e.g. the fev1_pctpred column from pft_spirometry() times nothing – it is already a percent).

Value

Character vector the same length as pctpred with values "mild", "moderate", "moderately severe", "severe", "very severe", or NA.

References

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. Severity bands taken from Table 4.

See Also

pft_severity() for the current Stanojevic 2022 z-score-based grading. pft_classify() with standard = "2005" for the matching 2005-era pattern classifier.

Examples

pft_severity_2005(c(85, 65, 55, 40, 30))
# -> "mild" "moderate" "moderately severe" "severe" "very severe"


Compute spirometry reference values for given demographics

Description

pft_spirometry() computes ATS-compliant upper and lower normal limits for common spirometry measures including FEV1, FVC, FEV1/FVC, FEF2575, and FEF75.

Usage

pft_spirometry(
  data,
  year = 2022,
  sex = sex,
  age = age,
  height = height,
  race = race
)

Arguments

data

A data frame containing columns for sex ("M","F"), race ("AfrAm","NEAsia","SEAsia","Other/mixed", "Caucasian"), age (in years, in the range 3-95 for FEV1 / FVC / FEV1/FVC and 3-90 for FEF25-75 / FEF75 per the GLI spline tables), and height (in centimeters). Rows with NA in sex, age, or height (or, for GLI 2012, in race) are returned with NA reference values. Race is ignored when year = 2022 (GLI Global equations are race-neutral).

If data also contains any of fev1_measured, fvc_measured, fev1fvc_measured, fef2575_measured, fef75_measured, the corresponding measured value is used to compute a z-score and percent-predicted for that measure (see Value).

year

The year of GLI published equations. Valid options are 2012 (multi-ethnic, requires a race column) and 2022 (race-neutral "GLI Global"; the race column, if present, is ignored). Defaults to 2022, the current ERS/ATS recommendation.

sex, age, height, race

Column references. By default pft_spirometry() reads from sex, age, height, and (for GLI 2012) race. If your data frame names them differently, override via a bare name (sex = Sex), a string (sex = "Sex"), or an rlang injection (sex = !!my_var). The user's original column names are preserved in the output.

Value

The original data frame with extra columns appended for each measure. Every output column carries the GLI year as a suffix so a single result frame can hold multiple equation outputs side-by-side (fev1_pred_2012, fev1_pred_2022, ...).

References

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.

See Also

pft_volumes() and pft_diffusion() for the analogous reference-value functions for lung volumes and diffusion capacity. pft_classify() consumes the LLN columns produced here to assign ATS interpretive patterns. pft_interpret() is the one-call wrapper that combines spirometry, volumes, diffusion, and all downstream interpretation primitives.

Examples

data <- data.frame(sex=c("M","F"),
                   age=c(30.1,5.1),
                   height=c(178,50),
                   race=c("SEAsia","NEAsia"))
pft_spirometry(data)


Classify lung-volume sub-pattern per Stanojevic 2022 Figure 10

Description

Differentiates the six lung-volume sub-patterns described in the 2022 ERS/ATS interpretive standard: Normal lung volumes, Large lungs, Hyperinflation, Simple restriction, Complex restriction, and Mixed disorder. These are the patterns that pft_classify() collapses into "Restricted", "Mixed", and "Obstructed" / "Normal" – this function recovers the finer-grained labels when lung-volume ratios (FRC/TLC and / or RV/TLC) are available.

Typically called via pft_interpret() as part of the one-call workflow; exported for callers who want to apply the sub-pattern classifier to pre-computed columns directly.

Usage

pft_volume_subpattern(
  data,
  year = 2022,
  tlc = tlc,
  tlc_lln = tlc_lln,
  tlc_uln = tlc_uln,
  fev1fvc = fev1fvc,
  fev1fvc_lln = NULL,
  rv_tlc = rv_tlc,
  rv_tlc_uln = rv_tlc_uln,
  frc_tlc = NULL,
  frc_tlc_uln = NULL
)

Arguments

data

A data frame containing at minimum:

  • tlc, tlc_lln, tlc_uln

  • fev1fvc, fev1fvc_lln

  • rv_tlc, rv_tlc_uln

Optional columns to refine the elevated-volumes branch:

  • frc_tlc, frc_tlc_uln

year

GLI year suffix used when looking up the spirometry FEV1/FVC LLN column. Defaults to 2022. Set to match the year argument used in the upstream pft_spirometry() / pft_interpret() call. The TLC and RV/TLC columns (volumes reference) are unsuffixed and are not affected by year.

tlc, tlc_lln, tlc_uln, fev1fvc, fev1fvc_lln, rv_tlc, rv_tlc_uln

Column references for the seven required inputs. Defaults are the canonical names (fev1fvc_lln carries the ⁠_<year>⁠ suffix); override with a bare name, a string, or !!var (see "Column-name overrides" below).

frc_tlc, frc_tlc_uln

Column references for the optional FRC/TLC pair. Default NULL means: auto-pickup if frc_tlc and frc_tlc_uln exist in data, otherwise skip the FRC branch and classify on RV/TLC alone.

Details

Implements the decision tree in Figure 10 of Stanojevic et al. ERJ 2022 (p. 21) verbatim:

TLC < 5th percentile (LLN)?
  YES -> Restriction:
    FRC/TLC OR RV/TLC > 95th percentile (ULN)?
      YES:
        FEV1/FVC < 5th percentile?
          YES -> "Mixed disorder"
          NO  -> "Complex restriction"
      NO    -> "Simple restriction"
  NO:
    TLC > 95th percentile?
      YES (possible hyperinflation):
        FRC/TLC OR RV/TLC > 95th percentile?
          YES -> "Hyperinflation"
          NO  -> "Large lungs"
      NO:
        FRC/TLC OR RV/TLC > 95th percentile?
          YES -> "Hyperinflation"
          NO  -> "Normal lung volumes"

RV/TLC reference ranges are produced by pft_volumes() (per Hall 2021 Table 3 row for RV/TLC). FRC/TLC is not fitted in the Hall 2021 standard; if the caller has FRC/TLC and its ULN available, supply them as columns frc_tlc / frc_tlc_uln to refine the OR-condition. When absent (the typical case), only RV/TLC is consulted – the function degrades gracefully.

Value

The input data with a new volume_subpattern character column appended. Values are one of "Normal lung volumes", "Large lungs", "Hyperinflation", "Simple restriction", "Complex restriction", "Mixed disorder", or NA_character_ if any required column is NA for that row.

Column-name overrides

Each column-reference argument accepts three forms:

Defaults are the canonical pft column names, so callers whose data already follows the convention pass no extra arguments. The optional FRC/TLC pair (frc_tlc, frc_tlc_uln) defaults to NULL to enable canonical-name auto-pickup; pass explicit column references to override.

References

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. Lung-volume sub-patterns defined in Figure 10 (p. 21) and Table 7 (p. 22).

See Also

pft_classify() for the five-band airflow / restriction classification; pft_volumes() to obtain rv_tlc / rv_tlc_uln per Hall 2021; pft_interpret() composes both classifications when the input columns are present.

Examples

# Mixed disorder: TLC < LLN, RV/TLC > ULN, FEV1/FVC < LLN.
data.frame(
  tlc = 4.0, tlc_lln = 5.0, tlc_uln = 7.0,
  fev1fvc = 0.55, fev1fvc_lln_2022 = 0.70,
  rv_tlc = 0.55, rv_tlc_uln = 0.45
) |> pft_volume_subpattern()

# Simple restriction: TLC < LLN, both ratios normal.
data.frame(
  tlc = 4.0, tlc_lln = 5.0, tlc_uln = 7.0,
  fev1fvc = 0.80, fev1fvc_lln_2022 = 0.70,
  rv_tlc = 0.30, rv_tlc_uln = 0.45
) |> pft_volume_subpattern()


Compute lung volume reference values for given demographics

Description

pft_volumes() computes ATS-compliant upper and lower normal limits for lung volume measures including FRC, TLC, RV, ERV, IC, and VC.

Usage

pft_volumes(data, sex = sex, age = age, height = height)

Arguments

data

A data frame containing columns for sex ("M","F"), age (in years, in the range 5-80 per the GLI 2021 spline tables) and height (in centimeters). If data also contains any of frc_measured, tlc_measured, rv_measured, rv_tlc_measured, erv_measured, ic_measured, vc_measured, the corresponding measured value is used to compute a z-score and percent-predicted (see Value).

sex, age, height

Column references. By default pft_volumes() reads from sex, age, and height. Override via a bare name (sex = Sex), a string (sex = "Sex"), or an rlang injection (sex = !!my_var). The user's original column names are preserved in the output.

Value

The original data frame with extra columns appended for each measure:

References

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.

See Also

pft_spirometry() and pft_diffusion() for the analogous reference-value functions. pft_classify() uses TLC and its LLN (produced by this function) to identify restrictive impairments. pft_interpret() composes all three reference functions in one call.

Examples

data <- data.frame(sex=c("M","F"),
                   age=c(30,5.1),
                   height=c(178,50))
pft_volumes(data)


Objects exported from other packages

Description

These objects are imported from other packages. Follow the links below to see their documentation.

tibble

as_tibble