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.
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 NoThe 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"
)| 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 | ||||
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"
)| 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 | |||||
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"
)| 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. | |||||
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"
)| 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 | ||||
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"
)| 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. | |||||
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"
)| 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. | |||||
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"
)| 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). | |||
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"
)| 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. | ||||
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| 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 | ||||
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 | ||||