Skip to content

Commit 35a0c72

Browse files
author
Alain Hauser
committed
Make CausalImpact compatible with ggplot2 version 4 and testthat edition 3:
* Use vdiffr for plot comparisons to avoid relying on internal representation of ggplot objects. * Replace deprecated expectation statements.
1 parent e38049f commit 35a0c72

12 files changed

Lines changed: 98 additions & 60 deletions

DESCRIPTION

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,30 @@
11
Package: CausalImpact
22
Title: Inferring Causal Effects using Bayesian Structural Time-Series Models
3-
Date: 2022-11-07
4-
Author: Kay H. Brodersen <kbrodersen@google.com>,
5-
Alain Hauser <alhauser@google.com>
3+
Date: 2025-09-24
4+
Authors@R: c(person(given = c("Kay", "H."),
5+
family = "Brodersen",
6+
role = "aut",
7+
email = "kbrodersen@google.com"),
8+
person(given = "Alain",
9+
family = "Hauser",
10+
role = c("aut", "cre"),
11+
email = "alhauser@google.com"))
612
Maintainer: Alain Hauser <alhauser@google.com>
713
URL: https://google.github.io/CausalImpact/
814
Description: Implements a Bayesian approach to causal impact estimation in time
915
series, as described in Brodersen et al. (2015) <DOI:10.1214/14-AOAS788>.
1016
See the package documentation on GitHub
1117
<https://google.github.io/CausalImpact/> to get started.
12-
Copyright: Copyright (C) 2014-2022 Google, Inc.
13-
Version: 1.3.0
18+
Copyright: Copyright (C) 2014-2025 Google, Inc.
19+
Version: 1.4.1
1420
VignetteBuilder: knitr
1521
License: Apache License 2.0 | file LICENSE
1622
Imports: assertthat (>= 0.2.0), Boom, ggplot2, zoo
1723
Depends: bsts (>= 0.9.0)
18-
Suggests:
24+
Config/testthat/edition: 3
25+
Suggests:
1926
covr,
2027
knitr,
2128
rmarkdown,
22-
testthat
29+
testthat,
30+
vdiffr

R/impact_analysis.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -583,7 +583,7 @@ PrintSummary <- function(impact, digits = 2L) {
583583
# Print overall tail-area probability
584584
p <- summary$p[1]
585585
cat(paste0("Posterior tail-area probability p: ", round(p, 5), "\n"))
586-
cat(paste0("Posterior prob. of a causal effect: ",
586+
cat(paste0("Posterior probability of an effect: ",
587587
round((1 - p) * 100, ifelse(p < 0.01, 5, ifelse(p < 0.05, 3, 0))),
588588
"%\n"))
589589
cat("\n")

R/impact_inference.R

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,14 @@ InterpretSummaryTable <- function(summary, digits = 2L) {
356356
"percentage is [", rel.effect.lower[1], ", ",
357357
rel.effect.upper[1], "].")
358358

359+
# Comment on expected relative effect
360+
if (abs(summary$RelEffect[1]-summary$AbsEffect[1]/summary$Pred[1])>=.05) {
361+
stmt <- paste0(stmt, "\n\n(Note that the expected relative ",
362+
"effect isn't generally the same as the expected absolute ",
363+
"effect divided by the expected prediction since ",
364+
"distributions are often not symmetric.)")
365+
}
366+
359367
# Comment on significance
360368
if (sig && pos) {
361369
stmt <- paste0(stmt, "\n\nThis means that the positive effect observed ",
@@ -409,14 +417,18 @@ InterpretSummaryTable <- function(summary, digits = 2L) {
409417
if (p < summary$alpha[1]) {
410418
stmt <- paste0(stmt, "\n\nThe probability of obtaining this effect by ",
411419
"chance is very small (Bayesian one-sided tail-area ",
412-
"probability p = ", round(p, 3), "). This means the causal ",
413-
"effect can be considered statistically significant.")
420+
"probability p = ", round(p, 3), "). This means the ",
421+
"effect is statistically significant. It can be considered ",
422+
"causal if the model assumptions are satisfied.")
414423
} else {
415424
stmt <- paste0(stmt, "\n\nThe probability of obtaining this ",
416425
"effect by chance is p = ", round(p, 3), ". This ",
417426
"means the effect may be spurious and would generally ",
418427
"not be considered statistically significant.")
419428
}
429+
stmt <- paste0(stmt, " For more details, including the model assumptions ",
430+
"behind the method, see ",
431+
"https://google.github.io/CausalImpact/.")
420432
return(stmt)
421433
}
422434

R/impact_plot.R

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -137,20 +137,20 @@ CreateImpactPlot <- function(impact, metrics = c("original", "pointwise",
137137
impact$model$post.period,
138138
time(impact$series))
139139
q <- q + geom_vline(xintercept = xintercept,
140-
colour = "darkgrey", size = 0.8, linetype = "dashed")
140+
colour = "darkgrey", linewidth = 0.8, linetype = "dashed")
141141

142142
# Add zero line to pointwise and cumulative plot
143143
q <- q + geom_line(aes(y = baseline),
144-
colour = "darkgrey", size = 0.8, linetype = "solid",
144+
colour = "darkgrey", linewidth = 0.8, linetype = "solid",
145145
na.rm = TRUE)
146146

147147
# Add point predictions
148148
q <- q + geom_line(aes(y = mean), data,
149-
size = 0.6, colour = "darkblue", linetype = "dashed",
149+
linewidth = 0.6, colour = "darkblue", linetype = "dashed",
150150
na.rm = TRUE)
151151

152152
# Add observed data
153-
q <- q + geom_line(aes(y = response), size = 0.6, na.rm = TRUE)
153+
q <- q + geom_line(aes(y = response), linewidth = 0.6, na.rm = TRUE)
154154
return(q)
155155
}
156156

README.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,13 @@ to be stable during the post-intervention period. Understanding and checking
2424
these assumptions for any given application is critical for obtaining valid
2525
conclusions.
2626

27+
For Python users we recommend the implementation in
28+
[TFP CausalImpact](https://github.com/google/tfp-causalimpact), contributed by
29+
Colin Carroll, David Moore, Jacob Burnim, Kyle Loveless, and Susanna Makela. The
30+
Python implementation uses TensorFlow Probability and has been designed to
31+
produce results close to those of the R package. Some differences are expected
32+
given the nature of randomized algorithms.
33+
2734
## Installation
2835

2936
```r
@@ -40,12 +47,15 @@ library(CausalImpact)
4047
## Further resources
4148

4249
* Manuscript:
43-
[Brodersen et al., Annals of Applied Statistics (2015)](https://research.google/pubs/pub41854/)
50+
[Brodersen et al., Annals of Applied Statistics (2015)](https://research.google/pubs/inferring-causal-impact-using-bayesian-structural-time-series-models/)
4451

4552
* For questions on the statistics behind CausalImpact:
4653
[Cross Validated](https://stats.stackexchange.com/questions/tagged/causalimpact)
4754

4855
* For questions on how to use the CausalImpact R package:
49-
[Stack Overflow](https://stackoverflow.com/questions/tagged/causalimpact)
56+
[Stack Overflow](https://stackoverflow.com/questions/tagged/causal-inference)
5057

5158
* [Bug reports](https://github.com/google/CausalImpact/issues)
59+
60+
* Python version:
61+
[TFP CausalImpact](https://github.com/google/tfp-causalimpact)

inst/CITATION

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ bibentry(
88

99
title = paste0("Inferring causal impact using {B}ayesian structural ",
1010
"time-series models"),
11-
author = personList(
11+
author = c(
1212
as.person("Kay H. Brodersen"),
1313
as.person("Fabian Gallusser"),
1414
as.person("Jim Koehler"),
@@ -18,5 +18,5 @@ bibentry(
1818
year = "2014",
1919
volume = "9",
2020
pages = "247--274",
21-
url = "https://research.google/pubs/pub41854/"
21+
url = "https://research.google/pubs/inferring-causal-impact-using-bayesian-structural-time-series-models/"
2222
)

tests/testthat/test-impact-analysis.R

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright 2014-2022 Google Inc. All rights reserved.
1+
# Copyright 2014-2025 Google Inc. All rights reserved.
22
#
33
# Licensed under the Apache License, Version 2.0 (the "License");
44
# you may not use this file except in compliance with the License.
@@ -12,8 +12,6 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
testthat::context("Unit tests for impact_analysis.R")
16-
1715
# Authors: kbrodersen@google.com (Kay H. Brodersen)
1816
# gallusser@google.com (Fabian Gallusser)
1917

@@ -48,7 +46,6 @@ CallAllS3Methods <- function(impact) {
4846
expect_error(print(impact, "foo"), "summary")
4947
expect_error(plot(impact), NA)
5048
expect_error(q <- plot(impact), NA)
51-
expect_error(plot(q), NA)
5249
}
5350

5451
test_that("FormatInputForCausalImpact", {
@@ -736,6 +733,7 @@ test_that("PrintSummary", {
736733
model.args))
737734
expect_output(PrintSummary(impact), "\\([0-9.]{3}[0-9]*\\)")
738735
expect_output(PrintSummary(impact, digits = 10), "\\([0-9.]{11}[0-9]*\\)")
736+
expect_output(PrintSummary(impact), "Posterior probability of an effect")
739737
expect_error(PrintSummary(impact, digits = 0), "positive")
740738
expect_error(PrintSummary(impact, digits = "test"), "positive")
741739
})

tests/testthat/test-impact-inference.R

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
testthat::context("Unit tests for impact_inference.R")
16-
1715
# Author: kbrodersen@google.com (Kay Brodersen)
1816

1917
test_that("GetPosteriorStateSamples", {
@@ -293,7 +291,7 @@ test_that("CompilePosteriorInferences", {
293291
alpha, UnStandardize)
294292
expect_equal(names(inferences), c("series", "summary", "report",
295293
"posterior.samples"))
296-
expect_is(inferences$posterior.samples, "matrix")
294+
expect_true(is.matrix(inferences$posterior.samples))
297295
expect_equal(ncol(inferences$posterior.samples), 200)
298296
expect_gte(nrow(inferences$posterior.samples), 80)
299297
expect_lte(nrow(inferences$posterior.samples), 100)

tests/testthat/test-impact-misc.R

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
testthat::context("Unit tests for impact_misc.R")
16-
1715
# Authors: kbrodersen@google.com (Kay Brodersen)
1816
# gallusser@google.com (Fabian Gallusser)
1917
# alhauser@google.com (Alain Hauser)
@@ -188,7 +186,7 @@ test_that("StandardizeAllVariables", {
188186
expect_equal(sd(result$data[, column]), 1, tolerance = 0.0001)
189187
})
190188
expect_equal(result$UnStandardize, Standardize(data[, 1])$UnStandardize,
191-
check.environment = FALSE)
189+
ignore_function_env = TRUE)
192190

193191
# Test that several columns are standardized correctly when fitting mean and
194192
# SD only over part of the rows.
@@ -198,7 +196,7 @@ test_that("StandardizeAllVariables", {
198196
expect_equal(sd(result$data[11 : 90, column]), 1, tolerance = 0.0001)
199197
})
200198
expect_equal(result$UnStandardize, Standardize(data[, 1])$UnStandardize,
201-
check.environment = FALSE)
199+
ignore_function_env = TRUE)
202200

203201
# Test healthy input: single series only
204202
set.seed(1)
@@ -209,15 +207,15 @@ test_that("StandardizeAllVariables", {
209207
expect_equal(mean(result$data), 0, tolerance = 0.0001)
210208
expect_equal(sd(result$data), 1, tolerance = 0.0001)
211209
expect_equal(result$UnStandardize, Standardize(data)$UnStandardize,
212-
check.environment = FALSE)
210+
ignore_function_env = TRUE)
213211

214212
# Test that a single series is standardized correctly when fitting mean and SD
215213
# only over part of the data range.
216214
result <- StandardizeAllVariables(data, c(11, 90))
217215
expect_equal(mean(result$data[11: 90]), 0, tolerance = 0.0001)
218216
expect_equal(sd(result$data[11 : 90]), 1, tolerance = 0.0001)
219217
expect_equal(result$UnStandardize, Standardize(data)$UnStandardize,
220-
check.environment = FALSE)
218+
ignore_function_env = TRUE)
221219
})
222220

223221
test_that("GetPeriodIndices.InvalidInput", {

tests/testthat/test-impact-model.R

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,6 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15-
testthat::context("Unit tests for impact_model.R")
16-
1715
# Author: kbrodersen@google.com (Kay Brodersen)
1816

1917
test_that("ObservationsAreIllConditioned", {
@@ -175,9 +173,12 @@ test_that("ConstructModel", {
175173
expect_false(is.null(bsts.model))
176174
expect_equal(class(bsts.model), "bsts")
177175
expect_equal(as.numeric(bsts.model$original.series), as.numeric(data[, 1]))
178-
expect_equivalent(bsts.model$predictors[, 1], rep(1, nrow(data)))
179-
expect_equivalent(bsts.model$predictors[, 2], as.numeric(data[, 2]))
180-
expect_equivalent(bsts.model$predictors[, 3], as.numeric(data[, 3]))
176+
expect_equal(bsts.model$predictors[, 1], rep(1, nrow(data)),
177+
ignore_attr = TRUE)
178+
expect_equal(bsts.model$predictors[, 2], as.numeric(data[, 2]),
179+
ignore_attr = TRUE)
180+
expect_equal(bsts.model$predictors[, 3], as.numeric(data[, 3]),
181+
ignore_attr = TRUE)
181182
expect_equal(bsts.model$state.contributions,
182183
expected.model$state.contributions)
183184
})

0 commit comments

Comments
 (0)