| Title: | Atmospheric Pressure Peak Area Correction for Gas Chromatography with Standard Detectors |
| Version: | 4.0.3 |
| Description: | Corrects gas-chromatography peak areas for the influence of ambient air pressure on standard detectors open to the ambient atmosphere, such as the flame ionization detector, whose pressure sensitivity was characterised by Bocek, Novak and Janak (1969) <doi:10.1016/S0021-9673(00)99223-9>. Unlike the pressure compensation of Ayers and Clardy (1985) https://patents.google.com/patent/US4512181A, which is combined with a calibration and valid only for a single calibration period of a few days, per-cylinder peak areas are decomposed by principal components into a pressure-correlated component and per-peak drift; a common pressure-sensitivity coefficient (kappa) is estimated with a heavy-tail-robust fit on a drift-reduced signal, and slow drift plus a daily factor are removed. Returns the corrected areas together with a chi-square goodness-of-fit diagnostic. Structural-break detection (package 'strucchange', Zeileis and others (2002) <doi:10.18637/jss.v007.i02>) is provided for episode-level and variance breakpoint analysis. |
| License: | GPL-3 |
| Encoding: | UTF-8 |
| Depends: | R (≥ 4.0.0) |
| Imports: | methods, stats, utils, data.table, dplyr, tibble, purrr, kza, robustbase, strucchange |
| Suggests: | ggplot2, patchwork, knitr, rmarkdown, testthat (≥ 3.0.0) |
| VignetteBuilder: | knitr |
| Config/testthat/edition: | 3 |
| LazyData: | true |
| Config/roxygen2/version: | 8.0.0 |
| NeedsCompilation: | no |
| Packaged: | 2026-07-06 18:31:49 UTC; ruedi |
| Author: | Ruediger Forster |
| Maintainer: | Ruediger Forster <meticulous.measurements@gmail.com> |
| Repository: | CRAN |
| Date/Publication: | 2026-07-15 18:00:02 UTC |
appac: Atmospheric Pressure Peak Area Correction for Gas Chromatography with Standard Detectors
Description
Corrects gas-chromatography peak areas for the influence of ambient air
pressure on standard detectors open to the ambient atmosphere, such as the
flame ionization detector (FID). The entry point is
appac; debias_ct refines the centres,
goodness_of_fit reports the residual reduced chi-square,
get_changepoints and get_variance_changepoints
detect episode level and variance breakpoints, and the
plot_* functions visualise the result.
Limitations
Change-point detection.
-
The level detector only sees compositional breaks.
get_changepointsreads the PC2 score. A composition-preserving (common-mode) level shift – every peak moving by the same factor – is rank-1 and loads onto PC1, so it is invisible to the PC2-based detector. Only breaks that change the relative peak amounts are found. -
Variance (precision) breaks are detectable only when large or well sampled. Detecting a variance ratio
Rover a segment requiresRabove the F-floor for the effective sample size. Variance detection cannot exploit the\sqrt{N}averaging that makes level detection easy – it is bounded by the variance estimate's own sampling noise (\propto \sqrt{2/n}), and serially correlated ("brown") noise shrinks the effectivenfurther. Small steps are, correctly, not flagged. -
Detection is significance-gated and needs enough data. Minimum series length and segment sizes apply; short series or fewer than two usable peaks yield no detection.
Minimum data. appac requires at least 3
samples/cylinders (to separate the common drift), at least 2 peaks,
at least 20 injections per sample, and non-constant areas;
undersized or degenerate input is rejected with an explanatory error.
Pressure-correction (kappa) fit.
-
A whole-series fit is biased by unmodelled level breakpoints. The drift model fits a smooth trend and cannot absorb an abrupt level step; a mid-series step biases
kappa. Detect breakpoints and analyse episodes separately rather than correcting across an uncorrected boundary. -
kappais a single common coefficient fitted across all peaks and cylinders; genuinely peak- or detector-specific sensitivities are not modelled. -
Requires sufficient pressure variation.
kappais the area-pressure slope, so near-constant ambient pressure leaves no signal, andP_refmust lie within the observed pressure range. -
Cannot reduce uncertainty below the repeatability floor. The correction removes the colored low-frequency drift but leaves the irreducible short-term measurement noise.
Missing data.
-
NA is tolerated, with caveats. Up to 30% NA per peak is filled by low-rank imputation (peaks above that are dropped); whole missing injections (staggered dates) are handled by cross-sample reconstruction. But imputation fabricates values, and heavy imputation on adversarial data can destabilise
kappa. -
The grid must be complete: every peak must be present as a row for every injection (
nrowa multiple of the peak count), even where the value is NA.
Model assumptions.
-
Multiplicative (proportional) noise is assumed: the dispersion normalisation models the per-peak standard deviation as proportional to the centre. A peak dominated by additive/constant background noise would be over-weighted; the method targets the proportional-noise regime typical of the FID.
-
A-posteriori only: appac corrects already-measured areas. It has no forecasting ability and makes no prediction beyond the acquired data.
Author(s)
Maintainer: Ruediger Forster meticulous.measurements@gmail.com (ORCID)
Authors:
Ruediger Forster meticulous.measurements@gmail.com (ORCID)
Fitted APPAC model
Description
The object returned by appac: the corrected areas plus the
drift
and pressure-correction models.
Usage
## S4 method for signature 'Appac'
show(object)
## S4 method for signature 'Appac'
print(x, ...)
Arguments
object |
the object to display. |
x |
the |
... |
ignored. |
Slots
samplesPer-cylinder list carrying
raw.area,corrected.area, dates and pressure.trendThe drift / daily-factor model (a
Compensation-class).correctionThe pressure correction (a
Correction-class).
Drift / daily-factor model
Description
The temporal-drift component of a fitted Appac-class object:
the
daily-factor and bias/trend decomposition of the area drift.
Usage
## S4 method for signature 'Compensation'
show(object)
Arguments
object |
the object to display. |
Slots
dateInteger date axis the model is evaluated on.
biasPer-cylinder additive bias.
trendPer-cylinder linear trend.
correlated.featuresThe common daily-factor signal.
correlated.features.scalingScaling applied to the correlated features.
bias.trend.scalingScaling applied to the bias/trend component.
centerPer-sample, per-peak centre (reference) values.
samplesPer-cylinder inputs to the drift model.
Pressure correction model
Description
The pressure-correction component of a fitted Appac-class
object.
Usage
## S4 method for signature 'Correction'
show(object)
Arguments
object |
the object to display. |
Slots
covariatesNames of the correction covariates (e.g. air pressure).
coefficientsFitted coefficient (
kappa) per covariate.reference.valuesReference value (e.g.
P_ref) per covariate.samplesPer-cylinder correction inputs.
fit.dataStored kappa-fit input, used by
plot_area_pressure_fit.
Flame-ionization-detector peak areas for APPAC
Description
An example set of gas-chromatography flame-ionization-detector (FID) injections from a real instrument, with expert-annotated peak integration, used to demonstrate the pressure correction. Long format: one row per injection and peak, from repeated analyses of several control cylinders.
Usage
PLOT_FID
Format
A data frame with 77365 rows and 6 columns:
- sample.name
Cylinder (control sample) identifier.
- injection.date
Date of the injection.
- peak.name
Component / peak identifier.
- retention.time
Peak retention time.
- raw.area
Raw integrated peak area.
- air.pressure
Ambient air pressure (hPa) at the injection.
Synthetic stress-test data for APPAC
Description
A compact, fully synthetic data set with a known ground truth, for
unit
tests and runnable examples. It is deliberately adversarial: three samples
of
largely different composition, ten peaks each spanning a factor-of-five
dynamic
range, measured over three fifty-run episodes separated by two planted
breakpoints, with every classical adversary of the pressure fit stacked on
top
(a small kappa buried under brown (reddened, AR(1) \phi = 0.85)
heavy-tailed Student-t noise, ~2% positive outlier contamination, and a slow
per-sample drift confounded with the pressure signal). The AR(1) coefficient
is
calibrated to the real PLOT_FID noise colour (lag-1
autocorrelation ~0.87, spectral slope ~1.4).
Usage
Synth_data
Format
A data frame with 4500 rows (3 samples x 10 peaks x 150 runs) and 6
columns, matching PLOT_FID:
- sample.name
Sample identifier (
S1,S2,S3).- injection.date
Date of the injection (
IDate).- peak.name
Peak identifier (
pk01..pk10).- retention.time
Peak retention time (elution order).
- raw.area
Raw integrated peak area.
- air.pressure
Ambient air pressure (hPa), shared per run across samples.
Details
The forward model is
area = ref \cdot c_{ep} \cdot (1 + \kappa (P - P_{ref}))
\cdot (1 + drift) \cdot (1 + noise),
with a shared per-run air pressure P (same instrument day, same ambient
pressure across samples). The composition is fixed throughout –
the same samples, the same relative peak amounts – and the two breakpoints
shift only the per-episode centre c_{ep}, multiplicatively and
predictably:
- breakpoint 1
an abrupt
+5\%shift of the centre (episodes 2–3).- breakpoint 2
a further
-2\%centre step and a nominal+200\%step in the measurement-noise variance (innovation sd\times\sqrt{3}, episode 3). The AR(1) memory and the short 50-run episodes pull the realized spread ratio to about1.5\times, so this break sits near the detection floor rather than clearing it (see ‘Limitations’ inappac-package).
Because the shifts are composition-preserving (common-mode) they are, by
design, hard for the trend/PC2 change-point detector to recover – a naive
whole-series appac fit is likewise biased by the
unmodelled
steps. That is the point: the set exercises robustness and motivates episode
splitting rather than promising clean recovery.
Two moments, two detectors. Breakpoint 2 deliberately carries two separable signatures that live in different statistical moments of the per-sample PCA (standardise each peak, then decompose):
the level steps (breakpoint 1, and breakpoint 2's
-2\%step) are a first-moment change – a shift in the mean of the dominant common-mode component (PC1). A trend / level change-point model recovers these (from PC1, not the PC2 thatget_changepointscurrently reads; see there).the variance expansion is a second-moment change – a shift in the spread of an uncorrelated (non-PC1) component, with its mean flat. A mean/trend model is blind to it by construction; it needs a dispersion change-point statistic (e.g. a change-point on the rolling variance of the uncorrelated subspace).
Which component carries the variance step is sample-dependent (an artefact of the whole-series PCA ordering: PC2 in one sample, PC3 or higher in another), so a variance detector must scan the uncorrelated subspace rather than a fixed PC. Physically the two moments are distinct instrument events: a calibration / level shift (mean) versus a loss of precision / repeatability (variance).
The planted ground truth is attached as attr(Synth_data, "truth"), a
list with kappa, P_ref, the breakpoints
(IDate), the center_shift /
center_shift_bp2
/ variance_expansion magnitudes, n_episodes,
runs_per_episode,
dynamic_range, the per-sample per-peak reference composition,
and
the noise parameters. Regenerate with data-raw/Synth_data.R.
See Also
PLOT_FID, appac,
get_changepoints
Examples
truth <- attr(Synth_data, "truth")
truth$kappa # the planted common pressure sensitivity
truth$breakpoints # the two planted episode boundaries
Fit the APPAC pressure correction
Description
Fits the APPAC (Atmospheric Pressure Peak Area Correction) model and returns
the
corrected peak areas with diagnostics. The data must already be
column-checked with check_cols. appac drives the whole
pipeline: pivot the data, decompose it by principal components, estimate the
common pressure sensitivity kappa with a heavy-tail-robust fit on a
drift-reduced signal, estimate the drift / daily-factor model, and divide the
assembled correction out of the raw areas.
Usage
appac(data, ct = NULL, P_ref = 1013.25)
Arguments
data |
Long-format data frame with the canonical columns
(Sample_Name, Peak_Name, Injection_Date, Air_Pressure, Raw_Area), e.g. the
output of |
ct |
Optional fixed per-sample centres (a list, named by sample, of
per-peak reference values). |
P_ref |
Reference pressure (hPa) at which the correction is unity. |
Details
Missing areas are tolerated: a peak with up to 30% NA is kept and its
gaps are imputed by low-rank reconstruction before the fit; whole missing
injections (samples on staggered dates) are handled by the cross-sample
reconstruction in the drift model.
Value
An object of class "Appac": the samples slot holds, per
cylinder, the corrected.area; correction@coefficients the
fitted kappa; and trend the drift / daily-factor model.
See Also
check_cols, debias_ct,
goodness_of_fit
Examples
acn <- list(sample_col = "sample.name", peak_col = "peak.name",
date_col = "injection.date", pressure_col = "air.pressure",
area_col = "raw.area")
dat <- check_cols(PLOT_FID, acn)
ap <- as.numeric(dat[, "Air_Pressure"])
P_ref <- mean(range(ap, na.rm = TRUE)) # mid-range reference pressure
fit <- appac(dat, P_ref = P_ref)
unlist(fit@correction@coefficients) # the fitted common kappa
Validate and canonicalise the input columns
Description
Validates that the input carries the required columns, renames them to the
canonical internal names, and cleans the sample and peak names to valid R
names with make.names. Call this before
appac.
Usage
check_cols(data, appac_colnames, verbose = FALSE)
Arguments
data |
The raw long-format data frame. |
appac_colnames |
Named list mapping the roles ( |
verbose |
If |
Details
The mapping is keyed by role (sample_col, peak_col,
date_col, pressure_col, area_col), so the order in which
appac_colnames lists them does not matter: each role's column is found
by name and relabelled to its canonical name.
Value
The data frame with columns renamed to the canonical names and
sample/peak names cleaned. Stops if a role or column is missing, or if
make.names() would collapse two distinct sample or peak names into
one.
See Also
Examples
acn <- list(sample_col = "sample.name", peak_col = "peak.name",
date_col = "injection.date", pressure_col = "air.pressure",
area_col = "raw.area")
dat <- check_cols(PLOT_FID, acn)
head(dat)
De-bias the per-peak centres
Description
Refines ("de-biases") the per-peak centres by minimising the chi-square of
the
corrected-area residuals about the centre. Sweeps a scale factor cf
around the current centre (the whole-series mean), re-runs
appac
at each cf, fits the per-peak chi-square as a parabola in cf
and
takes its (closed-form) minimum. Trades a little variance for reduced bias.
Usage
debias_ct(Appac, data, P_ref, npt = 20, quiet = FALSE)
Arguments
Appac |
A fitted |
data |
The same column-checked data passed to that |
P_ref |
The same reference pressure (hPa). |
npt |
Number of sweep points for |
quiet |
Suppress the progress dots. |
Value
A list of de-biased centres (one numeric vector per sample), to feed
back as appac(..., ct = <this>).
See Also
Examples
acn <- list(sample_col = "sample.name", peak_col = "peak.name",
date_col = "injection.date", pressure_col = "air.pressure",
area_col = "raw.area")
dat <- check_cols(PLOT_FID, acn)
ap <- as.numeric(dat[, "Air_Pressure"])
P_ref <- mean(range(ap, na.rm = TRUE)) # mid-range reference pressure
fit1 <- appac(dat, P_ref = P_ref)
ct <- debias_ct(fit1, data = dat, P_ref = P_ref, quiet = TRUE)
fit <- appac(dat, ct = ct, P_ref = P_ref) # de-biased second pass
Detect episode level breakpoints across cylinders
Description
Detects abrupt level shifts ("episode" boundaries) in the area series. Per
cylinder the change signal is the second principal-component score of a
one-component PCA on the per-peak standardised areas; a structural-break
model
(breakpoints, gated by an OLS-MOSUM fluctuation
test,
efp / sctest) is fitted
to
the daily-averaged PC2 series and the BIC-optimal breakpoints are kept. The
cross-cylinder breakpoints are the union of the per-cylinder breaks, merged
when
closer than merge_within days. Detection is deterministic.
Usage
get_changepoints(
samples,
alpha = 0.05,
h = 0.15,
max_grid = 250L,
min_seg = 30L,
merge_within = 3
)
Arguments
samples |
Named list, each element with |
alpha |
Significance level of the OLS-MOSUM test that gates whether a cylinder has any break. |
h |
Minimum segment width, as a fraction of the (coarsened) series length, for the MOSUM bandwidth and the breakpoint search. |
max_grid |
Long daily series are coarsened onto at most this many bins before dating, so the search stays fast. |
min_seg |
Minimum daily-series length (days) required to attempt detection. |
merge_within |
Merge breakpoints from different cylinders that fall within this many days of each other. |
Value
An IDate vector of breakpoint dates.
See Also
get_variance_changepoints, appac,
plot_area_date
Examples
acn <- list(sample_col = "sample.name", peak_col = "peak.name",
date_col = "injection.date", pressure_col = "air.pressure",
area_col = "raw.area")
dat <- check_cols(PLOT_FID, acn)
ap <- as.numeric(dat[, "Air_Pressure"])
P_ref <- mean(range(ap, na.rm = TRUE)) # mid-range reference pressure
fit <- appac(dat, P_ref = P_ref)
get_changepoints(fit@samples) # episode breakpoint dates
Detect variance breakpoints across cylinders
Description
Detects abrupt changes in the measurement variance (precision), the
second-moment counterpart of get_changepoints. Per cylinder
the
signal is the daily mean of the per-injection noise energy – the sum of
squared
residuals after removing the leading drop principal component(s) (the
level / pressure / drift) – and a change in its mean is a change in the
noise
variance. The same strucchange machinery (OLS-MOSUM gate +
breakpoints) dates the breaks; results are merged
and
returned as for get_changepoints.
Usage
get_variance_changepoints(
samples,
drop = 1L,
alpha = 0.05,
h = 0.15,
max_grid = 250L,
min_seg = 30L,
merge_within = 3
)
Arguments
samples |
Named list, each element with |
drop |
Number of leading principal components to remove (the level / pressure / drift) before forming the noise energy. |
alpha, h, max_grid, min_seg, merge_within |
As in
|
Details
A pure mean (level) shift leaves the variance unchanged, so this detector
does
not flag the level breaks that get_changepoints finds –
the
two are complementary. Small variance steps sit near the detection floor: a
change is only found when it is significant at alpha.
Value
An IDate vector of variance-breakpoint
dates.
See Also
Examples
acn <- list(sample_col = "sample.name", peak_col = "peak.name",
date_col = "injection.date", pressure_col = "air.pressure",
area_col = "raw.area")
dat <- check_cols(PLOT_FID, acn)
ap <- as.numeric(dat[, "Air_Pressure"])
P_ref <- mean(range(ap, na.rm = TRUE)) # mid-range reference pressure
fit <- appac(dat, P_ref = P_ref)
get_variance_changepoints(fit@samples) # precision-change dates
Goodness of fit of the correction
Description
Tests, per peak, the null hypothesis that the corrected areas are constant (equal to the centre) up to measurement noise. Residual structure left behind by the correction (drift, steps) inflates the statistic.
Usage
goodness_of_fit(Appac)
Arguments
Appac |
A corrected |
Details
The chi-square sums the squared residuals about the centre, each scaled by
the
measurement-noise variance of its peak. That variance is estimated from
successive injections – the von Neumann lag-1 estimator
\sigma^2 = mean(d^2)/2 with d the date-ordered first differences
–
so it captures the irreducible short-term noise and is independent of any
slow
residual drift. Scaling by a noise estimate (rather than by the centre, as a
Pearson form would, which assumes a Poisson variance) makes the statistic
correct for continuous peak areas and dimensionless.
Use the reduced chi-square (chi-square / dof): about 1 means the corrected areas are down to the short-term noise floor (a good correction), above 1 flags residual structure. The p-value is returned too but is of little use here: with thousands of injections the distribution is so tight that any reduced chi-square a touch above 1 gives p near 0.
Value
A named list (per sample) of data frames, one row per peak, with
columns reduced.chisq, chisq, dof and p.value.
See Also
Examples
acn <- list(sample_col = "sample.name", peak_col = "peak.name",
date_col = "injection.date", pressure_col = "air.pressure",
area_col = "raw.area")
dat <- check_cols(PLOT_FID, acn)
ap <- as.numeric(dat[, "Air_Pressure"])
P_ref <- mean(range(ap, na.rm = TRUE)) # mid-range reference pressure
fit <- appac(dat, P_ref = P_ref)
goodness_of_fit(fit)[[1]] # per-peak reduced chi-square, sample 1
Plot peak area versus date
Description
Scatter of raw and corrected peak area against injection date for one (sample, peak), with the reference (centre) line and, optionally, the robust episode change-points as dotted vertical lines.
Usage
plot_area_date(
appac,
sample = 1,
peak = 1,
show_changepoints = TRUE,
size = 12
)
Arguments
appac |
A fitted |
sample |
Sample selector: a cylinder name or a positive index. |
peak |
Peak selector: a peak name or a positive index. |
show_changepoints |
Draw the detected change-points (see
|
size |
Base font size passed to the theme. |
Value
A ggplot object.
See Also
plot_area_pressure, get_changepoints
Examples
acn <- list(sample_col = "sample.name", peak_col = "peak.name",
date_col = "injection.date", pressure_col = "air.pressure",
area_col = "raw.area")
dat <- check_cols(PLOT_FID, acn)
ap <- as.numeric(dat[, "Air_Pressure"])
P_ref <- mean(range(ap, na.rm = TRUE)) # mid-range reference pressure
fit <- appac(dat, P_ref = P_ref)
if (requireNamespace("ggplot2", quietly = TRUE))
plot_area_date(fit, sample = 1, peak = 1, show_changepoints = FALSE)
Plot peak area versus air pressure
Description
Scatter of raw and corrected peak area against air pressure for one (sample, peak), with the reference (centre) line. The pressure dependence visible in the raw area should be flattened in the corrected area.
Usage
plot_area_pressure(appac, sample = 1, peak = 1, size = 12)
Arguments
appac |
A fitted |
sample |
Sample selector: a cylinder name or a positive index. |
peak |
Peak selector: a peak name or a positive index. |
size |
Base font size passed to the theme. |
Value
A ggplot object.
See Also
plot_area_date, plot_residuals
Examples
acn <- list(sample_col = "sample.name", peak_col = "peak.name",
date_col = "injection.date", pressure_col = "air.pressure",
area_col = "raw.area")
dat <- check_cols(PLOT_FID, acn)
ap <- as.numeric(dat[, "Air_Pressure"])
P_ref <- mean(range(ap, na.rm = TRUE)) # mid-range reference pressure
fit <- appac(dat, P_ref = P_ref)
if (requireNamespace("ggplot2", quietly = TRUE))
plot_area_pressure(fit, sample = 1, peak = 1)
Plot the kappa-fit input
Description
Shows the data that actually feeds the kappa fit: the reference-scaled
correlated area, binned by pressure deviation, for each (sample, peak) and
coloured by peak, with the fitted kappa-slope line overlaid. All
series share the single common slope.
Usage
plot_area_pressure_fit(appac, covariate = 1, size = 12)
Arguments
appac |
A fitted |
covariate |
Covariate selector: a covariate name or a positive index (normally the pressure covariate, index 1). |
size |
Base font size passed to the theme. |
Details
Pass the de-biased result – appac(ct = debias_ct(...)) – so
this shows the chi-square-minimum fit (the correct kappa), not the
first pass.
Value
A ggplot object.
See Also
Examples
acn <- list(sample_col = "sample.name", peak_col = "peak.name",
date_col = "injection.date", pressure_col = "air.pressure",
area_col = "raw.area")
dat <- check_cols(PLOT_FID, acn)
ap <- as.numeric(dat[, "Air_Pressure"])
P_ref <- mean(range(ap, na.rm = TRUE)) # mid-range reference pressure
fit1 <- appac(dat, P_ref = P_ref)
ct <- debias_ct(fit1, data = dat, P_ref = P_ref, quiet = TRUE)
fit <- appac(dat, ct = ct, P_ref = P_ref)
if (requireNamespace("ggplot2", quietly = TRUE))
plot_area_pressure_fit(fit)
Residual diagnostic panel
Description
A 2x2 panel of residual diagnostics for one (sample, peak): histogram with a fitted normal, normal Q-Q plot, residual versus date, and residual versus pressure. The residual is the corrected area minus the reference (centre).
Usage
plot_residuals(appac, sample = 1, peak = 1, size = 12)
Arguments
appac |
A fitted |
sample |
Sample selector: a cylinder name or a positive index. |
peak |
Peak selector: a peak name or a positive index. |
size |
Base font size passed to the theme. |
Value
A patchwork object assembling the four panels (requires the
patchwork package).
See Also
plot_area_date, goodness_of_fit
Examples
acn <- list(sample_col = "sample.name", peak_col = "peak.name",
date_col = "injection.date", pressure_col = "air.pressure",
area_col = "raw.area")
dat <- check_cols(PLOT_FID, acn)
ap <- as.numeric(dat[, "Air_Pressure"])
P_ref <- mean(range(ap, na.rm = TRUE)) # mid-range reference pressure
fit <- appac(dat, P_ref = P_ref)
if (requireNamespace("ggplot2", quietly = TRUE) &&
requireNamespace("patchwork", quietly = TRUE))
plot_residuals(fit, sample = 1, peak = 1)