Skip to contents

spicy's tables return a display representation by default – a character data.frame with stars suffixes, en-dash for reference rows, bracketed "[L, U]" confidence intervals, and APA padding on p-values. This accessor returns the typed view that the output engines (Excel, gt, tinytable, flextable, clipboard) consume internally: a fully numeric body with CI pre-split into LL / UL columns, NAs for non-applicable / reference cells, plus per-cell markers and a format specification.

Usage

as_structured(x)

Arguments

x

A spicy table built with output = "default": table_regression(), table_categorical(), table_continuous(), table_continuous_lm(), table_outcome(), table_multiresponse() or the survey twins table_categorical_svy() and table_continuous_svy(). All return the same schema.

Value

A list with the structured view (see Details for the schema).

Details

This is the right entry point for users who want to:

  • Filter coefficients programmatically, e.g. as_structured(tbl)$body[which(as_structured(tbl)$body$p < 0.05), ] (which() drops the structurally empty rows – factor headers, reference levels – whose p is NA; a bare logical index would keep them as all-NA rows).

  • Aggregate raw values across rows, e.g. mean(as_structured(tbl)$body[["B"]], na.rm = TRUE).

  • Build a custom downstream renderer that consumes the same structured contract as spicy's built-in engines.

Schema

  • version – integer contract version (see Versioning below).

  • body – data.frame with a Variable character column, one or more numeric value columns, and four dot-prefixed identity columns at the end. Confidence intervals are split into a lower and an upper column, named as each family names them: "95% CI: LL" / "95% CI: UL" in a regression table (prefixed with the model label in multi-model output), "95% CI LL" / "95% CI UL" in table_continuous(), table_continuous_lm(), table_outcome() and table_continuous_svy(), "CI lower" / "CI upper" for the association measure of table_categorical(), and "<group> % CI lower" / "<group> % CI upper" in table_categorical_svy(). A cell with no value (reference level, term absent from a model, factor header) is NA.

  • body$.variable – source variable of the row: the factor name on a factor header / level / reference row, the term otherwise, and the fit-statistic token ("nobs", "r2", "fixed_effects", ...) on a fit-statistic row.

  • body$.level – the factor level (or the absorbed factor of a fixed-effects block, or the grouping factor of an "N (...)" row); NA outside a factor.

  • body$.row_role – what the row is: "coef", "factor_header", "level", "reference", "fit_stat", "outcome", "vc" (variance component) in a regression table, plus "summary" (a row summarizing one variable), "group" (a row keyed by one level of by), "missing" (a row keyed by the missing value), "none" (the respondents who chose no item of a multiple-response question) and "total" (the Total row of table_categorical() under total_row = TRUE) in the descriptive ones. The role is the key a consumer matches on: "(Missing)" is a display label – translatable, and relabeled through options(spicy.labels) – and the role survives both.

  • body$.indent – display indent depth of the label (0 or 1). Renderers indent .indent > 0 rows; the label text itself is already indented in the character body only.

  • cell_status – per-cell semantics, keyed by column name, one character vector as long as body per column that needs one: "reference" (reference level, in this estimate block and this model), "undefined" (the statistic applies to the row but no number expresses it – an unavailable variance-component standard error, a fit statistic undefined for one model's class), "" otherwise. Both marked statuses display as an en-dash. A cell whose value is NA with no status is absent and displays blank. Columns with no marked cell are omitted.

  • outcome_labels_by_col – for the outcome row (explicit outcome_labels with two or more models), the display label keyed by each model's first structured column name.

  • col_meta – per-column metadata keyed by structured column name (token, model_id, precision, p-style, below-threshold, CI pair / role / label). A column whose cells cannot be reconstructed from one number – the "events/N" counts of show_columns = "n_events" – also carries display_cells, a character vector as long as body holding the display string of each cell (NA where the number formats normally). A renderer must prefer it over the numeric value.

  • stars – NULL unless stars was requested, otherwise a list with thresholds (symbol to p cutoff) and markers (per-cell marker strings, keyed by column name, "" where a cell takes none).

  • spanners – named list mapping a grouping label to its column indices in body: the model labels of a multi-model regression table, the by levels of a categorical one (each spanning its n / % pair).

  • ci_pairs – list of (label, cols) entries describing each CI pair in body.

  • format_spec – global format defaults (decimal mark, digits, p-style, CI level, etc.).

Descriptive tables

The descriptive tables return the same schema; only the col_meta tokens and the row roles they emit differ. table_outcome() has the columns of table_continuous() and the rows of table_categorical(): a "summary" row for the whole sample, then a "factor_header" row per grouping and its "level" (or "missing") rows. The survey twins follow the table they twin, table_categorical_svy() with the interval of each percentage (token "prop_ci") and no association measure.

  • table_categorical() – one "factor_header" row per variable then one "level" (or "missing") row per category, and under total_row = TRUE the "total" row that closes each block, exactly as the console lays them out. Tokens: "n", "pct", "p", "assoc" (the association measure), "assoc_ci" (its bounds) and "smd" (the standardized mean difference, when smd = TRUE). In a by table each group owns an n / % pair with its own spanner, and the margin column is flagged col_meta$<col>$total, never matched on the label "Total".

  • table_continuous() – one "summary" row per variable, or one "group" row per level of by ("missing" for the missing-by group), with the level in .level. Tokens are the show_columns vocabulary itself ("m", "sd", "med", "iqr", "med_iqr", "q1", "q3", "min", "max", "ci", "med_ci", "n", "weighted_n") plus "statistic", "p", "es" and "smd" for the group comparison – the last four are col_meta tokens without being show_columns values, because they belong to a comparison BETWEEN groups rather than to any one group. A statistic another variable displays is absent (NA, no status); one that applies but has no value is "undefined".

  • table_continuous_lm() – one "summary" row per outcome. Tokens: "emmean" (a marginal mean; the column says which level in col_meta$<col>$level, also its display_label: the table heads each mean by its level under the mean symbol), "delta" (the contrast), "b" (a numeric predictor's slope), "ci", "statistic", "p", "r2", "es", "n", "weighted_n".

  • table_multiresponse() – one "summary" row per item, then the rows of none_row: a "none" row (.variable == "none") for the respondents of the base with no item chosen and a "missing" row (.variable == "no_data") for those with no data for the question, then one "fit_stat" row (.variable == "nobs") holding the respondents of each group. Tokens: "n", "pct" (percentage of cases), "pct_responses" (percentage of responses), "statistic" and "p". Each group owns its columns under its own spanner, and the margin is flagged col_meta$<col>$total.

Cells the console builds from more than one number – a "Mdn [Q1, Q3]", a test gloss, an effect size with its interval – keep their numeric anchor in body and their exact printed string in col_meta$<col>$display_cells. So does a cell of a variable that a named digits gives decimals other than the column's precision, which stays the table's digits. stars is always NULL: descriptive tables carry no significance markers.

Under p_adjust, the p-value column holds the adjusted values and its col_meta adds p_adjust (the method), p_adjust_m (the number of tests adjusted together) and p_unadjusted (the raw p-value of each cell, as long as body). In table_categorical() and table_multiresponse(), the p-value column's col_meta also carries test_type, the test of each cell, as long as body ("chi_squared", "n1_chi_squared", "fisher_exact", "fisher_midp", "fisher_exact_mc", NA where the cell holds no p-value).

Versioning

version says which contract an object carries. Version 3 moved row identity out of index vectors and into the body itself:

  • reference_rows and reference_models_by_row become cell_status, which marks the reference cell instead of the whole row – the row-scoped flag was blanking estimate blocks that have no per-level reference at all.

  • factor_header_rows becomes .row_role == "factor_header".

  • fit_stat_rows becomes .row_role == "fit_stat".

  • level_rows becomes .indent > 0.

  • outcome_row becomes .row_role == "outcome".

Index vectors are the structure that corrupts as soon as two bodies are stacked or merged, so they were removed rather than kept alongside. An object carrying an older contract (or none) is refused with the correspondence above; rebuild it with the function that produced it. An object carrying a newer contract than the spicy reading it is refused as well.

Version 3 also opened the accessor to the descriptive families, which had no typed view before: .row_role gained "summary", "group" and "missing", then "none" with table_multiresponse() and "total" with the total_row of table_categorical(). The vocabulary is extended by addition – an existing role never changes meaning.

Version 4 renames one component: in the format_spec of table_categorical(), the number of decimals of the association measure and the SMD is effect_size_digits, the key of the other tables, where it was v_digits (the argument was renamed alike). An object carrying version 3 is refused as an older contract.

See also

Examples

fit <- lm(mpg ~ wt + factor(cyl), data = mtcars)
tbl <- table_regression(fit)
s <- as_structured(tbl)
s$body                               # raw numeric body
#>       Variable          B        SE 95% CI: LL 95% CI: UL            p
#> 1  (Intercept) 33.9907940 1.8877934  30.123824  37.857764 6.257246e-17
#> 2           wt -3.2056133 0.7538957  -4.749898  -1.661328 2.130435e-04
#> 3 factor(cyl):         NA        NA         NA         NA           NA
#> 4     4 (ref.)         NA        NA         NA         NA           NA
#> 5            6 -4.2555824 1.3860728  -7.094824  -1.416341 4.717834e-03
#> 6            8 -6.0708597 1.6522878  -9.455418  -2.686301 9.991893e-04
#> 7            n 32.0000000        NA         NA         NA           NA
#> 8           R²  0.8374325        NA         NA         NA           NA
#> 9      Adj. R²  0.8200146        NA         NA         NA           NA
#>     .variable .level     .row_role .indent
#> 1 (Intercept)   <NA>          coef       0
#> 2          wt   <NA>          coef       0
#> 3 factor(cyl)   <NA> factor_header       0
#> 4 factor(cyl)      4     reference       1
#> 5 factor(cyl)      6         level       1
#> 6 factor(cyl)      8         level       1
#> 7        nobs   <NA>      fit_stat       0
#> 8          r2   <NA>      fit_stat       0
#> 9      adj_r2   <NA>      fit_stat       0
s$body[which(s$body$p < 0.05), ]     # filter significant rows
#>      Variable         B        SE 95% CI: LL 95% CI: UL            p
#> 1 (Intercept) 33.990794 1.8877934  30.123824  37.857764 6.257246e-17
#> 2          wt -3.205613 0.7538957  -4.749898  -1.661328 2.130435e-04
#> 5           6 -4.255582 1.3860728  -7.094824  -1.416341 4.717834e-03
#> 6           8 -6.070860 1.6522878  -9.455418  -2.686301 9.991893e-04
#>     .variable .level .row_role .indent
#> 1 (Intercept)   <NA>      coef       0
#> 2          wt   <NA>      coef       0
#> 5 factor(cyl)      6     level       1
#> 6 factor(cyl)      8     level       1
# which() drops the structural NA rows (headers, reference levels)
s$body$.row_role                     # what each row is
#> [1] "coef"          "coef"          "factor_header" "reference"    
#> [5] "level"         "level"         "fit_stat"      "fit_stat"     
#> [9] "fit_stat"     
s$body[s$body$.variable == "wt", ]   # address a row by its variable
#>   Variable         B        SE 95% CI: LL 95% CI: UL            p .variable
#> 2       wt -3.205613 0.7538957  -4.749898  -1.661328 0.0002130435        wt
#>   .level .row_role .indent
#> 2   <NA>      coef       0
s$col_meta$B                         # column metadata for B
#> $token
#> [1] "b"
#> 
#> $model_id
#> [1] "M1"
#> 
#> $source_field
#> [1] "estimate"
#> 
#> $precision
#> [1] 2
#> 
#> $p_style
#> NULL
#> 
#> $threshold
#> NULL
#> 
#> $signif
#> NULL
#> 
#> $ci_role
#> NULL
#> 
#> $ci_pair
#> NULL
#> 
#> $ci_label
#> NULL
#> 
#> $is_df
#> [1] FALSE
#> 
#> $display_label
#> [1] "B"
#> 
#> $fit_stat_overrides
#> $fit_stat_overrides[[1]]
#> $fit_stat_overrides[[1]]$fit_stat
#> [1] "nobs"
#> 
#> $fit_stat_overrides[[1]]$precision
#> [1] 0
#> 
#> $fit_stat_overrides[[1]]$p_style
#> NULL
#> 
#> $fit_stat_overrides[[1]]$threshold
#> NULL
#> 
#> $fit_stat_overrides[[1]]$row
#> [1] 7
#> 
#> 
#> $fit_stat_overrides[[2]]
#> $fit_stat_overrides[[2]]$fit_stat
#> [1] "r2"
#> 
#> $fit_stat_overrides[[2]]$precision
#> [1] 2
#> 
#> $fit_stat_overrides[[2]]$p_style
#> NULL
#> 
#> $fit_stat_overrides[[2]]$threshold
#> NULL
#> 
#> $fit_stat_overrides[[2]]$row
#> [1] 8
#> 
#> 
#> $fit_stat_overrides[[3]]
#> $fit_stat_overrides[[3]]$fit_stat
#> [1] "adj_r2"
#> 
#> $fit_stat_overrides[[3]]$precision
#> [1] 2
#> 
#> $fit_stat_overrides[[3]]$p_style
#> NULL
#> 
#> $fit_stat_overrides[[3]]$threshold
#> NULL
#> 
#> $fit_stat_overrides[[3]]$row
#> [1] 9
#> 
#> 
#> 

# The same schema on a descriptive table.
ct <- table_categorical(mtcars, c(cyl, gear), by = am)
sc <- as_structured(ct)
sc$body[, c("Variable", ".variable", ".level", ".row_role")]
#>   Variable .variable .level     .row_role
#> 1      cyl       cyl   <NA> factor_header
#> 2        4       cyl      4         level
#> 3        6       cyl      6         level
#> 4        8       cyl      8         level
#> 5     gear      gear   <NA> factor_header
#> 6        3      gear      3         level
#> 7        4      gear      4         level
#> 8        5      gear      5         level
sc$spanners                          # one per `by` group
#> $`0`
#> [1] 2 3
#> 
#> $`1`
#> [1] 4 5
#> 
#> $Total
#> [1] 6 7
#>