Sometimes you want the fmrigds pipeline without opening
an R session for each run. The CLI gives you the same lazy plan grammar
that you use in R, but in a form that fits scripts, shell pipelines, and
scheduled jobs.
The common path is short: inspect a source, save a plan, preview a small block, then execute it. When you need more control, the same CLI also accepts metadata tables, model formulas, reducer options, post-hoc options, and saved plan JSON.
At the terminal, the workflow looks like this:
fmrigds probe --input group.csv
fmrigds plan --input group.csv --reduce fixed --save-plan plan.json
fmrigds preview --load-plan plan.json --n 3
fmrigds run --load-plan plan.json --out results.h5The chunks below call the same CLI parser from inside R so the examples stay runnable in the vignette.
We’ll use a small long-table input with ROI labels, four subjects,
and two contrasts. The extra time column will become
contrast metadata later.
read.csv(stats_path, stringsAsFactors = FALSE)
#> sample subject contrast time beta var
#> 1 ROI_1 sub-01 baseline 0 0.50 0.04
#> 2 ROI_1 sub-01 task 1 0.80 0.04
#> 3 ROI_1 sub-02 baseline 0 0.40 0.05
#> 4 ROI_1 sub-02 task 1 0.90 0.05
#> 5 ROI_1 sub-03 baseline 0 0.45 0.05
#> 6 ROI_1 sub-03 task 1 1.05 0.05
#> 7 ROI_1 sub-04 baseline 0 0.52 0.05
#> 8 ROI_1 sub-04 task 1 1.12 0.05
#> 9 ROI_2 sub-01 baseline 0 0.60 0.03
#> 10 ROI_2 sub-01 task 1 0.70 0.03
#> 11 ROI_2 sub-02 baseline 0 0.55 0.02
#> 12 ROI_2 sub-02 task 1 0.95 0.02
#> 13 ROI_2 sub-03 baseline 0 0.58 0.02
#> 14 ROI_2 sub-03 task 1 1.10 0.02
#> 15 ROI_2 sub-04 baseline 0 0.62 0.02
#> 16 ROI_2 sub-04 task 1 1.18 0.02If you already know the source and the reducer you want,
run is the shortest path from input table to output
file.
cli_show(
"run",
"--input", stats_path,
"--subset", "contrast=task",
"--reduce", "fixed",
"--out", quick_out
)
#> $ fmrigds 'run' '--input' '/tmp/RtmpfORbjH/file159e1ea3ad84.csv' '--subset' 'contrast=task' '--reduce' 'fixed' '--out' '/tmp/RtmpfORbjH/file159e38ee1d76.csv'
#> GDS
#> dims: 2 x 1 x 1 [sample x subject x contrast]
#> space: sample_labels n=2
#> subjects: 1 (meta)
#> contrasts: 1 (task)
#> assays: beta[location], var[variance], beta_g[?], var_g[?], se_g[?], z_g[?], p_g[?], Q[?], I2[?], n_eff[n_eff], z[z], p[p]head(read.csv(quick_out, stringsAsFactors = FALSE))
#> sample subject contrast beta var beta_g var_g
#> 1 ROI_1 meta task 0.9576471 0.011764706 0.9576471 0.011764706
#> 2 ROI_2 meta task 1.0081818 0.005454545 1.0081818 0.005454545
#> se_g z_g p_g Q I2 n_eff z
#> 1 0.10846523 8.82907 1.055500e-18 1.385529 0.0000000 4 8.82907
#> 2 0.07385489 13.65085 1.995474e-42 5.232727 0.4266852 4 13.65085
#> p
#> 1 1.055500e-18
#> 2 1.995474e-42That is the “easy path”: subset to the contrast you want, reduce across subjects, and write a tidy result. For one-off jobs or shell scripts, that is often enough.
probe tells you what the CLI sees before anything is
computed: the adapter, the dimensions, the assays, and whether the
source has enough structure for the next step.
probe <- cli_json(
"probe",
"--input", stats_path,
"--contrast-data-cols", "time",
"--json"
)
data.frame(
adapter = probe$adapter,
samples = probe$dims[1],
subjects = probe$dims[2],
contrasts = probe$dims[3],
assays = paste(probe$assays, collapse = ", "),
has_contrast_data = probe$has_contrast_data,
stringsAsFactors = FALSE
)
#> adapter samples subjects contrasts assays has_contrast_data
#> 1 tabular 2 4 2 beta, var TRUEHere --contrast-data-cols time promotes the
time column into contrast-level metadata. That matters when
you move from a simple group summary to a model that needs contrast
annotations.
plan is useful when you want to describe the workflow
once, save it, review it, and execute it later.
plan_info <- cli_json(
"plan",
"--input", stats_path,
"--contrast-data-cols", "time",
"--subset", "contrast=task",
"--reduce", "fixed",
"--posthoc", "fdr:bh",
"--save-plan", plan_path,
"--json"
)
knitr::kable(node_table(plan_info$nodes), row.names = FALSE)| op | summary |
|---|---|
| subset_axis | subset |
| reduce | reduce:fixed |
| posthoc | posthoc:fdr:bh |
This is the point where the CLI stops being a thin wrapper around
compute() and becomes a real planning tool. You can keep
the saved JSON under version control, review the steps, and reuse the
same plan across runs.
preview runs a small sample block through the current
plan. That is useful when you want to check the output contract without
writing the full result.
preview_out <- cli_json(
"preview",
"--load-plan", plan_path,
"--n", "1",
"--show-assay", "beta,q",
"--json"
)
preview_out$data
#> sample subject contrast beta q
#> 1 ROI_1 meta task 0.9576 1.0555e-18The preview already includes the derived and post-hoc assays from the saved plan. In practice, this is the fastest way to confirm that a workflow is producing the fields you expect before you launch a longer batch run.
The CLI is not limited to fixed-effects summaries. You can also attach subject-level covariates, specify formulas, and hand reducer-specific options to more complex models.
cli_show(
"preview",
"--input", stats_path,
"--subset", "contrast=task",
"--col-data", cov_path,
"--reduce", "meta:fe_reg",
"--formula", "~ age",
"--show-assay", "coef:age",
"--n", "2"
)
#> $ fmrigds 'preview' '--input' '/tmp/RtmpfORbjH/file159e1ea3ad84.csv' '--subset' 'contrast=task' '--col-data' '/tmp/RtmpfORbjH/file159e71234794.csv' '--reduce' 'meta:fe_reg' '--formula' '~ age' '--show-assay' 'coef:age' '--n' '2'
#> GDS
#> dims: 2 x 1 x 1 [sample x subject x contrast]
#> space: sample_labels n=2
#> subjects: 1 (meta)
#> contrasts: 1 (task)
#> assays: Q[?], df_res[?], coef:(Intercept)[?], coef:age[?], se_coef:(Intercept)[?], se_coef:age[?], z_coef:(Intercept)[?], z_coef:age[?], p_coef:(Intercept)[?], p_coef:age[?]
#> design[1]: (Intercept), age
#> sample subject contrast coef:age
#> ROI_1 meta task 0.02776596
#> ROI_2 meta task 0.03831522The same pattern extends to other advanced switches:
--source-option key=value passes adapter-specific
arguments through gds().--reduce-option key=value passes reducer options
through reduce().--posthoc-option key=value passes method-specific
options through posthoc().--write-option key=value controls exporters when you
write to disk.--load-plan and --save-plan let you mix
shell-driven execution with plan-first workflows.list exposes the registries that the CLI can target. You
can use it to see which reducers and post-hoc methods are available in
the current installation.
reducers <- cli_json("list", "reducers", "--json")
head(
data.frame(
name = reducers$name,
requires = vapply(reducers$requires, paste, collapse = ", ", character(1)),
provides = vapply(reducers$provides, paste, collapse = ", ", character(1)),
stringsAsFactors = FALSE
),
6
)
#> name requires
#> 1 combine:fisher p
#> 2 combine:lancaster p
#> 3 combine:stouffer z
#> 4 lmm:ri beta
#> 5 lmm:ri_knownvar beta, var
#> 6 lmm:ri_slope1 beta
#> provides
#> 1 p_g, chi2, df
#> 2 p_g, chi2, df
#> 3 z_g, p_g
#> 4 coef, se_coef, t_coef, p_coef, df_coef, sigma2, vc_intercept, vc_resid, df_res, logLik, converged, lambda
#> 5 coef, se_coef, t_coef, p_coef, df_coef, sigma2, vc_intercept, vc_resid, sampling_var_mean, df_res, logLik, converged
#> 6 coef, se_coef, t_coef, p_coef, df_coef, sigma2, vc_intercept, vc_slope, vc_cov_intercept_slope, vc_resid, df_res, logLik, converged, lambda_intercept, lambda_slope, lambda_cov_intercept_slope, corr_intercept_slopeIf you are scripting around the CLI, this registry view is a good way to make your wrapper validate method names before a larger run.
Once you are comfortable with the CLI surface, the R vignettes fill in the modeling details:
vignette("fmrigds") for the core lazy workflowvignette("spatial-operations") for mapping, masking,
and alignmentvignette("as-plan-and-spatial-fdr") for post-hoc and
spatial FDR workflowsThe CLI is best when you want a reproducible command surface. When
you need custom transforms or deeply programmatic branching, save a plan
from the CLI and continue in R with load_plan(),
explain_plan(), and compute().