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