What should the denominator be for my delayed-reporting proportion? (Also a ggplot oddity)

Describe your issue

Hi all, I’m working with Ebola surveillance data and plotting the weekly proportion of cases reported more than 7 days after symptom onset. The code runs without errors, but I’m unsure about two things:

delay_1wk <- surv %>%
  mutate(diff_1wk = as.numeric(date_report - date_onset) > 7) %>%
  group_by(week = floor_date(date_report, "week")) %>%
  summarise(
    cases       = n(),
    delayed     = sum(diff_1wk, na.rm = TRUE),
    delayed_pct = delayed / cases)

ggplot(delay_1wk, aes(x = week, y = delayed_pct)) +
  geom_line(linewidth = 1, color = "brown")
  scale_y_continuous(labels = percent)

1. The y-axis still shows 0.1, 0.2, etc. instead of percentages. Why isn’t it converting?

2. What I’m really curious about: should cases with a missing onset date stay in the denominator?

If I drop them, the proportion goes up. How do you handle this in your own reports? Do you mention the missing cases in the caption, in a separate table, or somewhere else?

Also, floor_date starts my weeks on Sunday. Should I change this to match epi weeks?

Hey! Two separate issues here, one syntax bug and one that’s really an analysis choice.

The percent axis: your ggplot() chain is broken. The line break after geom_line(...) drops the + before scale_y_continuous(), so R treats that as a new, separate expression instead of a layer added to the plot. It runs without error because scale_y_continuous(labels = percent) on its own is valid R, it just builds a scale object and prints it, then discards it. It never touches your plot.

ggplot(delay_1wk, aes(x = week, y = delayed_pct)) +
  geom_line(linewidth = 1, color = "brown") +
  scale_y_continuous(labels = scales::percent)

extra tip: rename percent as scales::percent too, just so it’s unambiguous where it’s coming from regardless of what else is loaded.

Missing onset dates in the denominator: this is a judgment call rather than a bug, and reasonable surveillance reports handle it differently, but the common approach is to exclude cases with missing date_onset from this specific calculation (since delay can’t be computed for them) while reporting the exclusion explicitly, rather than silently dropping them or treating missing as zero delay.

delay_1wk <- surv %>%
  mutate(diff_1wk = as.numeric(date_report - date_onset) > 7) %>%
  group_by(week = floor_date(date_report, "week", week_start = 1)) %>%
  summarise(
    cases         = n(),
    missing_onset = sum(is.na(date_onset)),
    delayed       = sum(diff_1wk, na.rm = TRUE),
    delayed_pct   = delayed / sum(!is.na(diff_1wk))
  )

sum(!is.na(diff_1wk)) as the denominator (instead of cases) means the percentage reflects only cases where delay could actually be assessed, and missing_onset gives you a column to report alongside it, either in a caption, a footnote row, or a separate small table, so the reader can see how much data was excluded and judge for themselves whether that matters. Silently dropping missing cases without noting it anywhere is the version worth avoiding, since the reader has no way to know the completeness of the underlying data.

On epi weeks: yes, floor_date(x, "week") defaults to Sunday starts, which isn’t the same as an MMWR epi week. I added week_start = 1 above to at least start weeks on Monday, but that still isn’t full epi week logic (which anchors to the first Saturday of the year, not a fixed weekday). If you want true MMWR epi weeks, {aweek}'s date2week() is the more reliable option:

library(aweek)

delay_1wk <- surv %>%
  mutate(
    diff_1wk = as.numeric(date_report - date_onset) > 7,
    epiweek  = date2week(date_report, week_start = "Sunday", floor_day = TRUE)
  ) %>%
  group_by(epiweek) %>%
  summarise(...)

date2week() returns a proper epiweek label (like 2026-W12) rather than just a floored date

Hope this helps!

Best

Luis