Wonderful Wednesday November 2026 (79)

Wonderful Wednesdays Improvement Longitudinal data

Prurigo Nodularis

PSI VIS SIG https://www.psiweb.org/sigs-special-interest-groups/visualisation
11-11-2026

Prurigo Nodularis

Background

In the recent publication “Vixarelimab in Patients With Prurigo Nodularis: A Randomized Clinical Trial” (DOI: 10.1001/jamadermatol.2025.4950) the study results were presented using the following plot:

Figure 2. Least-Squares Means of Percentage Change From Baseline in Weekly Mean Worst Itch Numeric Rating Scale during the Double-Blind (DB) Period and Open-Label Extension Period
Footnote: Patients who received placebo in the DB period received vixarelimab, 360 mg, subcutaneously once every 2 weeks during the open-label extension period. Error bars indicate 95% CIs.

Importance: Prurigo nodularis (PN) is a chronic inflammatory skin disease characterized by severe pruritus and nodule formation, significantly affecting patients’ quality of life. There is an unmet need for effective therapies to address both symptoms and disease progression.

Challenge

Observe the plot and evaluate the design decisions made. Check what graphical principles can be used to improve the plot. Ask yourself: What is the main message? And how can this be visually supported?

Visualisations

One submission was received for this challenge. During the webinar, step-by-step improvements moving from the original visualisation to the final submission were presented and discussed. Each step is presented below, along with notes on what is changed each time.

Rebuild of the original visualisation

Simplify by combining plots

Eliminate clutter - add clarity

Eliminate clutter - focus attention

Colour shading

Eliminate clutter - clarity and focus

Information

Additional details (Final submission)

link to code

Code

# libraries
library(ggplot2)
library(cowplot)
library(gridExtra)
library(dplyr)
#import
# study_data <- read.excel("simulated_study_data.xlsx)

#### rebuild original plot (1) ####

p1_a <- ggplot(
  study_data |> dplyr::filter(Week <= 16),
  aes(
    x = Week,
    y = Mean,
    colour = Arm,
    group = Arm
  )
) +
  geom_errorbar(
    aes(
      ymin = Low,
      ymax = High
    ),
    width = 0.25,
    linewidth = 0.4
  ) +
  geom_line(linewidth = 1) +
  geom_point(aes(shape = Arm), size = 4, show.legend = FALSE) +
  scale_colour_manual(
    values = c(
      "Placebo" = "#081820",
      "Vixarelimab, 120 mg"   = "#EA4928",
      "Vixarelimab, 360 mg"   = "#284A52",
      "Vixarelimab, 540 mg"   = "#54B1F9"
    )
  ) +
  scale_x_continuous(
    breaks = seq(0, 16, 1)
  ) +
  labs(
    x = "Study week",
    y = "Change from Baseline, %",
    colour = "Treatment"
  ) +
  theme_bw() +
  theme(
    legend.position = "bottom"
  ) +
  theme(
    legend.position = c(0.85, 0.81),
    legend.background = element_rect(
      fill = "white",
      colour = "black"
    ),
    legend.box.background = element_rect(
      colour = "black"
    ),
    
    # X-Gridlines entfernen
    panel.grid.major.x = element_blank(),
    panel.grid.minor.x = element_blank()
  ) +
  coord_cartesian(
    ylim = c(-60, 20)
  ) +
  scale_shape_manual(
    values = c(
      "Placebo" = 15,  # Quadrat
      "Vixarelimab, 120 mg"   = 16,  # Kreis
      "Vixarelimab, 360 mg"   = 17,  # Dreieck
      "Vixarelimab, 540 mg"   = 18   # Diamant
    )
  )

p1_a

p1_b <- ggplot(
  study_data |> dplyr::filter(Week >= 18),
  aes(
    x = Week,
    y = Mean,
    colour = Arm,
    group = Arm
  )
) +
  geom_errorbar(
    aes(
      ymin = Low,
      ymax = High
    ),
    width = 0.25,
    linewidth = 0.4
  ) +
  geom_line(linewidth = 1, show.legend = FALSE) +
  geom_point(aes(shape = Arm), size = 4, show.legend = FALSE) +
  scale_colour_manual(
    values = c(
      "Placebo" = "#081820",
      "Vixarelimab, 120 mg"   = "#EA4928",
      "Vixarelimab, 360 mg"   = "#284A52",
      "Vixarelimab, 540 mg"   = "#54B1F9"
    )
  ) +
  scale_x_continuous(
    breaks = seq(18, 52, 2)
  ) +
  labs(
    x = "Study week",
    y = "Change from Baseline, %",
    colour = "Treatment"
  ) +
  theme_bw() +
  theme(
    legend.position = "none"
  ) +
  theme(
    # X-Gridlines entfernen
    panel.grid.major.x = element_blank(),
    panel.grid.minor.x = element_blank()
  ) +
  scale_shape_manual(
    values = c(
      "Placebo" = 15,  # Quadrat
      "Vixarelimab, 120 mg"   = 16,  # Kreis
      "Vixarelimab, 360 mg"   = 17,  # Dreieck
      "Vixarelimab, 540 mg"   = 18   # Diamant
    )
  )
p1_b

p1 <- plot_grid(
  p1_a,
  p1_b,
  ncol = 1,
  align = "v"
)

p1
#### merge to one plot (2) ####

p2 <- ggplot(
  study_data,
  aes(
    x = Week,
    y = Mean,
    colour = Arm,
    group = Arm
  )
) +
  geom_errorbar(
    aes(
      ymin = Low,
      ymax = High
    ),
    width = 0.25,
    linewidth = 0.4
  ) +
  geom_line(linewidth = 1) +
  geom_point(aes(shape = Arm), size = 4, show.legend = FALSE) +
  scale_colour_manual(
    values = c(
      "Placebo" = "#081820",
      "Vixarelimab, 120 mg"   = "#EA4928",
      "Vixarelimab, 360 mg"   = "#284A52",
      "Vixarelimab, 540 mg"   = "#54B1F9"
    )
  ) +
  scale_x_continuous(
    breaks = seq(0, 52, 2)
  ) +
  labs(
    x = "Study week",
    y = "Change from Baseline, %",
    colour = "Treatment"
  ) +
  theme_bw() +
  theme(
    legend.position = "bottom"
  ) +
  theme(
    legend.position = c(0.85, 0.85),
    legend.background = element_rect(
      fill = "white",
      colour = "black"
    ),
    legend.box.background = element_rect(
      colour = "black"
    ),
    
    # X-Gridlines entfernen
    panel.grid.major.x = element_blank(),
    panel.grid.minor.x = element_blank()
  ) +
  scale_shape_manual(
    values = c(
      "Placebo" = 15,  # Quadrat
      "Vixarelimab, 120 mg"   = 16,  # Kreis
      "Vixarelimab, 360 mg"   = 17,  # Dreieck
      "Vixarelimab, 540 mg"   = 18   # Diamant
    )
  )

p2


#### error bands instead of error bars (3) ####

p3 <- ggplot(
  study_data,
  aes(
    x = Week,
    y = Mean,
    colour = Arm,
    group = Arm
  )
) +
  # geom_errorbar(
  #   aes(
  #     ymin = Low,
  #     ymax = High
  #   ),
  #   width = 0.25,
  #   linewidth = 0.4
  # ) +
  geom_ribbon(
    data = study_data,
    aes(ymin = Low, ymax = High, fill = Arm),
    alpha = 0.15,
    colour = NA,
    show.legend = FALSE
  ) +
  geom_line(linewidth = 1) +
  geom_point(aes(shape = Arm), size = 4, show.legend = FALSE) +
  scale_colour_manual(
    values = c(
      "Placebo" = "#081820",
      "Vixarelimab, 120 mg"   = "#EA4928",
      "Vixarelimab, 360 mg"   = "#284A52",
      "Vixarelimab, 540 mg"   = "#54B1F9"
    )
  ) +
  scale_fill_manual(
    values = c(
      "Placebo" = "#081820",
      "Vixarelimab, 120 mg"   = "#EA4928",
      "Vixarelimab, 360 mg"   = "#284A52",
      "Vixarelimab, 540 mg"   = "#54B1F9"
    )
  ) +
  scale_x_continuous(
    breaks = seq(0, 52, 2)
  ) +
  labs(
    x = "Study week",
    y = "Change from Baseline, %",
    colour = "Treatment"
  ) +
  theme_bw() +
  theme(
    legend.position = "bottom"
  ) +
  theme(
    legend.position = c(0.85, 0.85),
    legend.background = element_rect(
      fill = "white",
      colour = "black"
    ),
    legend.box.background = element_rect(
      colour = "black"
    ),
    
    # X-Gridlines entfernen
    panel.grid.major.x = element_blank(),
    panel.grid.minor.x = element_blank()
  ) +
  scale_shape_manual(
    values = c(
      "Placebo" = 15,  # Quadrat
      "Vixarelimab, 120 mg"   = 16,  # Kreis
      "Vixarelimab, 360 mg"   = 17,  # Dreieck
      "Vixarelimab, 540 mg"   = 18   # Diamant
    )
  )

p3


#### remove symbols except week 16 (4) ####

p4 <- ggplot(
  study_data,
  aes(
    x = Week,
    y = Mean,
    colour = Arm,
    group = Arm
  )
) +
  geom_ribbon(
    data = study_data,
    aes(ymin = Low, ymax = High, fill = Arm),
    alpha = 0.15,
    colour = NA,
    show.legend = FALSE
  ) +
  geom_line(linewidth = 1) +
  # geom_point(aes(shape = Arm), size = 4, show.legend = FALSE) +
  scale_colour_manual(
    values = c(
      "Placebo" = "#081820",
      "Vixarelimab, 120 mg"   = "#EA4928",
      "Vixarelimab, 360 mg"   = "#284A52",
      "Vixarelimab, 540 mg"   = "#54B1F9"
    )
  ) +
  scale_fill_manual(
    values = c(
      "Placebo" = "#081820",
      "Vixarelimab, 120 mg"   = "#EA4928",
      "Vixarelimab, 360 mg"   = "#284A52",
      "Vixarelimab, 540 mg"   = "#54B1F9"
    )
  ) +
  scale_x_continuous(
    breaks = seq(0, 52, 2)
  ) +
  geom_point(
    data = subset(study_data, Week == 16),
    aes(fill = Arm),
    shape = 21,
    size = 3,
    stroke =1.2,
    colour = "#FFF", 
    show.legend = FALSE
  ) +
  labs(
    x = "Study week",
    y = "Change from Baseline, %",
    colour = "Treatment"
  ) +
  theme_bw() +
  theme(
    legend.position = "bottom"
  ) +
  theme(
    legend.position = c(0.85, 0.85),
    legend.background = element_rect(
      fill = "white",
      colour = "black"
    ),
    legend.box.background = element_rect(
      colour = "black"
    ),
    
    # X-Gridlines entfernen
    panel.grid.major.x = element_blank(),
    panel.grid.minor.x = element_blank()
  ) +
  scale_shape_manual(
    values = c(
      "Placebo" = 15,  # Quadrat
      "Vixarelimab, 120 mg"   = 16,  # Kreis
      "Vixarelimab, 360 mg"   = 17,  # Dreieck
      "Vixarelimab, 540 mg"   = 18   # Diamant
    )
  )

p4


#### change colors add light grey background color (5) ####

p5 <- ggplot(
  study_data,
  aes(
    x = Week,
    y = Mean,
    colour = Arm,
    group = Arm
  )
) +
  geom_vline(
    xintercept = 16, 
    colour = "#B1B1B1"
  ) +
  geom_hline(
    yintercept = 0,
    colour = "#C5C5C5"
  ) +
  annotate(
    "rect",
    xmin = -Inf, xmax = 16,
    ymin = -Inf, ymax = Inf,
    fill = "#F6F6F6",
    alpha = 0.5
  ) +
  geom_ribbon(
    data = study_data,
    aes(ymin = Low, ymax = High, fill = Arm),
    alpha = 0.15,
    colour = NA,
    show.legend = FALSE
  ) +
  geom_line(linewidth = 1) +
  # geom_point(aes(shape = Arm), size = 4, show.legend = FALSE) +
  scale_colour_manual(
    values = c(
      "Placebo" = "#8E8E8E",
      "Vixarelimab, 120 mg"   = "#5498E2",
      "Vixarelimab, 360 mg"   = "#187AC7",
      "Vixarelimab, 540 mg"   = "#2A5B92"
    )
  ) +
  scale_fill_manual(
    values = c(
      "Placebo" = "#8E8E8E",
      "Vixarelimab, 120 mg"   = "#5498E2",
      "Vixarelimab, 360 mg"   = "#187AC7",
      "Vixarelimab, 540 mg"   = "#2A5B92"
    )
  ) +
  scale_x_continuous(
    breaks = seq(0, 52, 2)
  ) +
  geom_point(
    data = subset(study_data, Week == 16),
    aes(fill = Arm),
    shape = 21,
    size = 3,
    stroke =1.2,
    colour = "#FFF", 
    show.legend = FALSE
  ) +
  labs(
    x = "Study week",
    y = "Change from Baseline, %",
    colour = "Treatment"
  ) +
  theme_bw() +
  theme(
    legend.position = "bottom"
  ) +
  theme(
    legend.position = c(0.85, 0.85),
    legend.background = element_rect(
      fill = "white",
      colour = "black"
    ),
    legend.box.background = element_rect(
      colour = "black"
    ),
    
    # X-Gridlines entfernen
    panel.grid.major.x = element_blank(),
    panel.grid.minor.x = element_blank()
  ) +
  scale_shape_manual(
    values = c(
      "Placebo" = 15,  # Quadrat
      "Vixarelimab, 120 mg"   = 16,  # Kreis
      "Vixarelimab, 360 mg"   = 17,  # Dreieck
      "Vixarelimab, 540 mg"   = 18   # Diamant
    )
  )

p5

#### Change line width until week 16, change font sizes, change axis breaks, remove legend box (6) ####

p6 <- ggplot(
  study_data,
  aes(
    x = Week,
    y = Mean,
    colour = Arm,
    group = Arm
  )
) +
  geom_vline(
    xintercept = 16, 
    colour = "#B1B1B1"
  ) +
  geom_hline(
    yintercept = 0,
    colour = "#C5C5C5"
  ) +
  annotate(
    "rect",
    xmin = -Inf, xmax = 16,
    ymin = -Inf, ymax = Inf,
    fill = "#F6F6F6",
    alpha = 0.5
  ) +
  geom_ribbon(
    data = study_data,
    aes(ymin = Low, ymax = High, fill = Arm),
    alpha = 0.15,
    colour = NA,
    show.legend = FALSE
  ) +
  geom_line(
    data = subset(study_data, Week <= 16),
    aes(Week, Mean),
    linewidth = 1.2
  ) +
  geom_line(
    data = subset(study_data, Week >= 16),
    aes(Week, Mean),
    linewidth = 0.8
  ) +
  # geom_point(aes(shape = Arm), size = 4, show.legend = FALSE) +
  scale_colour_manual(
    values = c(
      "Placebo" = "#8E8E8E",
      "Vixarelimab, 120 mg"   = "#5498E2",
      "Vixarelimab, 360 mg"   = "#187AC7",
      "Vixarelimab, 540 mg"   = "#2A5B92"
    )
  ) +
  scale_fill_manual(
    values = c(
      "Placebo" = "#8E8E8E",
      "Vixarelimab, 120 mg"   = "#5498E2",
      "Vixarelimab, 360 mg"   = "#187AC7",
      "Vixarelimab, 540 mg"   = "#2A5B92"
    )
  ) +
  scale_x_continuous(
    breaks = seq(0, 52, 4),
    labels = \(x) ifelse(x == 0, "Baseline", x)
  ) +
  geom_point(
    data = subset(study_data, Week == 16),
    aes(fill = Arm),
    shape = 21,
    size = 3,
    stroke =1.2,
    colour = "#FFF", 
    show.legend = FALSE
  ) +
  labs(
    x = "Study week",
    y = "Change from Baseline, %",
    colour = "Treatment"
  ) +
  theme_bw() +
  theme(
    legend.position = "bottom"
  ) +
  theme(
    plot.title = element_text(
      margin = margin(b = 12)
    ),
    plot.caption = element_text(
      margin = margin(t = 12),
      size = 12,
      hjust = 0
    ),
    legend.position = c(0.6, 0.75),
    legend.background = element_blank(), 
    legend.key = element_blank(),
    axis.title = element_text(size = 12),
    axis.text = element_text(size = 12),
    legend.text = element_text(size = 12),
    legend.title = element_text(size = 12),
    panel.grid.major.x = element_blank(),
    panel.grid.minor.x = element_blank(),
    panel.grid.major.y = element_blank(),
    panel.grid.minor.y = element_blank()
  ) +
  scale_shape_manual(
    values = c(
      "Placebo" = 15,  # Quadrat
      "Vixarelimab, 120 mg"   = 16,  # Kreis
      "Vixarelimab, 360 mg"   = 17,  # Dreieck
      "Vixarelimab, 540 mg"   = 18   # Diamant
    )
  )

p6

#### Add title, labels, footnote (7) ####

p7 <- ggplot(
  study_data,
  aes(
    x = Week,
    y = Mean,
    colour = Arm,
    group = Arm
  )
) +
  geom_vline(
    xintercept = 16, 
    colour = "#B1B1B1"
  ) +
  geom_hline(
    yintercept = 0,
    colour = "#C5C5C5"
  ) +
  annotate(
    "rect",
    xmin = -Inf, xmax = 16,
    ymin = -Inf, ymax = Inf,
    fill = "#F6F6F6",
    alpha = 0.5
  ) +
  geom_ribbon(
    data = study_data,
    aes(ymin = Low, ymax = High, fill = Arm),
    alpha = 0.15,
    colour = NA,
    show.legend = FALSE
  ) +
  geom_line(
    data = subset(study_data, Week <= 16),
    aes(Week, Mean),
    linewidth = 1.2
  ) +
  geom_line(
    data = subset(study_data, Week >= 16),
    aes(Week, Mean),
    linewidth = 0.8
  ) +
  # geom_point(aes(shape = Arm), size = 4, show.legend = FALSE) +
  scale_colour_manual(
    values = c(
      "Placebo" = "#8E8E8E",
      "Vixarelimab, 120 mg"   = "#5498E2",
      "Vixarelimab, 360 mg"   = "#187AC7",
      "Vixarelimab, 540 mg"   = "#2A5B92"
    )
  ) +
  scale_fill_manual(
    values = c(
      "Placebo" = "#8E8E8E",
      "Vixarelimab, 120 mg"   = "#5498E2",
      "Vixarelimab, 360 mg"   = "#187AC7",
      "Vixarelimab, 540 mg"   = "#2A5B92"
    )
  ) +
  scale_x_continuous(
    breaks = seq(0, 52, 4),
    labels = \(x) ifelse(x == 0, "Baseline", x)
  ) +
  geom_point(
    data = subset(study_data, Week == 16),
    aes(fill = Arm),
    shape = 21,
    size = 3,
    stroke =1.2,
    colour = "#FFF", 
    show.legend = FALSE
  ) +
  labs(
    x = "Study week",
    y = "Change from Baseline, %",
    colour = "Treatment"
  ) +
  theme_bw() +
  theme(
    legend.position = "bottom"
  ) +
  scale_y_continuous(
    expand = expansion(mult = c(0.05, 0.15))
  ) +
  theme(
    plot.title = element_text(
      margin = margin(b = 12)
    ),
    plot.caption = element_text(
      margin = margin(t = 12),
      size = 12,
      hjust = 0
    ),
    legend.position = c(0.6, 0.75),
    legend.background = element_blank(), 
    legend.key = element_blank(),
    axis.title = element_text(size = 12),
    axis.text = element_text(size = 12),
    legend.text = element_text(size = 12),
    legend.title = element_text(size = 12),
    panel.grid.major.x = element_blank(),
    panel.grid.minor.x = element_blank(),
    panel.grid.major.y = element_blank(),
    panel.grid.minor.y = element_blank()
  ) +
  scale_shape_manual(
    values = c(
      "Placebo" = 15,  # Quadrat
      "Vixarelimab, 120 mg"   = 16,  # Kreis
      "Vixarelimab, 360 mg"   = 17,  # Dreieck
      "Vixarelimab, 540 mg"   = 18   # Diamant
    )
  )+  geom_label(
    label = "DB period", 
    x = -1,
    y = 10,
    label.size = 0.35,
    border.color = NA,
    color = "#000",
    fill = "#F6F6F6",
    hjust = 0,
    size = 4
  ) + 
  geom_label(
    label="Subcutaneously every 4 weeks", 
    x = -1,
    y = 5,
    label.size = 0.35,
    border.color = NA,
    color = "#000",
    fill="#F6F6F6",
    hjust = 0,
    size = 4
  ) + 
  geom_label(
    label="Open-label extension period", 
    x = 17,
    y = 10,
    label.size = 0.35,
    color = "#000",
    border.color = NA,
    fill="#FFF",
    hjust = 0,
    size = 4
  ) +
  geom_label(
    label="All participants: Vixarelimab, 360 mg, subcutaneously every 2 weeks", 
    x = 17,
    y = 5,
    label.size = 0.35,
    color = "#000",
    border.color = NA,
    fill="#FFF",
    hjust = 0,
    size = 4
  ) +
  labs(
    title = 
      paste("Figure 2. Least-Squares Means of Percentage Change From Baseline in Weekly Mean Worst Itch",
            "Numeric Rating Scale during the Double-Blind (DB) Period and Open-Label Extension Period",
            sep = "\n"
      ),
    caption = paste(
      "Vixarelimab and placebo were administered subcutaneously every 4 weeks during the DB period. During the open−label",
      "extension period, all participants received vixarelimab, 360 mg, subcutaneously every 2 weeks.",
      "Shaded areas indicate 95% CIs.", sep = "\n"
    )
  ) 

p7

#### Add Table to Legend (8) ####
p8 <- ggplot(
  study_data, 
  aes(
    x = Week,
    y = Mean,
    colour = Arm,
    group = Arm, 
    fill = Arm
  )
) +
  geom_vline(
    xintercept = 16, 
    colour = "#B1B1B1"
  ) +
  geom_hline(
    yintercept = 0,
    colour = "#C5C5C5"
  ) +
  annotate(
    "rect",
    xmin = -Inf, xmax = 16,
    ymin = -Inf, ymax = Inf,
    fill = "#F6F6F6",
    alpha = 0.4
  ) +
  geom_ribbon(
    data = study_data,
    aes(ymin = Low, ymax = High),
    alpha = 0.15,
    colour = NA,
    show.legend = FALSE
  ) +
  geom_line(
    data = subset(study_data, Week <= 16),
    aes(Week, Mean),
    linewidth = 1.2
  ) +
  geom_line(
    data = subset(study_data, Week >= 16),
    aes(Week, Mean),
    linewidth = 0.8
  ) +
  geom_point(
    data = subset(study_data, Week == 16),
    aes(fill = Arm),
    shape = 21,
    size = 3,
    stroke =1.2,
    colour = "#FFF"
  ) +
  #colors
  scale_colour_manual(
    name = "Week 16",
    values = c(
      "Placebo" = "#8E8E8E",
      "Vixarelimab, 120 mg"   = "#5498E2",
      "Vixarelimab, 360 mg"   = "#187AC7",
      "Vixarelimab, 540 mg"   = "#2A5B92"
    )
  ) +
  scale_fill_manual(
    name = "Week 16",
    values = c(
      "Placebo" = "#8E8E8E",
      "Vixarelimab, 120 mg"   = "#5498E2",
      "Vixarelimab, 360 mg"   = "#187AC7",
      "Vixarelimab, 540 mg"   = "#2A5B92"
    )
  ) +
  guides(
    colour = guide_legend(
      override.aes = list(
        shape = 21,
        fill = c( "#8E8E8E","#5498E2","#187AC7","#2A5B92"),
        size = 3,
        stroke = 0.8
      )
    )
  ) +
  scale_y_continuous(
    expand = expansion(mult = c(0.05, 0.15))
  ) +
  scale_x_continuous(
    breaks = seq(0, 52, 4),
    labels = \(x) ifelse(x == 0, "Baseline", x)
  ) +
  #labels/footnote/header
  labs(
    x = "Study Week",
    y = "Change from baseline, %",
    colour = "Treatment"
  ) +
  geom_label(
    label = "DB period", 
    x = -1,
    y = 10,
    label.size = 0.35,
    border.color = NA,
    color = "#000",
    fill = "#F6F6F6",
    hjust = 0,
    size = 4
  ) + 
  geom_label(
    label="Subcutaneously every 4 weeks", 
    x = -1,
    y = 5,
    label.size = 0.35,
    border.color = NA,
    color = "#000",
    fill="#F6F6F6",
    hjust = 0,
    size = 4
  ) + 
  geom_label(
    label="Open-label extension period", 
    x = 17,
    y = 10,
    label.size = 0.35,
    color = "#000",
    border.color = NA,
    fill="#FFF",
    hjust = 0,
    size = 4
  ) +
  geom_label(
    label="All participants: Vixarelimab, 360 mg, subcutaneously every 2 weeks", 
    x = 17,
    y = 5,
    label.size = 0.35,
    color = "#000",
    border.color = NA,
    fill="#FFF",
    hjust = 0,
    size = 4
  ) +
  labs(
    title = 
      paste("Figure 2. Least-Squares Means of Percentage Change From Baseline in Weekly Mean Worst Itch",
            "Numeric Rating Scale during the Double-Blind (DB) Period and Open-Label Extension Period",
            sep = "\n"
      ),
    caption = paste(
      "Vixarelimab and placebo were administered subcutaneously every 4 weeks during the DB period. During the open−label",
      "extension period, all participants received vixarelimab, 360 mg, subcutaneously every 2 weeks.",
      "Shaded areas indicate 95% CIs.", sep = "\n"
    )
  ) +
  #theme
  theme_bw() +
  theme(
    plot.title = element_text(
      margin = margin(b = 12)
    ),
    plot.caption = element_text(
      margin = margin(t = 12),
      size = 12,
      hjust = 0
    ),
    legend.position = c(0.6, 0.75),
    legend.background = element_blank(), 
    legend.key = element_blank(),
    axis.title = element_text(size = 12),
    axis.text = element_text(size = 12),
    legend.text = element_text(size = 12),
    legend.title = element_text(size = 12),
    panel.grid.major.x = element_blank(),
    panel.grid.minor.x = element_blank(),
    panel.grid.major.y = element_blank(),
    panel.grid.minor.y = element_blank()
  ) 

p8

tab_dat <- data.frame(
  n = study_data |> dplyr::filter(Week == 16) |> dplyr::pull(n),
  LS_Mean = study_data |> dplyr::filter(Week == 16) |> dplyr::pull(Mean)|> round(2),
  SE = paste0("(",study_data |> dplyr::filter(Week == 16) |> dplyr::pull(SE) |> round(2),")")
) |> distinct()

names(tab_dat) <- c("n", "LS Mean", "  (SE), %")

tab_grob <- tableGrob(
  tab_dat,
  rows = NULL,
  theme = ttheme_minimal(
    core = list(
      fg_params = list(fontsize = 12),
      padding = unit(c(1, 1), "mm")
    ),
    colhead = list(
      fg_params = list(
        fontsize = 12,
        fontface = "bold"
      ),
      padding = unit(c(1, 1), "mm")
    )
  )
)

tab_grob$heights <- unit(rep(1.2, nrow(tab_grob)), "lines")

table_plot <- ggdraw(tab_grob)

ggdraw(p8) + 
  draw_plot(
    table_plot,
    x = 0.71,
    y = 0.55,
    width = 0.22,
    height = 0.35
  )

Back to blog

Citation

For attribution, please cite this work as

SIG (2026, Nov. 11). VIS-SIG Blog: Wonderful Wednesday November 2026 (79). Retrieved from https://graphicsprinciples.github.io/posts/2026-11-11-wonderful-wednesday-november-2026/

BibTeX citation

@misc{sig2026wonderful,
  author = {SIG, PSI VIS},
  title = {VIS-SIG Blog: Wonderful Wednesday November 2026 (79)},
  url = {https://graphicsprinciples.github.io/posts/2026-11-11-wonderful-wednesday-november-2026/},
  year = {2026}
}