--- title: "Reading DMAR Result Tables" author: "Ken Kelley" date: "September 2026" output: rmarkdown::html_vignette: toc: true toc_depth: 2 vignette: > %\VignetteIndexEntry{Reading DMAR Result Tables} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ## One Kind of Result, Everywhere Almost every DMAR function hands back the same kind of object: a tidy table with a `term` column that names each quantity and a `value` column that holds it. You do not need to learn a new object for each analysis, and you do not need to know anything about R's class systems to use what comes back. If you can work with a `data.frame`, you can work with a DMAR result. Here is a confidence interval for a standardized mean difference (Cohen's *d*), computed from summary statistics you might read out of a paper: ``` r x <- ci_smd(smd = 0.5, n_1 = 50, n_2 = 50) x ``` |term |value | |:-----------|:-----| |lower_limit |0.101 | |smd |0.5 | |upper_limit |0.897 | Confidence level: 95% The point estimate sits on the `smd` row; the lower and upper limits of the 95% interval are on their own rows. The confidence level is printed beneath the table so you never have to guess what interval you are looking at. ## What You See Is Rounded; What Is Stored Is Exact The printed table is a *display*. DMAR rounds numbers so the table is easy to read, but it never rounds the numbers it stores. Pull a value out and you get full precision: ``` r x$value[x$term == "smd"] #> [1] 0.5 x$value[x$term == "upper_limit"] #> [1] 0.8969414 ``` This matters whenever you do further arithmetic. The width of the interval, for example, uses the stored numbers, not the three digits you saw on screen: ``` r lo <- x$value[x$term == "lower_limit"] hi <- x$value[x$term == "upper_limit"] hi - lo #> [1] 0.7963557 ``` If you want to *see* more (or fewer) digits, ask the display for them. This changes only what is shown, not what is stored: ``` r print(x, digits = 8) #> term value #> lower_limit 0.10058571 #> smd 0.5 #> upper_limit 0.89694143 #> #> Confidence level: 95% ``` To change the default for the rest of your session, set the option once: ``` r options(dmar.digits = 4) x ``` |term |value | |:-----------|:------| |lower_limit |0.1006 | |smd |0.5 | |upper_limit |0.8969 | Confidence level: 95% ``` r options(dmar.digits = 3) # back to the default ``` ## Getting a Single Number Out Because the result is an ordinary data frame, you select from it the usual ways. Any of these returns the upper confidence limit: ``` r x$value[x$term == "upper_limit"] #> [1] 0.8969414 x[x$term == "upper_limit", "value"] #> [1] 0.8969414 subset(x, term == "upper_limit")$value #> [1] 0.8969414 ``` That is the whole trick: rows are named by `term`, numbers live in `value`, and you index them like any data frame. ## The "Wide" View and the One-Row Summary Sometimes you want the result spread across columns instead of down rows, for example to add a row to a results table or to feed a plot. The broom verbs `tidy()` and `glance()` do this, and they ship with DMAR through the lightweight **generics** package, so nothing extra needs to be installed. `tidy()` returns one row per term with tidy, predictable column names: ``` r generics::tidy(x) #> term estimate ci_lower ci_upper conf_level #> 1 smd 0.5 0.1005857 0.8969414 0.95 ``` `glance()` returns a one-row, model-level summary. For a result that is already a single estimate and its interval, such as `ci_smd()`, the summary is the same one row, because there are no extra model-level statistics to add: ``` r generics::glance(x) #> term estimate ci_lower ci_upper conf_level #> 1 smd 0.5 0.1005857 0.8969414 0.95 ``` The two verbs come apart when there is more to say at the model level. A regression fit by `mlmr()`, for instance, gives one `tidy()` row per coefficient and a `glance()` row of fit statistics (`R2`, AIC, BIC, and so on). The rule is the same everywhere, including the wide tables whose rows are items, construct pairs, ladder rungs, or groups (`content_validity_index()`, `htmt()`, `measurement_invariance()`, `measurement_alignment()`, and the rest): `tidy()` is the per-term table, `glance()` is the one-line summary. The columns use the same names as every other DMAR surface (`estimate`, `se`, `p_value`, `ci_lower`, `ci_upper`), so nothing needs translating between the function's own table and its tidied view. This is a deliberate design choice. A DMAR function always returns the same shape no matter how you call it, so a script that reads `x$value` keeps working. When you want a different shape, you ask for it with a verb (`tidy()` or `glance()`); the function's own output never changes shape underneath you. ## Wide Tables Read the Same Way Some functions are naturally wide already: each row is one term, and several typed columns describe it. The display rules are identical, and each column is formatted on its own terms. Here every effect in a two-factor design gets its own row, with a partial effect size, its confidence limits, the *F* statistic, the degrees of freedom, and the sample size: ``` r ci_eta_squared(aov(len ~ supp * factor(dose), data = ToothGrowth)) ``` |effect |eta_squared |lower_limit |upper_limit |F_value |df_effect |df_error |N | |:-----------------|:-----------|:-----------|:-----------|:-------|:---------|:--------|:--| |supp |0.224 |0.0531 |0.377 |15.6 |1 |54 |60 | |factor(dose) |0.773 |0.638 |0.823 |92 |2 |54 |60 | |supp:factor(dose) |0.132 |0.00132 |0.274 |4.11 |2 |54 |60 | Notice that the degrees of freedom and the sample size print as whole numbers with no decimal point, while the effect sizes and the *F* statistics print to three significant figures. You did not have to configure any of that. ## p-Values and Information Criteria Two kinds of numbers get special, conventional treatment so they read the way researchers expect: - **p-values** print to four decimal places, and a p-value too small to show at that precision prints as `< 0.0001` rather than rounding to `0.0000`. - **Information criteria** (AIC, BIC) and log-likelihoods print to a fixed number of decimal places rather than to significant figures, so a model comparison difference of a few points is never rounded away. As always, the stored values keep full precision; only the display is shaped to the convention. The reference page `?dmar_tbl` documents every display rule and the arguments (`digits`, `digits_p`, `digits_fixed`) that control them. ## In One Paragraph Every DMAR result is a tidy data frame: rows named by `term`, numbers in `value`, printed with sensible rounding but stored at full precision. Read a number by indexing the way you always have; see more digits with `print(x, digits = )`; get a wide row with `tidy()` or a one-line summary with `glance()`. That uniformity is the point. Learn it once and it holds for the whole package.