Bug description
Calling plot() on a GeoLift object with treatment_end_date, frequency = "weekly", and post_treatment_periods > 0 raises a length-mismatch error in $<-.data.frame. The same call pattern works correctly with frequency = "daily", isolating the issue to the weekly code path.
The Time vector passed to the plot's internal data frame is built with treatment_end_time + post_treatment_periods elements, but the data frame it's assigned to only has treatment_end_time rows. The mismatch is consistently equal to post_treatment_periods across runs:
| dataset |
treatment_end_time |
post_treatment_periods |
Time length |
data rows |
| Bundled (weekly-aggregated) |
16 |
2 |
18 |
16 |
| Private weekly |
72 |
2 |
74 |
72 |
| Private weekly |
74 |
2 |
76 |
74 |
| Private weekly |
70 |
4 |
74 |
70 |
Doc note (separate, minor): the Measuring Lagged Effects blog post refers to the argument as post_test_treatment_periods; the actual argument is post_treatment_periods. Worth aligning while this is being touched.
Session information
R version 4.5.3 (2026-03-11)
Platform: aarch64-apple-darwin20
Running under: macOS Tahoe 26.5
Matrix products: default
BLAS: /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/4.5-arm64/Resources/lib/libRlapack.dylib; LAPACK version 3.12.1
locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
time zone: America/Sao_Paulo
tzcode source: internal
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] ggplot2_4.0.3 tidyr_1.3.2 dplyr_1.2.1 GeoLift_2.7.5
loaded via a namespace (and not attached):
[1] gtable_0.3.6 xfun_0.57 remotes_2.5.0 GGally_2.4.0
[5] lattice_0.22-9 numDeriv_2016.8-1.1 quadprog_1.5-8 vctrs_0.7.3
[9] tools_4.5.3 generics_0.1.4 CausalImpact_1.4.1 augsynth_0.2.0
[13] curl_7.1.0 parallel_4.5.3 sandwich_3.1-1 proxy_0.4-29
[17] tibble_3.3.1 xts_0.14.2 pkgconfig_2.0.3 MarketMatching_1.2.1
[21] Matrix_1.7-5 RColorBrewer_1.1-3 S7_0.2.2 rngtools_1.5.2
[25] stringmagic_1.2.0 assertthat_0.2.1 lifecycle_1.0.5 compiler_4.5.3
[29] farver_2.1.2 stringr_1.6.0 progress_1.2.3 codetools_0.2-20
[33] gsynth_1.4.0 Formula_1.2-5 crayon_1.5.3 pillar_1.11.1
[37] panelView_1.3.0 MASS_7.3-65 doRNG_1.8.6.3 iterators_1.0.14
[41] abind_1.4-8 foreach_1.5.2 nlme_3.1-169 parallelly_1.47.0
[45] ggstats_0.13.0 tidyselect_1.2.1 digest_0.6.39 mvtnorm_1.3-7
[49] stringi_1.8.7 future_1.70.0 reshape2_1.4.5 purrr_1.2.2
[53] listenv_0.10.1 labeling_0.4.3 grid_4.5.3 cli_3.6.6
[57] magrittr_2.0.5 utf8_1.2.6 future.apply_1.20.2 withr_3.0.2
[61] dreamerr_1.5.0 prettyunits_1.2.0 scales_1.4.0 globals_0.19.1
[65] gridExtra_2.3 Boom_0.9.16 hms_1.1.4 zoo_1.8-15
[69] evaluate_1.0.5 knitr_1.51 BoomSpikeSlab_1.2.7 dtw_1.23-2
[73] doParallel_1.0.17 fixest_0.14.0 fect_2.2.0 rlang_1.2.0
[77] Rcpp_1.1.1 glue_1.8.1 directlabels_2025.6.24 osqp_1.0.0
[81] rstudioapi_0.18.0 R6_2.6.1 plyr_1.8.9 bsts_0.9.11
Reproduction steps
The bundled GeoLift_Test dataset is daily, so aggregating to weekly first gives a self-contained reproduction:
library(GeoLift)
library(dplyr)
data(GeoLift_Test)
# Aggregate the bundled daily data to weekly
weekly <- GeoLift_Test |>
mutate(date = as.Date(date),
week = as.Date(cut(date, breaks = "week", start.on.monday = TRUE))) |>
group_by(location, week) |>
summarise(Y = sum(Y), .groups = "drop") |>
rename(date = week) |>
mutate(date = format(date, "%Y-%m-%d"))
GTD_w <- GeoDataRead(data = weekly, date_id = "date",
location_id = "location", Y_id = "Y",
format = "yyyy-mm-dd")
n_periods <- max(GTD_w$time) # 16
GT_w <- GeoLift(Y_id = "Y", data = GTD_w,
locations = c("chicago", "portland"),
treatment_start_time = n_periods - 4,
treatment_end_time = n_periods)
last_date <- max(weekly$date)
# Errors:
plot(GT_w, type = "ATT",
treatment_end_date = last_date,
frequency = "weekly",
post_treatment_periods = 2)
Error output:
Error in `$<-.data.frame`(`*tmp*`, "Time", value = c(18624, 18631, 18638, :
replacement has 18 rows, data has 16
For contrast, the same pattern with the original daily data renders correctly:
GTD <- GeoDataRead(data = GeoLift_Test, date_id = "date",
location_id = "location", Y_id = "Y",
format = "yyyy-mm-dd")
GT <- GeoLift(Y_id = "Y", data = GTD,
locations = c("chicago", "portland"),
treatment_start_time = 91, treatment_end_time = 105)
plot(GT, type = "ATT",
treatment_end_date = "2022-03-08", # date corresponding to t = 105
frequency = "daily",
post_treatment_periods = 5)
Expected behavior
The plot should render with a dated x-axis and the trailing post_treatment_periods weeks visually delineated as the post-treatment window — matching the behavior documented in the Measuring Lagged Effects blog post and matching the behavior already produced when frequency = "daily".
A working alternative on the same fit (without dated axis) renders correctly:
plot(GT_w, type = "ATT", post_treatment_periods = 2)
So the underlying data is sufficient for the post-treatment window; the failure is specific to the dated-axis weekly code path.
Additional context
Workaround: omitting treatment_end_date and frequency produces a correct plot on weekly data, but with integer timestamps on the x-axis instead of dates.
Suspected location: the $<-.data.frame call is assigning a Time column of treatment_end_time + post_treatment_periods length to a data frame of treatment_end_time rows, consistently off by exactly post_treatment_periods. Whatever extends the Time sequence to cover the post-treatment window in the weekly code path isn't applied to the data frame being labeled.
Happy to test any patches or provide additional repros — also have a private weekly dataset (74 periods) where the same pattern shows up across multiple (treatment_end_time, post_treatment_periods) combinations if useful.
Bug description
Calling
plot()on aGeoLiftobject withtreatment_end_date,frequency = "weekly", andpost_treatment_periods > 0raises a length-mismatch error in$<-.data.frame. The same call pattern works correctly withfrequency = "daily", isolating the issue to the weekly code path.The Time vector passed to the plot's internal data frame is built with
treatment_end_time + post_treatment_periodselements, but the data frame it's assigned to only hastreatment_end_timerows. The mismatch is consistently equal topost_treatment_periodsacross runs:Doc note (separate, minor): the Measuring Lagged Effects blog post refers to the argument as
post_test_treatment_periods; the actual argument ispost_treatment_periods. Worth aligning while this is being touched.Session information
R version 4.5.3 (2026-03-11)
Platform: aarch64-apple-darwin20
Running under: macOS Tahoe 26.5
Matrix products: default
BLAS: /System/Library/Frameworks/Accelerate.framework/Versions/A/Frameworks/vecLib.framework/Versions/A/libBLAS.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/4.5-arm64/Resources/lib/libRlapack.dylib; LAPACK version 3.12.1
locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
time zone: America/Sao_Paulo
tzcode source: internal
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] ggplot2_4.0.3 tidyr_1.3.2 dplyr_1.2.1 GeoLift_2.7.5
loaded via a namespace (and not attached):
[1] gtable_0.3.6 xfun_0.57 remotes_2.5.0 GGally_2.4.0
[5] lattice_0.22-9 numDeriv_2016.8-1.1 quadprog_1.5-8 vctrs_0.7.3
[9] tools_4.5.3 generics_0.1.4 CausalImpact_1.4.1 augsynth_0.2.0
[13] curl_7.1.0 parallel_4.5.3 sandwich_3.1-1 proxy_0.4-29
[17] tibble_3.3.1 xts_0.14.2 pkgconfig_2.0.3 MarketMatching_1.2.1
[21] Matrix_1.7-5 RColorBrewer_1.1-3 S7_0.2.2 rngtools_1.5.2
[25] stringmagic_1.2.0 assertthat_0.2.1 lifecycle_1.0.5 compiler_4.5.3
[29] farver_2.1.2 stringr_1.6.0 progress_1.2.3 codetools_0.2-20
[33] gsynth_1.4.0 Formula_1.2-5 crayon_1.5.3 pillar_1.11.1
[37] panelView_1.3.0 MASS_7.3-65 doRNG_1.8.6.3 iterators_1.0.14
[41] abind_1.4-8 foreach_1.5.2 nlme_3.1-169 parallelly_1.47.0
[45] ggstats_0.13.0 tidyselect_1.2.1 digest_0.6.39 mvtnorm_1.3-7
[49] stringi_1.8.7 future_1.70.0 reshape2_1.4.5 purrr_1.2.2
[53] listenv_0.10.1 labeling_0.4.3 grid_4.5.3 cli_3.6.6
[57] magrittr_2.0.5 utf8_1.2.6 future.apply_1.20.2 withr_3.0.2
[61] dreamerr_1.5.0 prettyunits_1.2.0 scales_1.4.0 globals_0.19.1
[65] gridExtra_2.3 Boom_0.9.16 hms_1.1.4 zoo_1.8-15
[69] evaluate_1.0.5 knitr_1.51 BoomSpikeSlab_1.2.7 dtw_1.23-2
[73] doParallel_1.0.17 fixest_0.14.0 fect_2.2.0 rlang_1.2.0
[77] Rcpp_1.1.1 glue_1.8.1 directlabels_2025.6.24 osqp_1.0.0
[81] rstudioapi_0.18.0 R6_2.6.1 plyr_1.8.9 bsts_0.9.11
Reproduction steps
The bundled
GeoLift_Testdataset is daily, so aggregating to weekly first gives a self-contained reproduction:Error output:
For contrast, the same pattern with the original daily data renders correctly:
Expected behavior
The plot should render with a dated x-axis and the trailing
post_treatment_periodsweeks visually delineated as the post-treatment window — matching the behavior documented in the Measuring Lagged Effects blog post and matching the behavior already produced whenfrequency = "daily".A working alternative on the same fit (without dated axis) renders correctly:
So the underlying data is sufficient for the post-treatment window; the failure is specific to the dated-axis weekly code path.
Additional context
Workaround: omitting
treatment_end_dateandfrequencyproduces a correct plot on weekly data, but with integer timestamps on the x-axis instead of dates.Suspected location: the
$<-.data.framecall is assigning a Time column oftreatment_end_time + post_treatment_periodslength to a data frame oftreatment_end_timerows, consistently off by exactlypost_treatment_periods. Whatever extends the Time sequence to cover the post-treatment window in the weekly code path isn't applied to the data frame being labeled.Happy to test any patches or provide additional repros — also have a private weekly dataset (74 periods) where the same pattern shows up across multiple
(treatment_end_time, post_treatment_periods)combinations if useful.