table_or - Publication-ready odds ratio tables

library(plotor)
set.seed(123) # reproducibility

Overview

table_or() automates the creation of publication-ready odds ratio tables from logistic regression models. This vignette shows how to:

When to use this function

Use table_or() when you need to:

Quick example - minimal workflow

# create a small example dataset
rows <- 400
df <- data.frame(
  outcome = rbinom(n = rows, size = 1, prob = 0.25) |> 
    factor(labels = c("Healthy", "Disease")),
  age = rnorm(n = rows, mean = 50, sd = 12),
  sex = sample(x = 0:1, size = rows, replace = TRUE) |> 
    factor(labels = c("Female", "Male")),
  smoke = sample(x = 0:2, size = rows, replace = TRUE) |> 
    factor(labels = c("Never", "Former", "Current"))
)

# fit a logistic regression model
m <- glm(
  formula = outcome ~ age + sex + smoke,
  family = "binomial",
  data = df
)

Programmatic output (tibble)

Use the tibble output for downstream manipulation, reporting or combining results across models.

table_or(m, output = "tibble")
#> # A tibble: 6 × 14
#>   label level    rows outcome outcome_rate class   estimate std.error statistic
#>   <fct> <fct>   <int>   <int>        <dbl> <chr>      <dbl>     <dbl>     <dbl>
#> 1 age   age       400      97        0.242 numeric    0.990   0.00996   -1.04  
#> 2 sex   Female    206      53        0.257 factor    NA      NA         NA     
#> 3 sex   Male      194      44        0.227 factor     0.856   0.235     -0.663 
#> 4 smoke Never     137      34        0.248 factor    NA      NA         NA     
#> 5 smoke Former    135      34        0.252 factor     1.03    0.281      0.0997
#> 6 smoke Current   128      29        0.227 factor     0.902   0.290     -0.356 
#> # ℹ 5 more variables: p.value <dbl>, conf.low <dbl>, conf.high <dbl>,
#> #   significance <chr>, comparator <dbl>

Key columns returned:

Estimates are shown as coefficients on the log-odds scale (estimate / std.error / statistic / p.value) with confidence intervals presented for exponentiated results (conf.low / conf.high).

Interpreting the results

Formatted HTML table (gt)

Create a publication-ready table using gt formatting.

table_or(m, output = "gt")
outcome
Odds Ratio summary table with 95% Confidence Interval
Characteristic1
Odds Ratio (OR)2
95% Confidence Interval (CI)3
OR Plot
Level N n Rate Class OR SE p Lower Upper Significance
age age 400 97 24.25% numeric 0.9897 0.009961 3.00 × 10−1 0.9705 1.009 Not significant 000
sex Female 206 53 25.73% factor Comparator  
Male 194 44 22.68% factor 0.8558 0.2350 5.07 × 10−1 0.5385 1.355 Not significant 00-1
smoke Never 137 34 24.82% factor Comparator  
Former 135 34 25.19% factor 1.028 0.2808 9.21 × 10−1 0.5923 1.786 Not significant 01-1
Current 128 29 22.66% factor 0.9018 0.2903 7.22 × 10−1 0.5085 1.592 Not significant 00-1
1 Characteristics are the explanatory variables in the logistic regression analysis. For categorical variables the first characteristic is designated as a reference against which the others are compared. For numeric variables the results indicate a change per single unit increase.

Level - the name or the description of the explanatory variable.

N - the number of observations examined.

n - the number of observations resulting in the outcome of interest.

Rate - the proportion of observations resulting in the outcome of interest (n / N).

Class - description of the data type.

2 Odds Ratios estimate the relative odds of an outcome with reference to the Characteristic. For categorical data the first level is the reference against which the odds of other levels are compared. Numerical characteristics indicate the change in OR for each additional increase of one unit in the variable.

OR - The Odds Ratio point estimate - values below 1 indicate an inverse relationship whereas values above 1 indicate a positive relationship. Values shown to 4 significant figures.

SE - Standard Error of the point estimate. Values shown to 4 significant figures.

p - The p-value estimate based on the residual Chi-squared statistic.

3 Confidence Interval - the range of values likely to contain the OR in 95% of cases if this study were to be repeated multiple times. If the CI touches or crosses the value 1 then it is unlikely the Characteristic is significantly associated with the outcome.

Lower & Upper - The range of values comprising the CI, shown to 4 significant figures.

Significance - The statistical significance indicated by the CI, Significant where the CI does not touch or cross the value 1.

What the gt output includes

The HTML table includes:

Understanding the footnotes

The footnotes clarify:

Common customisations

Change confidence level

Report 90% confidence intervals for sensitivity analysis or different reporting standards:

table_or(m, output = "tibble", conf_level = 0.90)
#> # A tibble: 6 × 14
#>   label level    rows outcome outcome_rate class   estimate std.error statistic
#>   <fct> <fct>   <int>   <int>        <dbl> <chr>      <dbl>     <dbl>     <dbl>
#> 1 age   age       400      97        0.242 numeric    0.990   0.00996   -1.04  
#> 2 sex   Female    206      53        0.257 factor    NA      NA         NA     
#> 3 sex   Male      194      44        0.227 factor     0.856   0.235     -0.663 
#> 4 smoke Never     137      34        0.248 factor    NA      NA         NA     
#> 5 smoke Former    135      34        0.252 factor     1.03    0.281      0.0997
#> 6 smoke Current   128      29        0.227 factor     0.902   0.290     -0.356 
#> # ℹ 5 more variables: p.value <dbl>, conf.low <dbl>, conf.high <dbl>,
#> #   significance <chr>, comparator <dbl>

Anonymise counts for sensitive data

Improve data privacy by rounding counts to the nearest five and suppressing counts below ten. This is useful when working with sensitive dataset under strict information governance requirements:

table_or(m, output = "tibble", anonymise_counts = TRUE)
#> # A tibble: 6 × 14
#>   label level   rows  outcome outcome_rate class   estimate std.error statistic
#>   <fct> <fct>   <chr> <chr>          <dbl> <chr>      <dbl>     <dbl>     <dbl>
#> 1 age   age     400   95             0.242 numeric    0.990   0.00996   -1.04  
#> 2 sex   Female  205   55             0.257 factor    NA      NA         NA     
#> 3 sex   Male    195   45             0.227 factor     0.856   0.235     -0.663 
#> 4 smoke Never   135   35             0.248 factor    NA      NA         NA     
#> 5 smoke Former  135   35             0.252 factor     1.03    0.281      0.0997
#> 6 smoke Current 130   30             0.227 factor     0.902   0.290     -0.356 
#> # ℹ 5 more variables: p.value <dbl>, conf.low <dbl>, conf.high <dbl>,
#> #   significance <chr>, comparator <dbl>

Combine results from multiple models

Compare models by binding tibble outputs:

# fit a second model
m2 <- glm(
  formula = outcome ~ age + sex,
  family = "binomial",
  data = df
)

# combine results
combined_results <-
  dplyr::bind_rows(
    dplyr::bind_cols(model = "Model 1", table_or(m)),
    dplyr::bind_cols(model = "Model 2", table_or(m2))
  )

head(combined_results)
#> # A tibble: 6 × 15
#>   model   label level    rows outcome outcome_rate class   estimate std.error
#>   <chr>   <fct> <fct>   <int>   <int>        <dbl> <chr>      <dbl>     <dbl>
#> 1 Model 1 age   age       400      97        0.242 numeric    0.990   0.00996
#> 2 Model 1 sex   Female    206      53        0.257 factor    NA      NA      
#> 3 Model 1 sex   Male      194      44        0.227 factor     0.856   0.235  
#> 4 Model 1 smoke Never     137      34        0.248 factor    NA      NA      
#> 5 Model 1 smoke Former    135      34        0.252 factor     1.03    0.281  
#> 6 Model 1 smoke Current   128      29        0.227 factor     0.902   0.290  
#> # ℹ 6 more variables: statistic <dbl>, p.value <dbl>, conf.low <dbl>,
#> #   conf.high <dbl>, significance <chr>, comparator <dbl>

Exporting tables

Export to HTML file

gt_table <- table_or(m, output = "gt")
gt::gtsave(data = gt_table, filename = "odds_ratios.html")

Export to Word document

gt_table <- table_or(m, output = "gt")
gt::gtsave(data = gt_table, filename = "odds_ratios.docx")

Export to CSV (tibble)

readr::write_csv(x = table_or(m), file = "odds_ratios.csv")

Tips for publication

Conclusion

The table_or() function automates the creation of publication-ready odds ratio tables from logistic regression models, eliminating manual calculation and formatting while ensuring consistent presentation of results with appropriate statistical annotations.

See also