sjtable2df: Overview

The `sjPlot` R package is a great package for visualizing results.

However, the tables created using the functions `sjPlot::tab_model` or `sjPlot::tab_xtab` return HTML tables and are not straightforward to use in R, especially when trying to integrate them into pdf- or word-documents using Rmarkdown.

Various approaches/ tutorials exist to convert `sjPlot` HTML tables to R `data.frame` objects:

- https://stackoverflow.com/questions/63053465/how-to-convert-an-html-sjtable-from-the-sjplot-package-to-latex
- https://stackoverflow.com/questions/47744396/how-to-format-an-sjplot-in-pdf-rmarkdown
- https://stackoverflow.com/questions/62197268/knitting-output-from-sjplottab-model-or-other-html-tables-into-pdf-document
- https://stackoverflow.com/questions/65274404/how-to-extract-lme4-output-to-latex-tables

None of these approaches converts `sjPlot` HTML tables to R `data.frame` objects or integrates well with `knitr::kable` or the `kableExtra` R package.

The `sjtable2df` R package’s goal is to overcome this and to provide an easy interface for converting `sjPlot`’s HTML tables to `data.frame`, `data.table`, or `kable` objects for further usage in R or Rmarkdown.

Currently, `sjtable2df` provides two functions to convert tables created from `sjPlot`’s functions `tab_model` and `tab_xtab`: `sjtable2df::mtab2df` and `sjtable2df::xtab2df`.

# Example: Contingency-Tables

## Data Preprocessing

    library(sjtable2df)
    library(mlbench)

    # load data
    data("PimaIndiansDiabetes2")
    dataset <- PimaIndiansDiabetes2 |>
      data.table::as.data.table()

    # create new binary variable
    dataset[, ("preg_gt_4") := ifelse(get("pregnant") > 4, 1, 0) |> factor()]

## Create Contingency Table

    xtab <- sjPlot::tab_xtab(
      var.row = dataset$diabetes,
      var.col = dataset$preg_gt_4,
      show.summary = TRUE,
      use.viewer = FALSE
    )

    xtab

diabetes | preg_gt_4 | Total
--- | --- | ---
0 | 1
neg | 356 | 144 | 500
pos | 136 | 132 | 268
Total | 492 | 276 | 768
χ2=30.823 · df=1 · &phi=0.203 · p=0.000

## Convert Contingency Table to `data.frame`

    xtab_df <- sjtable2df::xtab2df(xtab = xtab, output = "data.frame")
    class(xtab_df)

    [1] "data.frame"

    xtab_df

      diabetes preg_gt_4 0 preg_gt_4 1                                   Total
    1      neg         356         144                                     500
    2      pos         136         132                                     268
    3    Total         492         276                                     768
    4                                  χ2=30.823 · df=1 · &phi=0.203 · p=0.000

## Convert Contingency Table to `kable`

    xtab_kbl <- sjtable2df::xtab2df(
      xtab = xtab,
      output = "kable",
      caption = "Diabetes vs. preg>4",
      col.names = c("Diabetes", "No", "Yes", "Total")
    )
    class(xtab_kbl)

    [1] "kableExtra"  "knitr_kable"

    xtab_kbl |>
      kableExtra::add_header_above(
        header = c(" " = 1, "Pregnant > 4" = 2, " " = 1)
      )

Diabetes vs. preg>4
| Pregnant > 4 |
--- | --- | ---
Diabetes | No | Yes | Total
neg | 356 | 144 | 500
pos | 136 | 132 | 268
Total | 492 | 276 | 768
$χ2=30.823 · df=1 · &phi=0.203 · p=0.000$ |  |  |

## Percentages in cells

This function also extracts further statistics from cells and writes them to parentheses:

    xtab <- sjPlot::tab_xtab(
      var.row = dataset$diabetes,
      var.col = dataset$preg_gt_4,
      show.summary = TRUE,
      show.col.prc = TRUE,
      use.viewer = FALSE
    )

    xtab

diabetes | preg_gt_4 | Total
--- | --- | ---
0 | 1
neg | 356 72.4 % | 144 52.2 % | 500 65.1 %
pos | 136 27.6 % | 132 47.8 % | 268 34.9 %
Total | 492 100 % | 276 100 % | 768 100 %
χ2=30.823 · df=1 · &phi=0.203 · p=0.000

## Convert Contingency Table to `data.frame`

    xtab_df <- sjtable2df::xtab2df(xtab = xtab, output = "data.frame")
    xtab_df

      diabetes  preg_gt_4 0  preg_gt_4 1                                   Total
    1      neg 356 (72.4 %) 144 (52.2 %)                            500 (65.1 %)
    2      pos 136 (27.6 %) 132 (47.8 %)                            268 (34.9 %)
    3    Total  492 (100 %)  276 (100 %)                             768 (100 %)
    4                                    χ2=30.823 · df=1 · &phi=0.203 · p=0.000

# Example: Model Tables: Linear Regression

## Create Three Models

    m0 <- lm(
      pressure ~ 1,
      data = dataset
    )
    m1 <- lm(
      pressure ~ glucose,
      data = dataset
    )
    m2 <- lm(
      pressure ~ glucose + diabetes,
      data = dataset
    )

## Create Model Table

    m_table <- sjPlot::tab_model(
      m0,
      m1,
      m2,
      show.aic = TRUE
    )

    m_table

| pressure | pressure | pressure
--- | --- | --- | ---
Predictors | Estimates | CI | p | Estimates | CI | p | Estimates | CI | p
(Intercept) | 72.41 | 71.51 – 73.30 | <0.001 | 61.46 | 57.85 – 65.06 | <0.001 | 62.65 | 58.85 – 66.44 | <0.001
glucose |  |  |  | 0.09 | 0.06 – 0.12 | <0.001 | 0.07 | 0.04 – 0.11 | <0.001
diabetes [pos] |  |  |  |  |  |  | 2.06 | -0.06 – 4.18 | 0.056
Observations | 733 | 728 | 728
R2 / R2 adjusted | 0.000 / 0.000 | 0.050 / 0.049 | 0.055 / 0.052
AIC | 5771.995 | 5697.909 | 5696.248

## Convert Model Table to `data.frame`

    mtab_df <- sjtable2df::mtab2df(
      mtab = m_table,
      n_models = 3,
      output = "data.frame"
    )
    class(mtab_df)

    [1] "data.frame"

    mtab_df

            Predictors     Estimates            CI      p     Estimates
    1      (Intercept)         72.41 71.51 – 73.30 <0.001         61.46
    2          glucose                                             0.09
    3   diabetes [pos]
    4     Observations           733                                728
    5 R2 / R2 adjusted 0.000 / 0.000                      0.050 / 0.049
    6              AIC      5771.995                           5697.909
                 CI      p     Estimates            CI      p
    1 57.85 – 65.06 <0.001         62.65 58.85 – 66.44 <0.001
    2   0.06 – 0.12 <0.001          0.07   0.04 – 0.11 <0.001
    3                               2.06  -0.06 – 4.18  0.056
    4                                728
    5                      0.055 / 0.052
    6                           5696.248

## Convert Model Table to `kable`

    mtab_kbl <- sjtable2df::mtab2df(
      mtab = m_table,
      n_models = 3,
      output = "kable"
    )
    class(mtab_kbl)

    [1] "kableExtra"  "knitr_kable"

    mtab_kbl

| pressure
--- | ---
Predictors | Estimates | CI | p | Estimates | CI | p | Estimates | CI | p
(Intercept) | 72.41 | 71.51 – 73.30 | <0.001 | 61.46 | 57.85 – 65.06 | <0.001 | 62.65 | 58.85 – 66.44 | <0.001
glucose |  |  |  | 0.09 | 0.06 – 0.12 | <0.001 | 0.07 | 0.04 – 0.11 | <0.001
diabetes [pos] |  |  |  |  |  |  | 2.06 | -0.06 – 4.18 | 0.056
Observations | 733 |  |  | 728 |  |  | 728 |  |
$R^2$ / $R^2$ adjusted | 0.000 / 0.000 |  |  | 0.050 / 0.049 |  |  | 0.055 / 0.052 |  |
AIC | 5771.995 |  |  | 5697.909 |  |  | 5696.248 |  |

# Example: Model Tables: Logistic Regression

## Create Three Models

    m0 <- stats::glm(
      diabetes ~ 1,
      data = dataset,
      family = binomial(link = "logit")
    )
    m1 <- stats::glm(
      diabetes ~ glucose,
      data = dataset,
      family = binomial(link = "logit")
    )
    m2 <- stats::glm(
      diabetes ~ glucose + pressure,
      data = dataset,
      family = binomial(link = "logit")
    )

## Create Model Table

    m_table <- sjPlot::tab_model(
      m0,
      m1,
      m2,
      show.aic = TRUE
    )

    m_table

| diabetes | diabetes | diabetes
--- | --- | --- | ---
Predictors | Odds Ratios | CI | p | Odds Ratios | CI | p | Odds Ratios | CI | p
(Intercept) | 0.54 | 0.46 – 0.62 | <0.001 | 0.00 | 0.00 – 0.01 | <0.001 | 0.00 | 0.00 – 0.01 | <0.001
glucose |  |  |  | 1.04 | 1.03 – 1.05 | <0.001 | 1.04 | 1.03 – 1.05 | <0.001
pressure |  |  |  |  |  |  | 1.01 | 1.00 – 1.03 | 0.060
Observations | 768 | 763 | 728
R2 Tjur | 0.000 | 0.249 | 0.246
AIC | 995.484 | 790.560 | 754.636

## Convert Model Table to `data.frame`

    mtab_df <- sjtable2df::mtab2df(
      mtab = m_table,
      n_models = 3,
      output = "data.frame"
    )
    class(mtab_df)

    [1] "data.frame"

    mtab_df

        Predictors Odds Ratios          CI      p Odds Ratios          CI      p
    1  (Intercept)        0.54 0.46 – 0.62 <0.001        0.00 0.00 – 0.01 <0.001
    2      glucose                                       1.04 1.03 – 1.05 <0.001
    3     pressure
    4 Observations         768                            763
    5      R2 Tjur       0.000                          0.249
    6          AIC     995.484                        790.560
      Odds Ratios          CI      p
    1        0.00 0.00 – 0.01 <0.001
    2        1.04 1.03 – 1.05 <0.001
    3        1.01 1.00 – 1.03  0.060
    4         728
    5       0.246
    6     754.636

## Convert Model Table to `kable`

    mtab_kbl <- sjtable2df::mtab2df(
      mtab = m_table,
      n_models = 3,
      output = "kable"
    )
    class(mtab_kbl)

    [1] "kableExtra"  "knitr_kable"

    mtab_kbl

| diabetes
--- | ---
Predictors | Odds Ratios | CI | p | Odds Ratios | CI | p | Odds Ratios | CI | p
(Intercept) | 0.54 | 0.46 – 0.62 | <0.001 | 0.00 | 0.00 – 0.01 | <0.001 | 0.00 | 0.00 – 0.01 | <0.001
glucose |  |  |  | 1.04 | 1.03 – 1.05 | <0.001 | 1.04 | 1.03 – 1.05 | <0.001
pressure |  |  |  |  |  |  | 1.01 | 1.00 – 1.03 | 0.060
Observations | 768 |  |  | 763 |  |  | 728 |  |
$R^2$ Tjur | 0.000 |  |  | 0.249 |  |  | 0.246 |  |
AIC | 995.484 |  |  | 790.560 |  |  | 754.636 |  |

# Example: Model Tables: GLMM

## Create Three Models

    set.seed(1)
    dataset$city <- sample(
      x = paste0("city_", 1:7),
      size = nrow(dataset),
      replace = TRUE
    )
    m0 <- lme4::glmer(
      diabetes ~ 1 + (1 | city),
      data = dataset,
      family = binomial(link = "logit")
    )

    boundary (singular) fit: see help('isSingular')

    m1 <- lme4::glmer(
      diabetes ~ mass + (1 | city),
      data = dataset,
      family = binomial(link = "logit")
    )
    m2 <- lme4::glmer(
      diabetes ~ mass + log(pressure) + (1 | city),
      data = dataset,
      family = binomial(link = "logit")
    )

## Create Model Table

    m_table <- sjPlot::tab_model(
      m0,
      m1,
      m2,
      show.aic = TRUE
    )

    boundary (singular) fit: see help('isSingular')
    boundary (singular) fit: see help('isSingular')
    boundary (singular) fit: see help('isSingular')

    m_table

| diabetes | diabetes | diabetes
--- | --- | --- | ---
Predictors | Odds Ratios | CI | p | Odds Ratios | CI | p | Odds Ratios | CI | p
(Intercept) | 0.54 | 0.46 – 0.62 | <0.001 | 0.02 | 0.01 – 0.04 | <0.001 | 0.00 | 0.00 – 0.01 | <0.001
mass |  |  |  | 1.11 | 1.08 – 1.14 | <0.001 | 1.10 | 1.07 – 1.13 | <0.001
pressure [log] |  |  |  |  |  |  | 3.31 | 1.26 – 8.67 | 0.015
Random Effects
σ2 | 3.29 | 3.29 | 3.29
τ00 | 0.00 city | 0.01 city | 0.00 city
ICC |  | 0.00 | 0.00
N | 7 city | 7 city | 7 city
Observations | 768 | 757 | 729
Marginal R2 / Conditional R2 | 0.000 / NA | 0.133 / 0.135 | 0.137 / 0.137
AIC | 997.484 | 910.822 | 871.890

## Convert Model Table to `data.frame`

    mtab_df <- sjtable2df::mtab2df(
      mtab = m_table,
      n_models = 3,
      output = "data.frame"
    )
    class(mtab_df)

    [1] "data.frame"

    mtab_df

                         Predictors Odds Ratios          CI      p   Odds Ratios
    1                   (Intercept)        0.54 0.46 – 0.62 <0.001          0.02
    2                          mass                                         1.11
    3                pressure [log]
    4                Random Effects
    5                            σ2        3.29                             3.29
    6                           τ00   0.00 city                        0.01 city
    7                           ICC                                         0.00
    8                             N      7 city                           7 city
    9                  Observations         768                              757
    10 Marginal R2 / Conditional R2  0.000 / NA                    0.133 / 0.135
    11                          AIC     997.484                          910.822
                CI      p   Odds Ratios          CI      p
    1  0.01 – 0.04 <0.001          0.00 0.00 – 0.01 <0.001
    2  1.08 – 1.14 <0.001          1.10 1.07 – 1.13 <0.001
    3                              3.31 1.26 – 8.67  0.015
    4
    5                              3.29
    6                         0.00 city
    7                              0.00
    8                            7 city
    9                               729
    10                    0.137 / 0.137
    11                          871.890

## Convert Model Table to `kable`

    mtab_kbl <- sjtable2df::mtab2df(
      mtab = m_table,
      n_models = 3,
      output = "kable"
    )
    class(mtab_kbl)

    [1] "kableExtra"  "knitr_kable"

    mtab_kbl

| diabetes
--- | ---
Predictors | Odds Ratios | CI | p | Odds Ratios | CI | p | Odds Ratios | CI | p
(Intercept) | 0.54 | 0.46 – 0.62 | <0.001 | 0.02 | 0.01 – 0.04 | <0.001 | 0.00 | 0.00 – 0.01 | <0.001
mass |  |  |  | 1.11 | 1.08 – 1.14 | <0.001 | 1.10 | 1.07 – 1.13 | <0.001
pressure [log] |  |  |  |  |  |  | 3.31 | 1.26 – 8.67 | 0.015
Random Effects |  |  |  |  |  |  |  |  |
σ2 | 3.29 |  |  | 3.29 |  |  | 3.29 |  |
τ00 | 0.00 city |  |  | 0.01 city |  |  | 0.00 city |  |
ICC |  |  |  | 0.00 |  |  | 0.00 |  |
N | 7 city |  |  | 7 city |  |  | 7 city |  |
Observations | 768 |  |  | 757 |  |  | 729 |  |
Marginal $R^2$ / Conditional $R^2$ | 0.000 / NA |  |  | 0.133 / 0.135 |  |  | 0.137 / 0.137 |  |
AIC | 997.484 |  |  | 910.822 |  |  | 871.890 |  |