gtsummary and ggpubr for hypothesis testing

Produce Table 1, Table 2, and figures-with-p-values for a randomized cohort using gtsummary and ggpubr.

Slides

Make slides full screen

Exercise 1: Demographics and Table 1

Mode setup. The room is split into Group A and Group B. For Exercise 1, Group A works agentic — Claude Code reads your notebook, writes code, and runs it. Group B works manual — you write each step yourself; you may ask a chat LLM conceptual questions, but it does not act in your environment. Exercise 2 flips the modes.

Open notebooks/06-table-one.qmd in your Codespace. The wrapper question across both exercises: can you defend the analysis you would put in a paper?

Part 1 — Predict (no code)

For each non-pid column of table_01 (arm, smoker, age, education, sex), predict — in plain words, before running anything:

  1. How will tbl_summary(by = arm) summarise it? Mean (SD)? Median (IQR)? Counts (%)?
  2. Is the column skewed or symmetric, sparse or dense? That same judgment is what decides which test add_p() reaches for.

Part 2 — Build

Build a publication-ready Table 1.

  1. Start with table_01 |> select(-pid, -sex) |> tbl_summary(by = arm). Read the defaults.
  2. Add |> add_p(). Confirm add_p()’s test choices against your Part 1 predictions.
  3. Override age to use "{mean} ({sd})" and t.test. Run again and see what changed.
  4. Polish: add |> add_overall() |> bold_labels() |> modify_header(label ~ "**Characteristic**").

After each step, compare what changed in the output.

Part 3 — Reach (optional)

Re-do the Table 1 with by = smoker instead of by = arm. Same data, different grouping.

Discussion question: what question is this Table 1 now answering, and is that a question the paper needs answered?

Discussion

Once the room is back together:

  • What was easy in your mode? What was hard?
  • Did add_p() pick what you predicted? Where was it wrong?
  • What would you change about gtsummary’s defaults?

Exercise 2: Figures with p-values

Mode flip. If you were agentic in Exercise 1, you are manual now; if you were manual, you are agentic. Same definitions as before — manual means you may ask a chat LLM conceptual questions but it does not touch your notebook.

Open notebooks/06-figures-with-pvalues.qmd in your Codespace.

Part 1 — Read three plots (no code)

The notebook shows three pre-built plots: the single-outcome ggboxplot (pH by arm at week 7), the six-cytokine facet_wrap panel, and the paired pre-post line plot. For each plot:

  1. What is the mark?
  2. What is the comparison being tested?
  3. What test is on it, and how do you know?
  4. How many rows of data made each mark?

Part 2 — Build

Build the four analyses, growing toward a publication-ready set.

  1. ggboxplot(table_02 |> filter(time_point == "week_7"), x = "arm", y = "ph", fill = "arm") + stat_compare_means(method = "wilcox.test"). The visual form of Table 2 row “ph”.
  2. Build the same plot in plain ggplot: ggplot(...) + geom_boxplot() + stat_compare_means(method = "wilcox.test"). Confirm both give the same p-value.
  3. The cytokine panel: filter table_03 to six chosen cytokines and limits == "within limits", join table_00 to attach arm and time_point, filter to week_7, then ggplot + geom_boxplot + facet_wrap(~ cytokine, scales = "free_y") + stat_compare_means(method = "wilcox.test").
  4. The paired pre-post: filter table_02 to baseline and week_7, mutate(time_point = fct_relevel(time_point, "baseline", "week_7")), then ggplot + geom_line(aes(group = pid)) + geom_point + facet_wrap(~ arm) + stat_compare_means(method = "wilcox.test", paired = TRUE, comparisons = list(c("baseline", "week_7"))).
  5. The adjusted analysis: join table_01 onto a week_7 slice of table_02, fit lm(ph ~ arm + age + smoker), pipe to tbl_regression().

Part 3 — Reach (optional)

Pick one of the four analyses and polish it for publication (axis labels, theme, panel ordering, p-value formatting, color palette).

Or, repeat the full analysis arc on nugent_score or crp_blood instead of ph. The pattern is identical; only the column name changes.

Discussion

Once the room is back together: pick one result from the five analyses you built that you would be willing to put in a paper. What is the test behind it, and how would you defend that choice to a skeptical reviewer?