Learn R Programming

matrixCorr (version 0.8.4)

bland_altman_repeated: Bland-Altman for repeated measurements

Description

Repeated-measures Bland-Altman (BA) for method comparison based on a mixed-effects model fitted to subject-time matched paired differences. A subject-specific random intercept accounts for clustering, and (optionally) an AR(1) process captures serial correlation across replicates within subject. The function returns bias (mean difference), limits of agreement (LoA), confidence intervals, and variance components, for either two methods or all pairwise contrasts when \(\ge\)3 methods are supplied.

Required columns / vectors

  • response: numeric measurements.

  • subject: subject identifier (integer/factor/numeric).

  • method: method label with \(\ge\)2 levels (factor/character/integer).

  • time: replicate index used to form pairs; only records where both methods are present for the same subject and time contribute to a given pairwise BA (rows with missing components are dropped before fitting each pair).

Usage

bland_altman_repeated(
  data = NULL,
  response,
  subject,
  method,
  time,
  two = 1.96,
  conf_level = 0.95,
  include_slope = FALSE,
  use_ar1 = FALSE,
  ar1_rho = NA_real_,
  max_iter = 200L,
  tol = 1e-06,
  verbose = FALSE
)

# S3 method for ba_repeated print(x, digits = 3, ci_digits = 3, ...)

# S3 method for ba_repeated_matrix print(x, digits = 3, ci_digits = 3, style = c("pairs", "matrices"), ...)

# S3 method for ba_repeated plot( x, title = "Bland-Altman (repeated measurements)", subtitle = NULL, point_alpha = 0.7, point_size = 2.2, line_size = 0.8, shade_ci = TRUE, shade_alpha = 0.08, smoother = c("none", "loess", "lm"), symmetrize_y = TRUE, show_points = TRUE, ... )

# S3 method for ba_repeated_matrix plot( x, pairs = NULL, against = NULL, facet_scales = c("free_y", "fixed"), title = "Bland-Altman (repeated, pairwise)", point_alpha = 0.6, point_size = 1.8, line_size = 0.7, shade_ci = TRUE, shade_alpha = 0.08, smoother = c("none", "loess", "lm"), show_points = TRUE, ... )

Value

Either a "ba_repeated" object (exactly two methods) or a "ba_repeated_matrix" object (pairwise results when \(\ge\)3 methods).

If "ba_repeated_matrix" (N\(\ge\)3 methods), outputs are:

  • bias \((m \times m)\); estimated mean difference (row - column). Diagonal is NA.

  • sd_loa \((m \times m)\); estimated SD of a single new paired difference for the (row, column) methods, accounting for the random subject intercept and (if enabled) AR(1) correlation.

  • loa_lower, loa_upper \((m \times m)\); limits of agreement for a single measurement pair, computed as \(\mathrm{bias} \pm \mathrm{two}\times \mathrm{sd\_loa}\). Signs follow the row - column convention (e.g., loa_lower[j,i] = -loa_upper[i,j]).

  • width \((m \times m)\); LoA width, loa_upper - loa_lower (= 2 * two * sd_loa).

  • n \((m \times m)\); number of subject-time pairs used in each pairwise BA (complete cases where both methods are present).

  • CI matrices at nominal conf_level (delta method):

    • mean_ci_low, mean_ci_high; CI for the bias.

    • loa_lower_ci_low, loa_lower_ci_high; CI for the lower LoA.

    • loa_upper_ci_low, loa_upper_ci_high; CI for the upper LoA.

  • slope (\(m \times m\); optional); proportional-bias slope (difference vs pair mean) when include_slope = TRUE.

  • sigma2_subject \((m \times m)\); estimated variance of the subject-level random intercept (on differences).

  • sigma2_resid \((m \times m)\); estimated residual variance of a single difference after accounting for the random intercept (and AR(1), if used).

  • use_ar1 (scalar logical); whether AR(1) modeling was requested.

  • ar1_rho (scalar numeric or NA); user-supplied \(\rho\) if a single common value was provided; NA otherwise.

  • ar1_rho_pair (\(m \times m\); optional); \(\rho\) actually used per pair (may be estimated from data or equal to the supplied value). AR(1) blocks require consecutive integer time indices for each subject.

  • ar1_estimated (\(m \times m\); optional logical); for each pair, TRUE if \(\rho\) was estimated internally; FALSE if supplied.

  • methods (character); method level names; matrix rows/columns follow this order.

  • two (scalar); LoA multiplier used (default 1.96).

  • conf_level (scalar); nominal confidence level used for CIs.

  • data_long (data.frame); the long data used for fitting (response, subject, method, time). Included to facilitate plotting/reproducibility; not required for summary methods.

If "ba_repeated" (exactly two methods), outputs are:

  • mean.diffs (scalar); estimated bias (method 2 - method 1).

  • lower.limit, upper.limit (scalars); LoA \(\mu \pm \mathrm{two}\times \mathrm{SD}\) for a single new pair.

  • critical.diff (scalar); two * SD; LoA half-width.

  • two, conf_level (scalars); as above.

  • CI.lines (named numeric); CI bounds for bias and both LoA (*.ci.lower, *.ci.upper) at conf_level.

  • means, diffs (vectors); per-pair means and differences used by plotting helpers.

  • based.on (integer); number of subject-time pairs used.

  • include_slope, beta_slope; whether a proportional-bias slope was estimated and its value (if requested).

  • sigma2_subject, sigma2_resid; variance components as above.

  • use_ar1, ar1_rho, ar1_estimated; AR(1) settings/results as above (scalars for the two-method fit).

Arguments

data

Optional data.frame/data.table containing required columns.

response

Numeric vector (stacked outcomes) or a single character string giving the column name in data.

subject

Subject ID (integer/factor/numeric) or a single character string giving the column name in data.

method

Method label (factor/character/integer; N >= 2 levels) or a single character string giving the column name in data.

time

Integer/numeric replicate/time index (pairs within subject) or a single character string giving the column name in data.

two

Positive scalar; LoA multiple of SD (default 1.96).

conf_level

Confidence level for CIs (default 0.95).

include_slope

Logical. If TRUE, the model includes the pair mean as a fixed effect and estimates a proportional-bias slope. Look at details for deeper information. We recommend fit both with and without the slope. If \(\beta_1\) is materially non-zero over a wide level range, consider a scale transformation (e.g., log or percent-logit) and re-fit without the slope.

use_ar1

Logical; AR(1) within-subject residual correlation.

ar1_rho

AR(1) parameter (|rho|<1).

max_iter, tol

EM control for the backend (defaults 200, 1e-6).

verbose

Logical; print brief progress.

x

A "ba_repeated_matrix" object.

digits

Number of digits for estimates (default 3).

ci_digits

Number of digits for CI bounds (default 3).

...

Additional theme adjustments passed to ggplot2::theme(...) (e.g., plot.title.position = "plot", axis.title.x = element_text(size=11)).

style

Show as pairs or matrix format?

title

Plot title (character scalar). Defaults to "Bland-Altman (repeated measurements)" for two methods and "Bland-Altman (repeated, pairwise)" for the faceted matrix plot.

subtitle

Optional subtitle (character scalar). If NULL, a compact summary is shown using the fitted object.

point_alpha

Numeric in [0, 1]. Transparency for scatter points drawn at (pair mean, pair difference) when point data are available. Passed to ggplot2::geom_point(alpha = ...). Default 0.7.

point_size

Positive numeric. Size of scatter points; passed to ggplot2::geom_point(size = ...). Default 2.2.

line_size

Positive numeric. Line width for horizontal bands (bias and both LoA) and, when requested, the proportional-bias line. Passed to ggplot2::geom_hline(linewidth = ...) (and geom_abline). Default 0.8.

shade_ci

Logical. If TRUE and confidence intervals are available in the object (CI.lines for two methods; *_ci_* matrices for the pairwise case), semi-transparent rectangles are drawn to indicate CI bands for the bias and each LoA. If FALSE, dashed horizontal CI lines are drawn instead. Has no effect if CIs are not present. Default TRUE.

shade_alpha

Numeric in [0, 1]. Opacity of the CI shading rectangles when shade_ci = TRUE. Passed to ggplot2::annotate("rect", alpha = ...). Default 0.08.

smoother

One of "none", "loess", or "lm". Adds an overlaid trend for differences vs means when points are drawn, to visualise proportional bias. "lm" fits a straight line with no SE ribbon; "loess" draws a locally-smoothed curve (span 0.9) with no SE ribbon; "none" draws no smoother. Ignored if show_points = FALSE or if no point data are available.

symmetrize_y

Logical (two-method plot only). If TRUE, the y-axis is centred at the estimated bias and expanded symmetrically to cover all elements used to compute the range (bands, CIs, and points if shown). Default TRUE.

show_points

Logical. If TRUE, per-pair points are drawn when present in the fitted object (two-method path) or when they can be reconstructed from x$data_long and x$mapping (pairwise path). If FALSE or if point data are unavailable, only the bands (and optional CI indicators) are drawn. Default TRUE.

pairs

(Faceted pairwise plot only.) Optional character vector of labels specifying which method contrasts to display. Labels must match the "row - column" convention used by print()/summary() (e.g., "B - A"). Defaults to all upper-triangle pairs.

against

(Faceted pairwise plot only.) Optional single method name. If supplied, facets are restricted to contrasts of the chosen method against all others. Ignored when pairs is provided.

facet_scales

(Faceted pairwise plot only.) Either "free_y" (default) to allow each facet its own y-axis limits, or "fixed" for a common scale across facets. Passed to ggplot2::facet_wrap(scales = ...).

Author

Thiago de Paula Oliveira

Details

The function implements a repeated-measures Bland–Altman (BA) analysis for two or more methods using a linear mixed model fitted to subject–time matched paired differences. For any selected pair of methods \((a,b)\), let $$d_{it} = y_{itb} - y_{ita}, \qquad m_{it} = \tfrac{1}{2}(y_{itb} + y_{ita}),$$ where \(y_{itm}\) denotes the observed value from method \(m\in\{a,b\}\) on subject \(i\) at replicate/time \(t\); \(d_{it}\) is the paired difference (method \(b\) minus method \(a\)); and \(m_{it}\) is the corresponding pair mean. Here \(i=1,\ldots,S\) indexes subjects and \(t\) indexes replicates/time within subject. Only records with both methods present at the same subject and time contribute to that pair.

The fitted per-pair model is $$d_{it} = \beta_0 + \beta_1\, m_{it} + u_i + \varepsilon_{it},$$ with random intercept \(u_i \sim \mathcal{N}(0, \sigma_u^2)\) and within-subject residual vector \(\varepsilon_i\) having covariance \(\operatorname{Cov}(\varepsilon_i) = \sigma_e^2\, \mathbf{R}_i\). When use_ar1 = FALSE, \(\mathbf{R}_i = \mathbf{I}_{n_i}\) (i.i.d.). When use_ar1 = TRUE, \(\mathbf{R}_i = \mathbf{C}_i^{-1}\) and \(\mathbf{C}_i\) encodes an AR(1) precision structure over time (see below). Setting include_slope = FALSE drops the regressor \(m_{it}\) (i.e., \(\beta_1 = 0\)).

AR(1) within-subject correlation

For each subject, observations are first ordered by time. Contiguous blocks satisfy \(t_{k+1} = t_k + 1\); non-contiguous gaps and any negative/NA times are treated as singletons (i.i.d.).

For a contiguous block of length \(L\) and AR(1) parameter \(\rho\) (clipped to \((-0.999, 0.999)\)), the blockwise precision matrix \(\mathbf{C}\) has entries $$\mathbf{C} = \frac{1}{1-\rho^2}\,\begin{bmatrix} 1 & -\rho & & & \\ -\rho & 1+\rho^2 & -\rho & & \\ & \ddots & \ddots & \ddots & \\ & & -\rho & 1+\rho^2 & -\rho \\ & & & -\rho & 1 \end{bmatrix}.$$ The full \(\mathbf{C}_i\) is block-diagonal over contiguous segments, with a small ridge added to the diagonal for numerical stability. Residual covariance is \(\sigma_e^2 \mathbf{R}_i = \sigma_e^2 \mathbf{C}_i^{-1}\).

If use_ar1 = TRUE and ar1_rho is NA, \(\rho\) is estimated in two passes (i) fit the i.i.d. model; (ii) compute detrended residuals within each contiguous block (remove block-specific intercept and linear time), form lag-1 correlations, apply a small-sample bias adjustment \((1-\rho^2)/L\), pool with Fisher's \(z\) (weights \(\approx L-3\)), and refit with the pooled \(\hat\rho\).

Estimation by stabilised EM/GLS

Let \(\mathbf{X}_i\) be the fixed-effects design for subject \(i\) (intercept, and optionally the pair mean). Internally, the pair mean regressor is centred and scaled before fitting to stabilise the slope; estimates are back-transformed to the original units afterwards.

Given current \((\sigma_u^2, \sigma_e^2)\), the marginal precision of \(d_i\) integrates \(u_i\) via a Woodbury/Sherman-Morrison identity: $$\mathbf{V}_i^{-1} \;=\; \sigma_e^{-2}\mathbf{C}_i \;-\; \sigma_e^{-2}\mathbf{C}_i \mathbf{1}\, \Big(\sigma_u^{-2} + \sigma_e^{-2}\mathbf{1}^\top \mathbf{C}_i \mathbf{1}\Big)^{-1} \mathbf{1}^\top \mathbf{C}_i \sigma_e^{-2}.$$ The GLS update is $$\hat\beta \;=\; \Big(\sum_i \mathbf{X}_i^\top \mathbf{V}_i^{-1} \mathbf{X}_i\Big)^{-1} \Big(\sum_i \mathbf{X}_i^\top \mathbf{V}_i^{-1} d_i\Big).$$ Writing \(r_i = d_i - \mathbf{X}_i\hat\beta\), the E-step gives the BLUP of the random intercept and its conditional second moment: $$M_i \;=\; \sigma_u^{-2} + \sigma_e^{-2}\mathbf{1}^\top \mathbf{C}_i \mathbf{1},\qquad \tilde u_i \;=\; M_i^{-1}\,\sigma_e^{-2}\mathbf{1}^\top \mathbf{C}_i r_i,\qquad \mathbb{E}[u_i^2\mid y] \;=\; \tilde u_i^2 + M_i^{-1}.$$ The M-step updates the variance components by $$\hat\sigma_u^2 \;=\; \frac{1}{m}\sum_i \mathbb{E}[u_i^2\mid y], \qquad \hat\sigma_e^2 \;=\; \frac{1}{n}\sum_i \Big\{(r_i - \mathbf{1}\tilde u_i)^\top \mathbf{C}_i (r_i - \mathbf{1}\tilde u_i) + M_i^{-1}\,\mathbf{1}^\top \mathbf{C}_i \mathbf{1}\Big\}.$$ The algorithm employs positive-definite inverses with ridge fallback, trust-region damping of variance updates (ratio-bounded), and clipping of parameters to prevent degeneracy.

Point estimates reported

The reported BA bias is the subject-equal mean of the paired differences, $$\mu_0 \;=\; \frac{1}{m}\sum_{i=1}^m \bar d_i,\qquad \bar d_i = \frac{1}{n_i}\sum_{t=1}^{n_i} d_{it},$$ which is robust to unbalanced replicate counts (\(n_i\)) and coincides with the GLS intercept when subjects contribute equally. If include_slope = TRUE, the proportional-bias slope is estimated against the pair mean \(m_{it}\); internally \(m_{it}\) is centred and scaled and the slope is returned on the original scale.

Proportional bias slope (include_slope)

When include_slope = TRUE, the model augments the mean difference with the pair mean \(m_{it} = \tfrac{1}{2}(y_{itb}+y_{ita})\): $$d_{it} = \beta_0 + \beta_1 m_{it} + u_i + \varepsilon_{it}.$$ Internally \(m_{it}\) is centred and scaled for numerical stability; the reported beta_slope and intercept are back-transformed to the original scale.

\(\beta_1 \neq 0\) indicates level-dependent (proportional) bias, where the difference between methods changes with the overall measurement level. The Bland–Altman limits of agreement produced by this function remain the conventional, horizontal bands centred at the subject-equal bias \(\mu_0\); they are not regression-adjusted LoA.

On an appropriate scale and when the two methods have comparable within-subject variances, the null expectation is \(\beta_1 \approx 0\). However, because \(m_{it}\) contains measurement error from both methods, unequal precisions can produce a spurious slope even if there is no true proportional bias. Under a simple no-bias data-generating model \(y_{itm}=\theta_{it}+c_m+e_{itm}\) with independent errors of variances \(\sigma_a^2,\sigma_b^2\), the expected OLS slope is approximately $$\mathbb{E}[\hat\beta_1] \;\approx\; \dfrac{\tfrac{1}{2}(\sigma_b^2-\sigma_a^2)} {\mathrm{Var}(\theta_{it}) + \tfrac{1}{4}(\sigma_a^2+\sigma_b^2)},$$ showing zero expectation when \(\sigma_a^2=\sigma_b^2\) and attenuation as the level variability \(\mathrm{Var}(\theta_{it})\) increases.

Limits of Agreement (LoA)

A single new paired measurement for a random subject has variance $$\mathrm{Var}(d^\star) \;=\; \sigma_u^2 + \sigma_e^2,$$ so the LoA are $$\mathrm{LoA} \;=\; \mu_0 \;\pm\; \texttt{two}\,\sqrt{\sigma_u^2 + \sigma_e^2}.$$ The argument two is the SD multiplier (default \(z_{1-\alpha/2}\) implied by conf_level, e.g., \(1.96\) at \(95\%\)).

Wald/Delta confidence intervals

CIs for bias and each LoA use a delta method around the EM estimates. The standard error of \(\mu_0\) is based on the dispersion of subject means \(\mathrm{Var}(\mu_0) \approx \mathrm{Var}(\bar d_i)/m\). For \(\mathrm{sd}_{\mathrm{LoA}} = \sqrt{V}\) with \(V=\sigma_u^2+\sigma_e^2\), we can define $$\mathrm{Var}(\mathrm{sd}) \;\approx\; \frac{\mathrm{Var}(V)}{4V}, \quad \mathrm{Var}(V) \;\approx\; \mathrm{Var}(\hat\sigma_u^2) + \mathrm{Var}(\hat\sigma_e^2) + 2\,\mathrm{Cov}(\hat\sigma_u^2,\hat\sigma_e^2).$$ The per-subject contributions used to approximate these terms are: \(A_i=\mathbb{E}[u_i^2\mid y]\) and \(B_i=\{(r_i-\mathbf{1}\tilde u_i)^\top\mathbf{C}_i(r_i-\mathbf{1}\tilde u_i) + M_i^{-1}\mathbf{1}^\top\mathbf{C}_i\mathbf{1}\}/n_i\). Empirical variances of \(A_i\) and \(B_i\) (with replicate-size weighting for \(B_i\)) and their covariance yield \(\mathrm{Var}(\hat\sigma_u^2)\), \(\mathrm{Var}(\hat\sigma_e^2)\) and \(\mathrm{Cov}(\hat\sigma_u^2,\hat\sigma_e^2)\). For a LoA bound \(L_\pm = \mu_0 \pm \texttt{two}\cdot \mathrm{sd}\), the working variance is $$\mathrm{Var}(L_\pm) \;\approx\; \mathrm{Var}(\mu_0) + \texttt{two}^2\,\mathrm{Var}(\mathrm{sd}),$$ and Wald CIs use the normal quantile at conf_level. These are large-sample approximations; with very small \(m\) they may be conservative.

Three or more methods

When \(\ge 3\) methods are supplied, the routine performs the above fit for each unordered pair \((j,k)\) and reassembles matrices. Specifically,

  • Orientation is defined to be row minus column, i.e., \(\texttt{bias}[j,k]\) estimates \(\mathbb{E}(y_k - y_j)\).

  • \(\texttt{bias}\) is antisymmetric (\(b_{jk} = -b_{kj}\)); \(\texttt{sd\_loa}\) and \(\texttt{width}\) are symmetric; LoA obey \(\texttt{loa\_lower}[j,k] = -\texttt{loa\_upper}[k,j]\).

  • \(\texttt{n}[j,k]\) is the number of subject-time pairs used for that contrast (complete cases where both methods are present).

Missingness, time irregularity, and safeguards

Pairs are formed only where both methods are observed at the same subject and time; other records do not influence that pair. AR(1) structure is applied only over contiguous time sequences within subject; gaps break contiguity and revert those positions to i.i.d. Numerically, inverses use a positive-definite solver with adaptive ridge and pseudo-inverse fallback; variance updates are clamped and ratio-damped; \(\rho\) is clipped to \((-0.999,0.999)\).

Examples

Run this code
# -------- Simulate repeated-measures data --------
set.seed(1)

# design (no AR)
# subjects
S   <- 30L
# replicates per subject
Tm  <- 15L
subj <- rep(seq_len(S), each = Tm)
time <- rep(seq_len(Tm), times = S)

# subject signal centered at 0 so BA "bias" won't be driven by the mean level
mu_s  <- rnorm(S, mean = 0, sd = 8)
# constant within subject across replicates
true  <- mu_s[subj]

# common noise (no AR, i.i.d.)
sd_e <- 2
e0   <- rnorm(length(true), 0, sd_e)

# --- Methods ---
# M1: signal + noise
y1 <- true + e0

# M2: same precision as M1; here identical so M3 can be
#     almost perfectly the inverse of both M1 and M2
y2 <- y1 + rnorm(length(true), 0, 0.01)

# M3: perfect inverse of M1 and M2
y3 <- -y1   # = -(true + e0)

# M4: unrelated to all others (pure noise, different scale)
y4 <- rnorm(length(true), 3, 6)

data <- rbind(
  data.frame(y = y1, subject = subj, method = "M1", time = time),
  data.frame(y = y2, subject = subj, method = "M2", time = time),
  data.frame(y = y3, subject = subj, method = "M3", time = time),
  data.frame(y = y4, subject = subj, method = "M4", time = time)
)
data$method <- factor(data$method, levels = c("M1","M2","M3","M4"))

# quick sanity checks
with(data, {
  Y <- split(y, method)
  round(cor(cbind(M1 = Y$M1, M2 = Y$M2, M3 = Y$M3, M4 = Y$M4)), 3)
})

# Run BA (no AR)
ba4 <- bland_altman_repeated(
  data = data,
  response = "y", subject = "subject", method = "method", time = "time",
  two = 1.96, conf_level = 0.95,
  include_slope = FALSE, use_ar1 = FALSE
)
summary(ba4)
plot(ba4)

# -------- Simulate repeated-measures with AR(1) data --------
set.seed(123)
S <- 40L                      # subjects
Tm <- 50L                     # replicates per subject
methods <- c("A","B","C")     # N = 3 methods
rho <- 0.4                    # AR(1) within-subject across time

ar1_sim <- function(n, rho, sd = 1) {
  z <- rnorm(n)
  e <- numeric(n)
  e[1] <- z[1] * sd
  if (n > 1) for (t in 2:n) e[t] <- rho * e[t-1] + sqrt(1 - rho^2) * z[t] * sd
  e
}

# Subject baseline + time trend (latent "true" signal)
subj <- rep(seq_len(S), each = Tm)
time <- rep(seq_len(Tm), times = S)
# subject effects
mu_s  <- rnorm(S, 50, 7)
trend <- rep(seq_len(Tm) - mean(seq_len(Tm)), times = S) * 0.8
true  <- mu_s[subj] + trend

# Method-specific biases (B has +1.5 constant; C has slight proportional bias)
bias  <- c(A = 0, B = 1.5, C = -0.5)
# proportional component on "true"
prop  <- c(A = 0.00, B = 0.00, C = 0.10)

# Build long data: for each method, add AR(1) noise within subject over time
make_method <- function(meth, sd = 3) {
  e <- unlist(lapply(split(seq_along(time), subj),
                     function(ix) ar1_sim(length(ix), rho, sd)))
  y <- true * (1 + prop[meth]) + bias[meth] + e
  data.frame(y = y, subject = subj, method = meth, time = time,
             check.names = FALSE)
}

data <- do.call(rbind, lapply(methods, make_method))
data$method <- factor(data$method, levels = methods)

# -------- Repeated BA (pairwise matrix) ---------------------
baN <- bland_altman_repeated(
  response = data$y, subject = data$subject, method = data$method, time = data$time,
  two = 1.96, conf_level = 0.95,
  include_slope = FALSE,         # estimate proportional bias per pair
  use_ar1 = TRUE # model AR(1) within-subject
)

# Matrices (row - column orientation)
print(baN)
summary(baN)

# Faceted BA scatter by pair
plot(baN, smoother = "lm", facet_scales = "free_y")

# -------- Two-method path (A vs B only) -----------------------------------
data_AB <- subset(data, method %in% c("A","B"))
baAB <- bland_altman_repeated(
  response = data_AB$y, subject = data_AB$subject,
  method = droplevels(data_AB$method), time = data_AB$time,
  include_slope = FALSE, use_ar1 = TRUE, ar1_rho = 0.4
)
print(baAB)
plot(baAB)

Run the code above in your browser using DataLab