Skip to contents

table_continuous() summarizes continuous variables either overall or by a categorical grouping variable. It is designed for readable summary tables in the console and for publication-ready outputs in rendered documents. When by is supplied, it can also add group-comparison tests, test statistics, and effect sizes. Formatting follows APA conventions by default; style = "jama" (or "nejm", "lancet", "annals", "aer") switches the whole table to that journal’s published rules — the House styles section of Summary tables for reporting shows each style and the guideline sentence behind every rule.

Basic usage

Use select to choose the continuous variables you want to summarize:

table_continuous(
  sochealth,
  select = c(bmi, wellbeing_score, life_sat_health)
)
#> Descriptive statistics
#> 
#>  Variable                       │   M     SD     Min    Max    95% CI LL 
#> ────────────────────────────────┼────────────────────────────────────────
#>  Body mass index                │ 25.93   3.72  16.00   38.90    25.72   
#>  WHO-5 wellbeing index (0-100)  │ 69.04  15.62  18.70  100.00    68.16   
#>  Satisfaction with health (1-5) │  3.55   1.25   1.00    5.00     3.48   
#> 
#>  Variable                       │ 95% CI UL   n   
#> ────────────────────────────────┼─────────────────
#>  Body mass index                │   26.14    1188 
#>  WHO-5 wellbeing index (0-100)  │   69.93    1200 
#>  Satisfaction with health (1-5) │    3.62    1192 
#> 
#> Missing values removed: bmi (12), life_sat_health (8).

If you omit select, table_continuous() scans the data frame and keeps numeric columns. Non-numeric columns are dropped silently; set verbose = TRUE to list them in a message:

table_continuous(sochealth)
#> Descriptive statistics
#> 
#>  Variable                                   │    M       SD       Min   
#> ────────────────────────────────────────────┼───────────────────────────
#>  Age (years)                                │   49.26    14.70    25.00 
#>  Monthly household income (CHF)             │ 3833.00  1394.58  1000.00 
#>  WHO-5 wellbeing index (0-100)              │   69.04    15.62    18.70 
#>  Body mass index                            │   25.93     3.72    16.00 
#>  Political position (0 = left, 10 = right)  │    5.48     2.03     0.00 
#>  Satisfaction with health (1-5)             │    3.55     1.25     1.00 
#>  Satisfaction with work (1-5)               │    3.38     1.18     1.00 
#>  Satisfaction with relationships (1-5)      │    3.72     1.10     1.00 
#>  Satisfaction with standard of living (1-5) │    3.40     1.16     1.00 
#>  Survey design weight                       │    1.00     0.41     0.29 
#> 
#>  Variable                                   │   Max    95% CI LL  95% CI UL 
#> ────────────────────────────────────────────┼───────────────────────────────
#>  Age (years)                                │   75.00     48.43      50.10  
#>  Monthly household income (CHF)             │ 7388.00   3754.01    3911.98  
#>  WHO-5 wellbeing index (0-100)              │  100.00     68.16      69.93  
#>  Body mass index                            │   38.90     25.72      26.14  
#>  Political position (0 = left, 10 = right)  │   10.00      5.36       5.60  
#>  Satisfaction with health (1-5)             │    5.00      3.48       3.62  
#>  Satisfaction with work (1-5)               │    5.00      3.31       3.45  
#>  Satisfaction with relationships (1-5)      │    5.00      3.66       3.79  
#>  Satisfaction with standard of living (1-5) │    5.00      3.33       3.46  
#>  Survey design weight                       │    3.45      0.97       1.02  
#> 
#>  Variable                                   │  n   
#> ────────────────────────────────────────────┼──────
#>  Age (years)                                │ 1200 
#>  Monthly household income (CHF)             │ 1200 
#>  WHO-5 wellbeing index (0-100)              │ 1200 
#>  Body mass index                            │ 1188 
#>  Political position (0 = left, 10 = right)  │ 1185 
#>  Satisfaction with health (1-5)             │ 1192 
#>  Satisfaction with work (1-5)               │ 1192 
#>  Satisfaction with relationships (1-5)      │ 1192 
#>  Satisfaction with standard of living (1-5) │ 1192 
#>  Survey design weight                       │ 1200 
#> 
#> Missing values removed: bmi (12), political_position (15), life_sat_health (8), life_sat_work (8), life_sat_relationships (8), life_sat_standard (8).

Grouped summaries

Add by to summarize the same variables across categories. When by is supplied, a Welch-test p-value column is added automatically:

table_continuous(
  sochealth,
  select = c(bmi, wellbeing_score, life_sat_health),
  by = education
)
#> Descriptive statistics by Highest education level
#> 
#>  Variable                       │ Group              M     SD     Min    Max   
#> ────────────────────────────────┼──────────────────────────────────────────────
#>  Body mass index                │ Lower secondary  28.09   3.47  18.20   38.90 
#>                                 │ Upper secondary  26.02   3.43  16.00   37.10 
#>                                 │ Tertiary         24.39   3.52  16.00   33.00 
#> ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
#>  WHO-5 wellbeing index (0-100)  │ Lower secondary  57.22  15.44  18.70   97.90 
#>                                 │ Upper secondary  68.97  13.62  26.70  100.00 
#>                                 │ Tertiary         76.85  13.23  40.40  100.00 
#> ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
#>  Satisfaction with health (1-5) │ Lower secondary   2.71   1.20   1.00    5.00 
#>                                 │ Upper secondary   3.53   1.19   1.00    5.00 
#>                                 │ Tertiary          4.11   1.04   1.00    5.00 
#> 
#>  Variable                       │ Group            95% CI LL  95% CI UL   n  
#> ────────────────────────────────┼────────────────────────────────────────────
#>  Body mass index                │ Lower secondary    27.66      28.51    260 
#>                                 │ Upper secondary    25.73      26.31    534 
#>                                 │ Tertiary           24.04      24.74    394 
#> ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
#>  WHO-5 wellbeing index (0-100)  │ Lower secondary    55.33      59.10    261 
#>                                 │ Upper secondary    67.82      70.12    539 
#>                                 │ Tertiary           75.55      78.15    400 
#> ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
#>  Satisfaction with health (1-5) │ Lower secondary     2.57       2.86    259 
#>                                 │ Upper secondary     3.43       3.63    534 
#>                                 │ Tertiary            4.01       4.21    399 
#> 
#>  Variable                       │ Group              p   
#> ────────────────────────────────┼────────────────────────
#>  Body mass index                │ Lower secondary  <.001 
#>                                 │ Upper secondary        
#>                                 │ Tertiary               
#> ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
#>  WHO-5 wellbeing index (0-100)  │ Lower secondary  <.001 
#>                                 │ Upper secondary        
#>                                 │ Tertiary               
#> ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
#>  Satisfaction with health (1-5) │ Lower secondary  <.001 
#>                                 │ Upper secondary        
#>                                 │ Tertiary               
#> 
#> Missing values removed: bmi (12), life_sat_health (8).

This is the main pattern for reporting continuous variables across groups such as education, sex, treatment arm, or survey wave. Pass p_value = FALSE to suppress the test column and keep the output strictly descriptive.

If you want the same outcomes reported in a linear-model workflow, with heteroskedasticity-consistent or cluster-robust standard errors, case weights, or additive covariate adjustment, use table_continuous_lm() instead:

table_continuous_lm(
  sochealth,
  select = c(bmi, wellbeing_score, life_sat_health),
  by = education,
  vcov = "HC3"
)
#> Continuous outcomes by Highest education level
#> 
#>  Variable                       │ M (Lower secondary)  M (Upper secondary) 
#> ────────────────────────────────┼──────────────────────────────────────────
#>  Body mass index                │        28.09                26.02        
#>  WHO-5 wellbeing index (0-100)  │        57.22                68.97        
#>  Satisfaction with health (1-5) │         2.71                 3.53        
#> 
#>  Variable                       │ M (Tertiary)    p     R²    n   
#> ────────────────────────────────┼─────────────────────────────────
#>  Body mass index                │    24.39      <.001  0.13  1188 
#>  WHO-5 wellbeing index (0-100)  │    76.85      <.001  0.21  1200 
#>  Satisfaction with health (1-5) │     4.11      <.001  0.16  1192 
#> 
#> Note. Std. errors: heteroskedasticity-robust (HC3).
#> Missing values removed: bmi (12), life_sat_health (8).

Add test statistics and effect sizes

Grouped tables can also report the test statistic and an effect size alongside the default p-value column:

table_continuous(
  sochealth,
  select = c(bmi, wellbeing_score, life_sat_health),
  by = education,
  statistic = TRUE,
  effect_size = "auto",
  effect_size_ci = TRUE
)
#> Descriptive statistics by Highest education level
#> 
#>  Variable                       │ Group              M     SD     Min    Max   
#> ────────────────────────────────┼──────────────────────────────────────────────
#>  Body mass index                │ Lower secondary  28.09   3.47  18.20   38.90 
#>                                 │ Upper secondary  26.02   3.43  16.00   37.10 
#>                                 │ Tertiary         24.39   3.52  16.00   33.00 
#> ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
#>  WHO-5 wellbeing index (0-100)  │ Lower secondary  57.22  15.44  18.70   97.90 
#>                                 │ Upper secondary  68.97  13.62  26.70  100.00 
#>                                 │ Tertiary         76.85  13.23  40.40  100.00 
#> ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
#>  Satisfaction with health (1-5) │ Lower secondary   2.71   1.20   1.00    5.00 
#>                                 │ Upper secondary   3.53   1.19   1.00    5.00 
#>                                 │ Tertiary          4.11   1.04   1.00    5.00 
#> 
#>  Variable                       │ Group            95% CI LL  95% CI UL   n  
#> ────────────────────────────────┼────────────────────────────────────────────
#>  Body mass index                │ Lower secondary    27.66      28.51    260 
#>                                 │ Upper secondary    25.73      26.31    534 
#>                                 │ Tertiary           24.04      24.74    394 
#> ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
#>  WHO-5 wellbeing index (0-100)  │ Lower secondary    55.33      59.10    261 
#>                                 │ Upper secondary    67.82      70.12    539 
#>                                 │ Tertiary           75.55      78.15    400 
#> ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
#>  Satisfaction with health (1-5) │ Lower secondary     2.57       2.86    259 
#>                                 │ Upper secondary     3.43       3.63    534 
#>                                 │ Tertiary            4.01       4.21    399 
#> 
#>  Variable                       │ Group                    Test             p   
#> ────────────────────────────────┼───────────────────────────────────────────────
#>  Body mass index                │ Lower secondary  F(2, 654.48) = 87.96   <.001 
#>                                 │ Upper secondary                               
#>                                 │ Tertiary                                      
#> ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
#>  WHO-5 wellbeing index (0-100)  │ Lower secondary  F(2, 638.59) = 144.35  <.001 
#>                                 │ Upper secondary                               
#>                                 │ Tertiary                                      
#> ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
#>  Satisfaction with health (1-5) │ Lower secondary  F(2, 652.08) = 118.74  <.001 
#>                                 │ Upper secondary                               
#>                                 │ Tertiary                                      
#> 
#>  Variable                       │ Group                      ES           
#> ────────────────────────────────┼─────────────────────────────────────────
#>  Body mass index                │ Lower secondary  η² = 0.13 [0.10, 0.17] 
#>                                 │ Upper secondary                         
#>                                 │ Tertiary                                
#> ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
#>  WHO-5 wellbeing index (0-100)  │ Lower secondary  η² = 0.21 [0.17, 0.25] 
#>                                 │ Upper secondary                         
#>                                 │ Tertiary                                
#> ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
#>  Satisfaction with health (1-5) │ Lower secondary  η² = 0.16 [0.13, 0.20] 
#>                                 │ Upper secondary                         
#>                                 │ Tertiary                                
#> 
#> Missing values removed: bmi (12), life_sat_health (8).

Use test = "student" for equal-variance parametric tests or test = "nonparametric" for rank-based comparisons:

table_continuous(
  sochealth,
  select = c(bmi, wellbeing_score),
  by = education,
  test = "nonparametric",
  statistic = TRUE,
  effect_size = "auto"
)
#> Descriptive statistics by Highest education level
#> 
#>  Variable                      │ Group              M     SD     Min    Max   
#> ───────────────────────────────┼──────────────────────────────────────────────
#>  Body mass index               │ Lower secondary  28.09   3.47  18.20   38.90 
#>                                │ Upper secondary  26.02   3.43  16.00   37.10 
#>                                │ Tertiary         24.39   3.52  16.00   33.00 
#> ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
#>  WHO-5 wellbeing index (0-100) │ Lower secondary  57.22  15.44  18.70   97.90 
#>                                │ Upper secondary  68.97  13.62  26.70  100.00 
#>                                │ Tertiary         76.85  13.23  40.40  100.00 
#> 
#>  Variable                      │ Group            95% CI LL  95% CI UL   n  
#> ───────────────────────────────┼────────────────────────────────────────────
#>  Body mass index               │ Lower secondary    27.66      28.51    260 
#>                                │ Upper secondary    25.73      26.31    534 
#>                                │ Tertiary           24.04      24.74    394 
#> ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
#>  WHO-5 wellbeing index (0-100) │ Lower secondary    55.33      59.10    261 
#>                                │ Upper secondary    67.82      70.12    539 
#>                                │ Tertiary           75.55      78.15    400 
#> 
#>  Variable                      │ Group                Test         p   
#> ───────────────────────────────┼───────────────────────────────────────
#>  Body mass index               │ Lower secondary  H(2) = 144.63  <.001 
#>                                │ Upper secondary                       
#>                                │ Tertiary                              
#> ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
#>  WHO-5 wellbeing index (0-100) │ Lower secondary  H(2) = 233.53  <.001 
#>                                │ Upper secondary                       
#>                                │ Tertiary                              
#> 
#>  Variable                      │ Group               ES     
#> ───────────────────────────────┼────────────────────────────
#>  Body mass index               │ Lower secondary  ε² = 0.12 
#>                                │ Upper secondary            
#>                                │ Tertiary                   
#> ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
#>  WHO-5 wellbeing index (0-100) │ Lower secondary  ε² = 0.19 
#>                                │ Upper secondary            
#>                                │ Tertiary                   
#> 
#> Missing values removed: bmi (12).

effect_size = "auto" selects the canonical measure for the chosen test and number of groups: Hedges’ g (parametric, 2 groups), eta-squared (parametric, 3+ groups), rank-biserial r (nonparametric, 2 groups), or epsilon-squared (nonparametric, 3+ groups). To pick a specific measure explicitly, pass its character name:

table_continuous(
  sochealth,
  select = wellbeing_score,
  by = sex,
  effect_size = "hedges_g",
  effect_size_ci = TRUE
)
#> Descriptive statistics by Sex
#> 
#>  Variable                      │ Group     M     SD     Min    Max    95% CI LL 
#> ───────────────────────────────┼────────────────────────────────────────────────
#>  WHO-5 wellbeing index (0-100) │ Female  67.16  14.80  19.60  100.00    65.99   
#>                                │ Male    71.05  16.23  18.70  100.00    69.73   
#> 
#>  Variable                      │ Group   95% CI UL   n     p   
#> ───────────────────────────────┼───────────────────────────────
#>  WHO-5 wellbeing index (0-100) │ Female    68.33    620  <.001 
#>                                │ Male      72.37    580        
#> 
#>  Variable                      │ Group              ES            
#> ───────────────────────────────┼──────────────────────────────────
#>  WHO-5 wellbeing index (0-100) │ Female  g = -0.25 [-0.36, -0.14] 
#>                                │ Male

Allowed values are "none" (default), "auto" (= legacy TRUE), "hedges_g", "eta_sq", "r_rb", and "epsilon_sq". Incompatible explicit choices (e.g. "eta_sq" with two groups, or "hedges_g" with test = "nonparametric") trigger an actionable error.

When you need the underlying columns for further processing, use output = "data.frame":

table_continuous(
  sochealth,
  select = c(bmi, wellbeing_score),
  by = education,
  statistic = TRUE,
  effect_size = "auto",
  output = "data.frame"
)
#>          variable                         label           group     mean
#> 1             bmi               Body mass index Lower secondary 28.08731
#> 2             bmi               Body mass index Upper secondary 26.01891
#> 3             bmi               Body mass index        Tertiary 24.39036
#> 4 wellbeing_score WHO-5 wellbeing index (0-100) Lower secondary 57.21571
#> 5 wellbeing_score WHO-5 wellbeing index (0-100) Upper secondary 68.96920
#> 6 wellbeing_score WHO-5 wellbeing index (0-100)        Tertiary 76.85250
#>          sd  min   max ci_lower ci_upper median    q1     q3    iqr
#> 1  3.471744 18.2  38.9 27.66333 28.51129   28.2 25.70 29.900  4.200
#> 2  3.434736 16.0  37.1 25.72693 26.31090   26.1 23.50 28.500  5.000
#> 3  3.520150 16.0  33.0 24.04170 24.73901   24.5 22.00 26.775  4.775
#> 4 15.444587 18.7  97.9 55.33323 59.09819   57.9 45.40 68.500 23.100
#> 5 13.621193 26.7 100.0 67.81669 70.12172   69.5 60.85 77.300 16.450
#> 6 13.226818 40.4 100.0 75.55235 78.15265   77.2 68.60 86.150 17.550
#>   med_ci_lower med_ci_upper   n weighted_n   test_type statistic df1      df2
#> 1         27.6         28.7 260         NA welch_anova  87.95902   2 654.4758
#> 2         25.7         26.5 534         NA        <NA>        NA  NA       NA
#> 3         24.1         24.9 394         NA        <NA>        NA  NA       NA
#> 4         55.5         60.2 261         NA welch_anova 144.35083   2 638.5873
#> 5         68.1         70.9 539         NA        <NA>        NA  NA       NA
#> 6         76.2         78.5 400         NA        <NA>        NA  NA       NA
#>        p.value es_type  es_value es_ci_lower es_ci_upper smd_type smd_value
#> 1 1.467916e-34  eta_sq 0.1307679  0.09667861   0.1654516     <NA>        NA
#> 2           NA    <NA>        NA          NA          NA     <NA>        NA
#> 3           NA    <NA>        NA          NA          NA     <NA>        NA
#> 4 1.888362e-52  eta_sq 0.2081970  0.16901207   0.2461732     <NA>        NA
#> 5           NA    <NA>        NA          NA          NA     <NA>        NA
#> 6           NA    <NA>        NA          NA          NA     <NA>        NA

Balance: the standardized mean difference

A baseline table comparing two arms is usually read for balance, not for significance. smd = TRUE adds the standardized mean difference of the Table 1 literature, and the idiomatic balance table drops the p column at the same time:

table_continuous(
  sochealth,
  select = c(age, bmi, wellbeing_score),
  by = sex,
  smd = TRUE,
  p_value = FALSE
)
#> Descriptive statistics by Sex
#> 
#>  Variable                      │ Group     M     SD     Min    Max    95% CI LL 
#> ───────────────────────────────┼────────────────────────────────────────────────
#>  Age (years)                   │ Female  49.38  14.91  25.00   75.00    48.20   
#>                                │ Male    49.14  14.50  25.00   75.00    47.96   
#> ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
#>  Body mass index               │ Female  25.69   3.78  16.00   38.90    25.39   
#>                                │ Male    26.20   3.64  16.00   37.70    25.90   
#> ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
#>  WHO-5 wellbeing index (0-100) │ Female  67.16  14.80  19.60  100.00    65.99   
#>                                │ Male    71.05  16.23  18.70  100.00    69.73   
#> 
#>  Variable                      │ Group   95% CI UL   n    SMD  
#> ───────────────────────────────┼───────────────────────────────
#>  Age (years)                   │ Female    50.55    620   0.02 
#>                                │ Male      50.32    580        
#> ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
#>  Body mass index               │ Female    25.98    616  -0.14 
#>                                │ Male      26.50    572        
#> ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
#>  WHO-5 wellbeing index (0-100) │ Female    68.33    620  -0.25 
#>                                │ Male      72.37    580        
#> 
#> Missing values removed: bmi (12). SMD = standardized mean difference (Female - Male); |SMD| > 0.1 is the usual imbalance threshold.

The two arguments are independent — turning the SMD on turns nothing else off, so you have to write both. The rule of thumb the table note quotes is |SMD| > 0.1; spicy prints the number and never highlights it.

The column is signed, group 1 minus group 2 in the order the table displays the groups, so the direction of an imbalance is readable without going back to the two means. It requires exactly two groups: a by with three or more is refused rather than averaged over pairs, because an average over pairs has no published reading and can sit under 0.1 while one pair sits well over it.

It is not the effect size

The SMD and Hedges’ g look alike and are never the same number. On sochealth the two agree to two decimals, which hides the point, so here they are on a small unbalanced sample where they cannot hide:

small <- sochealth[c(1:6, 601:604), c("sex", "bmi")]
table_continuous(
  small,
  select = bmi,
  by = sex,
  smd = TRUE,
  effect_size = "hedges_g",
  effect_size_digits = 4
)
#> Descriptive statistics by Sex
#> 
#>  Variable        │ Group     M     SD    Min    Max   95% CI LL  95% CI UL  n 
#> ─────────────────┼────────────────────────────────────────────────────────────
#>  Body mass index │ Female  25.03  3.74  19.50  29.10    21.57      28.49    7 
#>                  │ Male    30.20  3.03  27.80  33.60    22.68      37.72    3 
#> 
#>  Variable        │ Group    p        ES         SMD   
#> ─────────────────┼────────────────────────────────────
#>  Body mass index │ Female  .072  g = -1.3057  -1.5194 
#>                  │ Male                               
#> 
#> SMD = standardized mean difference (Female - Male); |SMD| > 0.1 is the usual imbalance threshold.

Both standardize a mean difference, but by different denominators. Austin’s SMD divides by the root mean of the two group variances; g divides by the degrees-of-freedom pooled SD and then multiplies by the small-sample correction J. Two consequences, both worth knowing:

  • At equal group sizes the two denominators coincide, so the SMD is Cohen’s d. At unequal sizes it is not.
  • g is never the SMD, at any sample size. With equal groups the ratio g / SMD is exactly J: 0.80 at n = 3 per group, 0.96 at n = 10, approaching 1 only as the sample grows. Reading one for the other costs 20% on a small trial.

Read each for what it is: the SMD is a balance diagnostic, g is an effect size. It also follows that the SMD carries no confidence interval and no p-value — attaching one would put back the test reasoning the balance literature asks the reader to drop.

Selecting variables

select supports tidyselect helpers:

table_continuous(
  sochealth,
  select = starts_with("life_sat"),
  by = sex
)
#> Descriptive statistics by Sex
#> 
#>  Variable                                   │ Group    M     SD   Min   Max  
#> ────────────────────────────────────────────┼────────────────────────────────
#>  Satisfaction with health (1-5)             │ Female  3.51  1.25  1.00  5.00 
#>                                             │ Male    3.59  1.25  1.00  5.00 
#> ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
#>  Satisfaction with work (1-5)               │ Female  3.32  1.17  1.00  5.00 
#>                                             │ Male    3.44  1.20  1.00  5.00 
#> ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
#>  Satisfaction with relationships (1-5)      │ Female  3.71  1.09  1.00  5.00 
#>                                             │ Male    3.74  1.10  1.00  5.00 
#> ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
#>  Satisfaction with standard of living (1-5) │ Female  3.37  1.16  1.00  5.00 
#>                                             │ Male    3.42  1.17  1.00  5.00 
#> 
#>  Variable                                   │ Group   95% CI LL  95% CI UL   n  
#> ────────────────────────────────────────────┼───────────────────────────────────
#>  Satisfaction with health (1-5)             │ Female    3.41       3.61     616 
#>                                             │ Male      3.49       3.69     576 
#> ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
#>  Satisfaction with work (1-5)               │ Female    3.23       3.41     615 
#>                                             │ Male      3.34       3.54     577 
#> ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
#>  Satisfaction with relationships (1-5)      │ Female    3.62       3.79     615 
#>                                             │ Male      3.65       3.83     577 
#> ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
#>  Satisfaction with standard of living (1-5) │ Female    3.28       3.46     615 
#>                                             │ Male      3.33       3.52     577 
#> 
#>  Variable                                   │ Group    p   
#> ────────────────────────────────────────────┼──────────────
#>  Satisfaction with health (1-5)             │ Female  .267 
#>                                             │ Male         
#> ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌
#>  Satisfaction with work (1-5)               │ Female  .073 
#>                                             │ Male         
#> ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌
#>  Satisfaction with relationships (1-5)      │ Female  .570 
#>                                             │ Male         
#> ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌
#>  Satisfaction with standard of living (1-5) │ Female  .453 
#>                                             │ Male         
#> 
#> Missing values removed: life_sat_health (8), life_sat_work (8), life_sat_relationships (8), life_sat_standard (8).

For more programmatic selection, set regex = TRUE:

table_continuous(
  sochealth,
  select = "^life_sat",
  regex = TRUE,
  by = education,
  output = "data.frame"
)
#>                  variable                                      label
#> 1         life_sat_health             Satisfaction with health (1-5)
#> 2         life_sat_health             Satisfaction with health (1-5)
#> 3         life_sat_health             Satisfaction with health (1-5)
#> 4           life_sat_work               Satisfaction with work (1-5)
#> 5           life_sat_work               Satisfaction with work (1-5)
#> 6           life_sat_work               Satisfaction with work (1-5)
#> 7  life_sat_relationships      Satisfaction with relationships (1-5)
#> 8  life_sat_relationships      Satisfaction with relationships (1-5)
#> 9  life_sat_relationships      Satisfaction with relationships (1-5)
#> 10      life_sat_standard Satisfaction with standard of living (1-5)
#> 11      life_sat_standard Satisfaction with standard of living (1-5)
#> 12      life_sat_standard Satisfaction with standard of living (1-5)
#>              group     mean        sd min max ci_lower ci_upper median q1  q3
#> 1  Lower secondary 2.714286 1.2021575   1   5 2.567189 2.861382      3  2 3.5
#> 2  Upper secondary 3.533708 1.1853493   1   5 3.432943 3.634473      4  3 5.0
#> 3         Tertiary 4.110276 1.0432216   1   5 4.007602 4.212950      4  3 5.0
#> 4  Lower secondary 2.570881 1.1467994   1   5 2.431102 2.710660      3  2 3.0
#> 5  Upper secondary 3.422430 1.1037312   1   5 3.328691 3.516169      4  3 4.0
#> 6         Tertiary 3.851010 1.0314174   1   5 3.749112 3.952909      4  3 5.0
#> 7  Lower secondary 3.023077 1.2268891   1   5 2.873246 3.172908      3  2 4.0
#> 8  Upper secondary 3.743446 0.9645227   1   5 3.661453 3.825439      4  3 5.0
#> 9         Tertiary 4.158291 0.9322485   1   5 4.066423 4.250159      4  4 5.0
#> 10 Lower secondary 2.666667 1.1635489   1   5 2.524846 2.808487      3  2 3.0
#> 11 Upper secondary 3.387218 1.1065913   1   5 3.292970 3.481466      4  3 4.0
#> 12        Tertiary 3.887218 0.9588582   1   5 3.792847 3.981589      4  3 5.0
#>    iqr med_ci_lower med_ci_upper   n weighted_n   test_type statistic df1
#> 1  1.5            3            3 259         NA welch_anova 118.73585   2
#> 2  2.0            3            4 534         NA        <NA>        NA  NA
#> 3  2.0            4            5 399         NA        <NA>        NA  NA
#> 4  1.0            2            3 261         NA welch_anova 105.98821   2
#> 5  1.0            3            4 535         NA        <NA>        NA  NA
#> 6  2.0            4            4 396         NA        <NA>        NA  NA
#> 7  2.0            3            3 260         NA welch_anova  82.35074   2
#> 8  2.0            4            4 534         NA        <NA>        NA  NA
#> 9  1.0            4            5 398         NA        <NA>        NA  NA
#> 10 1.0            2            3 261         NA welch_anova 101.31672   2
#> 11 1.0            3            4 532         NA        <NA>        NA  NA
#> 12 2.0            4            4 399         NA        <NA>        NA  NA
#>         df2      p.value smd_type smd_value
#> 1  652.0775 1.063917e-44     <NA>        NA
#> 2        NA           NA     <NA>        NA
#> 3        NA           NA     <NA>        NA
#> 4  651.9434 1.398117e-40     <NA>        NA
#> 5        NA           NA     <NA>        NA
#> 6        NA           NA     <NA>        NA
#> 7  617.9668 1.969764e-32     <NA>        NA
#> 8        NA           NA     <NA>        NA
#> 9        NA           NA     <NA>        NA
#> 10 648.7723 5.105889e-39     <NA>        NA
#> 11       NA           NA     <NA>        NA
#> 12       NA           NA     <NA>        NA

Use exclude when you want a broad selection with one or two explicit removals:

table_continuous(
  sochealth,
  select = c(bmi, wellbeing_score, life_sat_health, life_sat_work),
  exclude = "life_sat_work",
  by = sex
)
#> Descriptive statistics by Sex
#> 
#>  Variable                       │ Group     M     SD     Min    Max   
#> ────────────────────────────────┼─────────────────────────────────────
#>  Body mass index                │ Female  25.69   3.78  16.00   38.90 
#>                                 │ Male    26.20   3.64  16.00   37.70 
#> ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
#>  WHO-5 wellbeing index (0-100)  │ Female  67.16  14.80  19.60  100.00 
#>                                 │ Male    71.05  16.23  18.70  100.00 
#> ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
#>  Satisfaction with health (1-5) │ Female   3.51   1.25   1.00    5.00 
#>                                 │ Male     3.59   1.25   1.00    5.00 
#> 
#>  Variable                       │ Group   95% CI LL  95% CI UL   n     p   
#> ────────────────────────────────┼──────────────────────────────────────────
#>  Body mass index                │ Female    25.39      25.98    616   .018 
#>                                 │ Male      25.90      26.50    572        
#> ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
#>  WHO-5 wellbeing index (0-100)  │ Female    65.99      68.33    620  <.001 
#>                                 │ Male      69.73      72.37    580        
#> ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
#>  Satisfaction with health (1-5) │ Female     3.41       3.61    616   .267 
#>                                 │ Male       3.49       3.69    576        
#> 
#> Missing values removed: bmi (12), life_sat_health (8).

Handling missing values

Missing values can never enter a mean, so they are always excluded from each variable’s statistics. The table discloses the exclusion in a note rather than staying silent:

table_continuous(
  sochealth,
  select = c(bmi, life_sat_health)
)
#> Descriptive statistics
#> 
#>  Variable                       │   M     SD    Min    Max   95% CI LL 
#> ────────────────────────────────┼──────────────────────────────────────
#>  Body mass index                │ 25.93  3.72  16.00  38.90    25.72   
#>  Satisfaction with health (1-5) │  3.55  1.25   1.00   5.00     3.48   
#> 
#>  Variable                       │ 95% CI UL   n   
#> ────────────────────────────────┼─────────────────
#>  Body mass index                │   26.14    1188 
#>  Satisfaction with health (1-5) │    3.62    1192 
#> 
#> Missing values removed: bmi (12), life_sat_health (8).

Missing values in by are removed by default (drop_na = TRUE), and the removal is again disclosed rather than silent – here in the note (“Rows with missing income_group removed”) and in a warning:

table_continuous(
  sochealth,
  select = bmi,
  by = income_group
)
#> Warning: 18 observation(s) with NA in `income_group` were excluded.
#> Descriptive statistics by Household income group
#> 
#>  Variable        │ Group           M     SD    Min    Max   95% CI LL 
#> ─────────────────┼────────────────────────────────────────────────────
#>  Body mass index │ Low           26.58  3.94  16.00  38.90    26.08   
#>                  │ Lower middle  26.19  3.47  16.00  37.30    25.84   
#>                  │ Upper middle  25.66  3.89  16.00  37.70    25.24   
#>                  │ High          25.15  3.43  16.00  35.00    24.68   
#> 
#>  Variable        │ Group         95% CI UL   n     p   
#> ─────────────────┼─────────────────────────────────────
#>  Body mass index │ Low             27.07    246  <.001 
#>                  │ Lower middle    26.53    385        
#>                  │ Upper middle    26.09    325        
#>                  │ High            25.61    214        
#> 
#> Missing values removed: bmi (12). Rows with missing income_group removed: 18.

Set drop_na = FALSE to display those rows as a dedicated “(Missing)” group instead. The group-comparison test and effect size still cover the observed groups only, matching table_categorical():

table_continuous(
  sochealth,
  select = bmi,
  by = income_group,
  drop_na = FALSE
)
#> Descriptive statistics by Household income group
#> 
#>  Variable        │ Group           M     SD    Min    Max   95% CI LL 
#> ─────────────────┼────────────────────────────────────────────────────
#>  Body mass index │ Low           26.58  3.94  16.00  38.90    26.08   
#>                  │ Lower middle  26.19  3.47  16.00  37.30    25.84   
#>                  │ Upper middle  25.66  3.89  16.00  37.70    25.24   
#>                  │ High          25.15  3.43  16.00  35.00    24.68   
#>                  │ (Missing)     25.83  4.08  18.50  31.80    23.80   
#> 
#>  Variable        │ Group         95% CI UL   n     p   
#> ─────────────────┼─────────────────────────────────────
#>  Body mass index │ Low             27.07    246  <.001 
#>                  │ Lower middle    26.53    385        
#>                  │ Upper middle    26.09    325        
#>                  │ High            25.61    214        
#>                  │ (Missing)       27.86     18        
#> 
#> Missing values removed: bmi (12).

Weights

Survey data usually comes with case weights, and sochealth carries one. Passing it weights every displayed statistic — mean, SD, quantiles, extremes, and the CI of the mean — and the table says so in its note:

table_continuous(
  sochealth,
  select = c(bmi, wellbeing_score),
  weights = weight,
  rescale = TRUE
)
#> Descriptive statistics
#> 
#>  Variable                      │   M     SD     Min    Max    95% CI LL 
#> ───────────────────────────────┼────────────────────────────────────────
#>  Body mass index               │ 25.72   3.69  16.00   38.90    25.51   
#>  WHO-5 wellbeing index (0-100) │ 68.78  15.54  18.70  100.00    67.90   
#> 
#>  Variable                      │ 95% CI UL   n   
#> ───────────────────────────────┼─────────────────
#>  Body mass index               │   25.94    1188 
#>  WHO-5 wellbeing index (0-100) │   69.66    1200 
#> 
#> Statistics weighted by weight. Missing values removed: bmi (12).

Two conventions coexist for weighted statistics, and spicy makes the choice explicit instead of silent. Without rescale, weights are taken as frequencies: integer weights reproduce, exactly, the statistics of the data with each row repeated w times — the reading of SPSS’s WEIGHT BY and Stata’s fweight. With rescale = TRUE, weights are first normalised to sum to the number of observations — the sampling-weights reading, invariant to the scale of the weights, whose SD matches Stata’s aweight and survey::svyvar(). For a survey weight like this one, rescale = TRUE is the reading you want. The mean is the same under both (it is a ratio); the SD and the weighted_n column differ. The exact formulas and the cross-software correspondence are in the Weights section of ?table_continuous.

Two things are deliberately refused under weights rather than silently wrong: the median confidence interval (an order-statistic interval with no weighted version), and the group tests and effect sizes — a t-test printed next to weighted descriptives would itself be unweighted. For weighted group comparisons, table_continuous_lm() takes the same weights argument and fits them properly:

# Weighted descriptives by group: turn the test columns off
table_continuous(
  sochealth,
  select = bmi,
  by = sex,
  weights = weight,
  rescale = TRUE,
  p_value = FALSE
)
# Weighted group comparison: the model-based tool
table_continuous_lm(sochealth, select = bmi, by = sex, weights = weight)

The standardized mean difference is the exception, and passes under weights:

table_continuous(
  sochealth,
  select = c(age, bmi),
  by = sex,
  weights = weight,
  rescale = TRUE,
  smd = TRUE,
  p_value = FALSE
)
#> Descriptive statistics by Sex
#> 
#>  Variable        │ Group     M     SD     Min    Max   95% CI LL  95% CI UL 
#> ─────────────────┼──────────────────────────────────────────────────────────
#>  Age (years)     │ Female  45.14  14.48  25.00  75.00    44.03      46.25   
#>                  │ Male    44.86  13.96  25.00  75.00    43.68      46.03   
#> ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
#>  Body mass index │ Female  25.51   3.75  16.00  38.90    25.22      25.80   
#>                  │ Male    25.98   3.61  16.00  37.70    25.67      26.29   
#> 
#>  Variable        │ Group    n    SMD  
#> ─────────────────┼────────────────────
#>  Age (years)     │ Female  620   0.02 
#>                  │ Male    580        
#> ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
#>  Body mass index │ Female  616  -0.13 
#>                  │ Male    572        
#> 
#> Statistics weighted by weight. Missing values removed: bmi (12). SMD = standardized mean difference (Female - Male); |SMD| > 0.1 is the usual imbalance threshold.

It is a descriptive balance diagnostic with no p and no interval — which is precisely why the balance literature substitutes it for the test — and it is computed from the same weighted means and variances the M and SD columns display, so it cannot be unweighted inference sitting next to weighted descriptives. One consequence of that shared producer: without rescale, the weighted SMD inherits the frequency reading and is not invariant to the scale of the weights, exactly as the SD column is not. With rescale = TRUE it is. For a survey weight, use rescale = TRUE.

Custom labels

By default, table_continuous() labels each variable with its label attribute when one is present (e.g. data imported with haven), and with the column name otherwise – that is why the tables above read “Body mass index” rather than bmi. Use the labels argument, a named character vector keyed by column name, to override either. Only the listed columns are relabelled; the others keep their attribute label or column name:

table_continuous(
  sochealth,
  select = c(wellbeing_score, life_sat_health),
  by = sex,
  labels = c(wellbeing_score = "Well-being score (0-100)")
)
#> Descriptive statistics by Sex
#> 
#>  Variable                       │ Group     M     SD     Min    Max   
#> ────────────────────────────────┼─────────────────────────────────────
#>  Well-being score (0-100)       │ Female  67.16  14.80  19.60  100.00 
#>                                 │ Male    71.05  16.23  18.70  100.00 
#> ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
#>  Satisfaction with health (1-5) │ Female   3.51   1.25   1.00    5.00 
#>                                 │ Male     3.59   1.25   1.00    5.00 
#> 
#>  Variable                       │ Group   95% CI LL  95% CI UL   n     p   
#> ────────────────────────────────┼──────────────────────────────────────────
#>  Well-being score (0-100)       │ Female    65.99      68.33    620  <.001 
#>                                 │ Male      69.73      72.37    580        
#> ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
#>  Satisfaction with health (1-5) │ Female     3.41       3.61    616   .267 
#>                                 │ Male       3.49       3.69    576        
#> 
#> Missing values removed: life_sat_health (8).

Display options

The printed ASCII table and every rendered output share the same formatting vocabulary as table_continuous_lm(); align, p_digits, and decimal_mark are also shared with table_categorical():

  • align = "decimal" (default) aligns numeric columns on the decimal mark, matching SPSS / SAS / LaTeX siunitx conventions. "center" and "right" are the alternatives.
  • p_digits = 3 (default, the APA standard) drives both the displayed precision of the p column and the small-p threshold: with p_digits = 4, the chunk below shows .0176 for BMI and <.0001 for the well-being score.
  • show_n = FALSE and ci = FALSE drop the corresponding columns (and the CI spanner / borders) structurally from every output; the underlying n and ci_lower / ci_upper are always present in output = "data.frame" / "long" for downstream code.
table_continuous(
  sochealth,
  select = c(bmi, wellbeing_score),
  by = sex,
  ci = FALSE,
  show_n = FALSE,
  p_digits = 4
)
#> Descriptive statistics by Sex
#> 
#>  Variable                      │ Group     M     SD     Min    Max      p    
#> ───────────────────────────────┼─────────────────────────────────────────────
#>  Body mass index               │ Female  25.69   3.78  16.00   38.90   .0176 
#>                                │ Male    26.20   3.64  16.00   37.70         
#> ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌
#>  WHO-5 wellbeing index (0-100) │ Female  67.16  14.80  19.60  100.00  <.0001 
#>                                │ Male    71.05  16.23  18.70  100.00         
#> 
#> Missing values removed: bmi (12).

Tidying for downstream pipelines

table_continuous() returns an object that can be coerced to a plain data.frame / tbl_df (stripping the spicy formatting attributes) or piped into broom::tidy() / broom::glance() for any downstream tidyverse stats workflow:

out <- table_continuous(
  sochealth,
  select = c(bmi, wellbeing_score),
  by = sex
)

# Long descriptive rows: one per (variable x group) with broom-style
# columns (outcome, label, group, estimate = mean, std.error,
# conf.low / conf.high, n, min, max, sd).
broom::tidy(out)
#> # A tibble: 4 × 11
#>   outcome    label group estimate std.error conf.low conf.high     n   min   max
#>   <chr>      <chr> <chr>    <dbl>     <dbl>    <dbl>     <dbl> <int> <dbl> <dbl>
#> 1 bmi        Body… Fema…     25.7     0.152     25.4      26.0   616  16    38.9
#> 2 bmi        Body… Male      26.2     0.152     25.9      26.5   572  16    37.7
#> 3 wellbeing… WHO-… Fema…     67.2     0.594     66.0      68.3   620  19.6 100  
#> 4 wellbeing… WHO-… Male      71.0     0.674     69.7      72.4   580  18.7 100  
#> # ℹ 1 more variable: sd <dbl>

# One row per outcome with the omnibus test + effect-size summary
# (test_type, statistic, df, df.residual, p.value, es_type, es_value,
# es_ci_lower / es_ci_upper, n_total).
broom::glance(out)
#> # A tibble: 2 × 14
#>   outcome   label test_type statistic    df df.residual p.value es_type es_value
#>   <chr>     <chr> <chr>         <dbl> <dbl>       <dbl>   <dbl> <chr>      <dbl>
#> 1 bmi       Body… welch_t       -2.38 1184.          NA 1.76e-2 NA            NA
#> 2 wellbein… WHO-… welch_t       -4.33 1169.          NA 1.65e-5 NA            NA
#> # ℹ 5 more variables: es_ci_lower <dbl>, es_ci_upper <dbl>, smd_type <chr>,
#> #   smd_value <dbl>, n_total <int>

# Or just unbox to a plain data.frame (long-format underlying data)
head(as.data.frame(out))
#>          variable                         label  group     mean        sd  min
#> 1             bmi               Body mass index Female 25.68506  3.781113 16.0
#> 2             bmi               Body mass index   Male 26.19685  3.638092 16.0
#> 3 wellbeing_score WHO-5 wellbeing index (0-100) Female 67.16194 14.798488 19.6
#> 4 wellbeing_score WHO-5 wellbeing index (0-100)   Male 71.04879 16.227304 18.7
#>     max ci_lower ci_upper median     q1     q3    iqr med_ci_lower med_ci_upper
#> 1  38.9 25.38588 25.98425   25.7 23.100 28.600  5.500         25.4         26.1
#> 2  37.7 25.89808 26.49563   26.1 23.875 28.625  4.750         25.8         26.6
#> 3 100.0 65.99480 68.32907   68.2 57.300 77.525 20.225         66.6         69.7
#> 4 100.0 69.72540 72.37219   72.3 61.275 81.575 20.300         70.8         73.2
#>     n weighted_n test_type statistic      df1 df2      p.value smd_type
#> 1 616         NA   welch_t -2.377237 1184.497  NA 1.760093e-02     <NA>
#> 2 572         NA      <NA>        NA       NA  NA           NA     <NA>
#> 3 620         NA   welch_t -4.326141 1168.700  NA 1.647005e-05     <NA>
#> 4 580         NA      <NA>        NA       NA  NA           NA     <NA>
#>   smd_value
#> 1        NA
#> 2        NA
#> 3        NA
#> 4        NA

Output formats

table_continuous() supports the same reporting-oriented outputs as table_categorical():

output value Returned object
"default" Styled ASCII console table
"data.frame" / "long" Plain data.frame with the underlying long-format rows (synonyms; pick whichever reads better in your code)
"tinytable" Formatted tinytable
"gt" Formatted gt table
"flextable" Formatted flextable
"excel" Written .xlsx file
"clipboard" Copied text table
"word" Written .docx file

output = "gt" produces a formatted gt table with APA-style borders and column spanners:

table_continuous(
  sochealth,
  select = c(bmi, wellbeing_score, life_sat_health),
  by = education,
  output = "gt"
)

Descriptive statistics by Highest education level
Variable
Group
M
SD
Min
Max
95% CI
n
p
LL UL
Body mass index Lower secondary 28.09  3.47 18.20  38.90 27.66 28.51 260 <.001
Upper secondary 26.02  3.43 16.00  37.10 25.73 26.31 534      
Tertiary 24.39  3.52 16.00  33.00 24.04 24.74 394      
WHO-5 wellbeing index (0-100) Lower secondary 57.22 15.44 18.70  97.90 55.33 59.10 261 <.001
Upper secondary 68.97 13.62 26.70 100.00 67.82 70.12 539      
Tertiary 76.85 13.23 40.40 100.00 75.55 78.15 400      
Satisfaction with health (1-5) Lower secondary  2.71  1.20  1.00   5.00  2.57  2.86 259 <.001
Upper secondary  3.53  1.19  1.00   5.00  3.43  3.63 534      
Tertiary  4.11  1.04  1.00   5.00  4.01  4.21 399      
Missing values removed: bmi (12), life_sat_health (8).

output = "tinytable" works well in Quarto and R Markdown documents:

table_continuous(
  sochealth,
  select = c(bmi, wellbeing_score, life_sat_health),
  by = education,
  output = "tinytable"
)
Descriptive statistics by Highest education level
Variable Group M SD Min Max 95% CI n p
LL UL
Missing values removed: bmi (12), life_sat_health (8).
Body mass index Lower secondary 28.09  3.47 18.20  38.90 27.66 28.51 260 <.001
Upper secondary 26.02  3.43 16.00  37.10 25.73 26.31 534      
Tertiary 24.39  3.52 16.00  33.00 24.04 24.74 394      
WHO-5 wellbeing index (0-100) Lower secondary 57.22 15.44 18.70  97.90 55.33 59.10 261 <.001
Upper secondary 68.97 13.62 26.70 100.00 67.82 70.12 539      
Tertiary 76.85 13.23 40.40 100.00 75.55 78.15 400      
Satisfaction with health (1-5) Lower secondary  2.71  1.20  1.00   5.00  2.57  2.86 259 <.001
Upper secondary  3.53  1.19  1.00   5.00  3.43  3.63 534      
Tertiary  4.11  1.04  1.00   5.00  4.01  4.21 399      

Export to Excel or Word

Use the same function to export the table directly:

table_continuous(
  sochealth,
  select = c(bmi, wellbeing_score, life_sat_health),
  by = education,
  output = "excel",
  excel_path = "table_continuous.xlsx"
)

table_continuous(
  sochealth,
  select = c(bmi, wellbeing_score, life_sat_health),
  by = education,
  output = "word",
  word_path = "table_continuous.docx"
)

See also