MJMSTable

Introduction

The MJMSTable package is designed to streamline the process of creating publication-ready statistical tables that adhere to the formatting requirements of the Malaysian Journal of Medical Sciences (MJMS). Built on top of gtsummary and gt, this package provides wrapper functions for common statistical tests, automatically applying the correct themes, layouts, and footnotes.

Setup

First, load the package. For this vignette, we will use a hypothetical dataset called mjms_data that comes with the package.

library(MJMSTable)

# View the structure of the dataset
head(mjms_data)
#>   PatientID Age  BMI Cholesterol    Sex Smoker Treatment_Group SBP_Left_Arm
#> 1         1  46 25.2       225.5 Female    Yes          Drug_A          155
#> 2         2  44 30.8       193.5   Male     No          Drug_A          156
#> 3         3  58 34.0       248.8 Female     No         Placebo          149
#> 4         4  62 24.7       190.8   Male    Yes         Placebo          135
#> 5         5  71 25.7       206.4   Male     No          Drug_A          161
#> 6         6  74 21.3       240.1   Male     No          Drug_A          153
#>   SBP_Right_Arm Heart_Disease Diagnosis_Doc_A Diagnosis_Doc_B Gold_Standard
#> 1           162            No        Positive        Negative       Healthy
#> 2           159            No        Positive        Positive       Disease
#> 3           153           Yes        Negative        Negative       Healthy
#> 4           135            No        Positive        Positive       Disease
#> 5           165            No        Positive        Negative       Disease
#> 6           157           Yes        Negative        Negative       Healthy
#>   New_Test_Result Symptom_Pre Symptom_Post
#> 1        Negative         Yes          Yes
#> 2        Positive          No           No
#> 3        Negative          No           No
#> 4        Positive         Yes          Yes
#> 5        Negative          No          Yes
#> 6        Negative          No           No

1. Descriptive Statistics

The descriptive_tbl() function provides a clean summary of demographic or baseline characteristics, grouped by a specific variable.

descriptive_tbl(
  data = mjms_data,
  group_var = "Treatment_Group",
  included_var = c("Age", "BMI", "Sex", "Smoker"),
  non_normal_vars = c("Age"),
  table_caption = "Baseline Patient Characteristics",
  abbreviation = "BMI = Body Mass Index, SBP = Systolic Blood Pressure"
)
Table : Baseline Patient Characteristics (n = 500)
Variables Drug_A ( n = 196) Drug_B ( n = 152) Placebo ( n = 152) Total n (%)
Agea 54.0 (16.0) 55.0 (19.0) 56.0 (17.5) 55.0 (18.0)
BMIb 26.0 (4.6) 27.2 (4.7) 27.0 (5.1) 26.7 (4.8)
Sex



    Female 101 (51.5) 70 (46.1) 72 (47.4) 243 (48.6)
    Male 95 (48.5) 82 (53.9) 80 (52.6) 257 (51.4)
Smoker 71 (36.2) 55 (36.2) 30 (19.7) 156 (31.2)
a Median (IQR)
b Mean (SD)
Abbreviation: BMI = Body Mass Index, SBP = Systolic Blood Pressure

2. Comparing Means

Independent t-test

Compare continuous variables between two independent groups using ttest_tbl(). You can toggle between standard and Welch’s t-test using the equal_var argument.

ttest_tbl(
  data = mjms_data,
  outcome_var = "BMI",
  group_var = "Sex",
  equal_var = TRUE,
  table_caption = "Comparison of BMI by Biological Sex",
  abbreviation = "BMI = body mass index"
)
Table : Comparison of BMI by Biological Sex
Variable
Mean (SD)
Mean
difference
(95% CI)
t-statistic
(df)
P-valuea
Male

n = 257

Female

n = 243

BMI 26.50 (4.86) 26.87 (4.73) 0.37 (-0.47, 1.21) 0.862 (498) 0.389
a Independent t-test.
Abbreviation: BMI = body mass index

Paired t-test

For paired continuous data (e.g., pre- and post-measurements or paired body parts), use paired_ttest_tbl().

paired_ttest_tbl(
  data = mjms_data,
  id_var = "PatientID",
  pre_var = "SBP_Left_Arm",
  post_var = "SBP_Right_Arm",
  variable_label = "Systolic Blood Pressure (mmHg)",
  table_caption = "Comparison of SBP between Left and Right Arms"
)
Table : Comparison of SBP between Left and Right Arms
Variable
Mean (SD)
n = 500
Mean
difference
(95% CI)
t-statistic
(df)
P-valuea
SBP_Right_Arm SBP_Left_Arm
Systolic Blood Pressure (mmHg) 142.01 (12.04) 145.87 (12.16) 3.9 (3.7, 4.0) -44.946 (499) <0.001
a Paired t-test.

One-Way ANOVA

When comparing means across more than two groups, anova_tbl() provides the F-statistic, degrees of freedom, and post-hoc adjustment results.

anova_tbl(
  data = mjms_data,
  outcome_var = "Cholesterol",
  group_var = "Treatment_Group",
  outcome_label = "Serum Cholesterol"
)
Table : Comparison of means between groups
Groups n Serum Cholesterol
Mean (SD)
F-statistic (df1,df2)a P-valuea
Treatment_Group

4.86 (2, 497) 0.019b
    Drug_A 196 205.37 (28.75)
    Drug_B 152 215.19 (29.40)
    Placebo 152 210.27 (29.71)
a One-way ANOVA.
b Sig. differences (p < 0.05): Drug_B vs Drug_A

3. Categorical Associations

Chi-square Test

Evaluate the association between two categorical variables with chisq_tbl().

chisq_tbl(
  data = mjms_data,
  exposure_var = "Sex",
  outcome_var = "Smoker",
  table_caption = "Association of Gender vs. Smoking Status"
)
Table : Association of Gender vs. Smoking Status
Variable
Smoker
n χ² -statistic (df) P-valuea
No n (%) Yes n (%)
Sex


0.11 (1) 0.745
    Female 165 (48.0) 78 (50.0) 243

    Male 179 (52.0) 78 (50.0) 257

a Chi-square test for independence.

McNemar Test

For paired categorical data, mcnemar_tbl() computes McNemar’s Chi-squared test with continuity correction.

mcnemar_tbl(
  data = mjms_data,
  pre_var = "Symptom_Pre",
  post_var = "Symptom_Post",
  table_caption = "Symptom status pre- and post-treatment",
  header_pre = "Pre Symptom",
  header_post = "Post Symptom"
)
Table : Symptom status pre- and post-treatment
Pre Symptom
Post Symptom
n χ² -statistic (df) P-valuea
No
n (%)
Yes
n (%)
Pre Symptom


136.94 (1) <0.001
    No 168 (33.6) 39 (7.8) 207

    Yes 233 (46.6) 60 (12.0) 293

a McNemar’s Chi-squared test with continuity correction.

4. Regression Analyses

Linear Regression

Summarize Simple or Multiple Linear Regression models using lm_tbl(). Interaction terms can be included using standard R formula syntax, “*” or “:”.

lm_tbl(
  data = mjms_data,
  outcome_var = "Cholesterol",
  predictor_vars = c("Age * BMI", "Smoker"), # "Age * BMI" adds an interaction term
  var_labels = list(Age ~ "Age (Years)", BMI ~ "Body Mass Index"),
  table_caption = "Multiple Linear Regression for Cholesterol"
)
Table : Multiple Linear Regression for Cholesterol (n = 500).
Factors Adjusted b(95% CI)a t-statistic P-valueb
Age (Years) -0.20 (-1.3, 0.93) -0.350 0.727
Body Mass Index -0.26 (-2.7, 2.1) -0.215 0.830
Smoker
-
    No -
    Yes 0.79 (-4.8, 6.4) 0.278 0.781
Age (Years) * Body Mass Index 0.01 (-0.03, 0.05) 0.551 0.582
a Adjusted regression coefficients,
b Multiple linear regression (R² =0.007).

Logistic Regression

For binary outcomes, logistic_tbl() outputs Adjusted Odds Ratios, 95% CIs, and Wald statistics. You can also explicitly define reference levels. Interaction terms can be included using standard R formula syntax, “*” or “:”.

logistic_tbl(
  data = mjms_data,
  outcome_var = "Heart_Disease",
  predictor_vars = c("Age * Sex", "Smoker"), # "Age * Sex" adds an interaction term
  ref_levels = list(Smoker = "No", Sex = "Female"),
  cat_vars = c("Sex", "Smoker"),
  table_caption = "Risk Factors for Heart Disease"
)
Table : Risk Factors for Heart Disease (n = 500).
Factors b Adjusted OR
(95% CI)
Wald statistic P-valuea
Age -0.00 1.00 (0.97, 1.03) -0.019 0.985
Sex -
-
    Female - -
    Male -0.53 0.59 (0.06, 5.29) -0.469 0.639
Smoker -
-
    No - -
    Yes -0.07 0.93 (0.54, 1.55) -0.275 0.783
Age * Sex -
-
    Age * Male 0.00 1.00 (0.97, 1.04) 0.198 0.843
a Likelihood ratio test.

5. Advanced Analyses

Diagnostic Test (Sensitivity, Specificity, AUC)

Analyze the performance of medical markers against a gold standard using diagnostic_tbl(). It handles multiple markers at once and optionally generates an ROC plot.

# Define index markers and their cutoffs
med_marker <- list(
  Cholesterol  = list(label = "Serum Cholesterol (Cutoff: 200)", cutoff = 200),
  BMI          = list(label = "Body Mass Index (Cutoff: 25)", cutoff = 25),
  SBP_Left_Arm = list(label = "Systolic BP - Left (Cutoff: 140)", cutoff = 140)
)

diag_results <- diagnostic_tbl(
  data = mjms_data,
  status_var = "Gold_Standard",
  marker_map = med_marker,
  show_plot = TRUE,
  plot_marker = "Cholesterol",
  table_caption = "Diagnostic Accuracy of Cardiovascular Markers",
  abbreviation = "AUC = Area Under Curve; SBP = Systolic Blood Pressure"
)

# View outputs
diag_results$table
Diagnostic Accuracy of Cardiovascular Markers (n = 500)
Variable (cutoff) Sensitivity (%)
(95% CI)
Specificity (%)
(95% CI)
AUC (95% CI) P-valuea
Serum Cholesterol (Cutoff: 200) 67.1 (61.1, 72.6) 41.1 (35.1, 46.8) 0.53 (0.48, 0.58) 0.29
Body Mass Index (Cutoff: 25) 67.5 (61.5, 73) 37.9 (31.9, 44) 0.51 (0.46, 0.57) 0.56
Systolic BP - Left (Cutoff: 140) 44 (38.1, 50) 54.8 (48.8, 60.9) 0.5 (0.45, 0.55) 0.95
a Null hypothesis: true area = 0.5.
Abbreviation: AUC = Area Under Curve; SBP = Systolic Blood Pressure
diag_results$plot

Pearson Correlation

Generate a clean correlation matrix with embedded p-values and standard deviations using pearson_tbl().

pearson_tbl(
  data = mjms_data,
  included_var = c("Age", "BMI", "SBP_Left_Arm", "SBP_Right_Arm"),
  abbreviation = "BMI = body mass index; SBP = systolic blood pressure",
  table_caption = "Correlation between clinical variables"
)
Correlation between clinical variables (n = 500)
Variables Age BMI SBP_Left_Arm SBP_Right_Arm
Age 12.427a 0.850b 0.847b 0.949b
BMI 0.008c 4.799a 0.260b 0.292b
SBP_Left_Arm -0.009c -0.051c 12.040a < 0.001b
SBP_Right_Arm -0.003c -0.047c 0.987c 12.159a
a Standard Deviation (SD)
b P-value
c Pearson correlation coefficient (r)
Abbreviation: BMI = body mass index; SBP = systolic blood pressure