1111class CausalImpactResults :
1212 """Results of causal impact analysis."""
1313
14- # Observed data
15- actual : np .ndarray # (T_post,) observed y in post period
16-
17- # Pointwise effects
18- point_effects : np .ndarray # (T_post,) mean effect per time point
19- point_effect_lower : np .ndarray # (T_post,) lower CI per time point
20- point_effect_upper : np .ndarray # (T_post,) upper CI per time point
21- ci_lower : float # lower CI bound on average effect
22- ci_upper : float # upper CI bound on average effect
23- point_effect_mean : float # mean of point effects across time
24- average_effect_sd : float # std of per-sample average effects
25-
26- # Cumulative effects
27- cumulative_effect : np .ndarray # (T_post,) cumulative point effects
28- cumulative_effect_lower : np .ndarray # (T_post,) lower cumulative CI
29- cumulative_effect_upper : np .ndarray # (T_post,) upper cumulative CI
30- cumulative_effect_total : float # total cumulative effect
31- cumulative_effect_sd : float # std of per-sample cumulative effects
32-
33- # Relative effects
34- relative_effect_mean : float # relative effect (effect / predicted)
35- relative_effect_sd : float # std of per-sample relative effects
36- relative_effect_lower : float # lower CI on relative effect
37- relative_effect_upper : float # upper CI on relative effect
38-
39- # Significance
40- p_value : float # Bayesian one-sided tail probability
41-
42- # Counterfactual predictions
43- predictions_mean : np .ndarray # (T_post,) mean counterfactual
44- predictions_sd : np .ndarray # (T_post,) std of predictions per time point
45- predictions_lower : np .ndarray # (T_post,) lower CI counterfactual
46- predictions_upper : np .ndarray # (T_post,) upper CI counterfactual
47- average_prediction_sd : float # std of per-sample average predictions
48- average_prediction_lower : float # lower CI on average prediction
49- average_prediction_upper : float # upper CI on average prediction
50- cumulative_prediction_sd : float # std of per-sample cumulative predictions
51- cumulative_prediction_lower : float # lower CI on cumulative prediction
52- cumulative_prediction_upper : float # upper CI on cumulative prediction
14+ actual : np .ndarray
15+ point_effects : np .ndarray
16+ point_effect_lower : np .ndarray
17+ point_effect_upper : np .ndarray
18+ ci_lower : float
19+ ci_upper : float
20+ point_effect_mean : float
21+ average_effect_sd : float
22+ cumulative_effect : np .ndarray
23+ cumulative_effect_lower : np .ndarray
24+ cumulative_effect_upper : np .ndarray
25+ cumulative_effect_total : float
26+ cumulative_effect_sd : float
27+ relative_effect_mean : float
28+ relative_effect_sd : float
29+ relative_effect_lower : float
30+ relative_effect_upper : float
31+ p_value : float
32+ predictions_mean : np .ndarray
33+ predictions_sd : np .ndarray
34+ predictions_lower : np .ndarray
35+ predictions_upper : np .ndarray
36+ average_prediction_sd : float
37+ average_prediction_lower : float
38+ average_prediction_upper : float
39+ cumulative_prediction_sd : float
40+ cumulative_prediction_lower : float
41+ cumulative_prediction_upper : float
5342
5443
5544class CausalAnalysis :
@@ -66,28 +55,19 @@ def compute_effects(
6655
6756 n_samples = predictions .shape [0 ]
6857
69- # Effect per sample per time point: observed - counterfactual
70- # predictions shape: (n_samples, t_post )
71- effects = y_post [ np . newaxis , :] - predictions # (n_samples, t_post )
58+ effects = y_post [ np . newaxis , :] - predictions
59+ avg_effects = effects . mean ( axis = 1 )
60+ point_effects = effects . mean ( axis = 0 )
7261
73- # Average effect across time for each sample
74- avg_effects = effects .mean (axis = 1 ) # (n_samples,)
75-
76- # Point effects: mean across samples at each time point
77- point_effects = effects .mean (axis = 0 ) # (t_post,)
78-
79- # Summary-table CI on average effect uses sample-average quantiles.
8062 lower_q = alpha / 2
8163 upper_q = 1 - alpha / 2
8264 point_effect_lower = np .percentile (effects , 100 * lower_q , axis = 0 )
8365 point_effect_upper = np .percentile (effects , 100 * upper_q , axis = 0 )
8466 ci_lower = float (np .percentile (avg_effects , 100 * lower_q ))
8567 ci_upper = float (np .percentile (avg_effects , 100 * upper_q ))
8668
87- # Mean effect
8869 point_effect_mean = float (avg_effects .mean ())
8970
90- # Cumulative effect
9171 cumulative_effect = np .cumsum (point_effects )
9272 cum_effects_samples = np .cumsum (effects , axis = 1 )
9373 cumulative_effect_lower = np .percentile (
@@ -102,18 +82,15 @@ def compute_effects(
10282 )
10383 cumulative_effect_total = float (cumulative_effect [- 1 ])
10484
105- # Actual observed values
10685 actual = y_post .copy ()
10786
108- # Per-time-point std of predictions across samples
10987 if n_samples == 1 :
11088 predictions_sd_arr = np .zeros (predictions .shape [1 ])
11189 else :
11290 predictions_sd_arr = np .std (predictions , axis = 0 , ddof = 1 )
11391
114- # Prediction scalars (cross-sample aggregates)
115- avg_pred_per_sample = predictions .mean (axis = 1 ) # (n_samples,)
116- cum_pred_per_sample = predictions .sum (axis = 1 ) # (n_samples,)
92+ avg_pred_per_sample = predictions .mean (axis = 1 )
93+ cum_pred_per_sample = predictions .sum (axis = 1 )
11794
11895 if n_samples == 1 :
11996 average_prediction_sd = 0.0
@@ -135,8 +112,7 @@ def compute_effects(
135112 np .percentile (cum_pred_per_sample , 100 * upper_q )
136113 )
137114
138- # Effect s.d. scalars
139- cum_effects_per_sample = effects .sum (axis = 1 ) # (n_samples,)
115+ cum_effects_per_sample = effects .sum (axis = 1 )
140116
141117 if n_samples == 1 :
142118 average_effect_sd = 0.0
@@ -145,7 +121,6 @@ def compute_effects(
145121 average_effect_sd = float (np .std (avg_effects , ddof = 1 ))
146122 cumulative_effect_sd = float (np .std (cum_effects_per_sample , ddof = 1 ))
147123
148- # Relative effect per sample
149124 avg_pred_per_sample_safe = np .where (
150125 np .abs (avg_pred_per_sample ) > 1e-10 ,
151126 avg_pred_per_sample ,
@@ -171,15 +146,12 @@ def compute_effects(
171146 np .percentile (rel_effects_per_sample , 100 * upper_q )
172147 )
173148
174- # p-value: proportion of samples where average effect has opposite sign
175149 if point_effect_mean >= 0 :
176150 p_value = float (np .mean (avg_effects < 0 ))
177151 else :
178152 p_value = float (np .mean (avg_effects > 0 ))
179- # Ensure minimum p-value of 1/n_samples
180153 p_value = max (p_value , 1.0 / n_samples )
181154
182- # Counterfactual prediction summaries
183155 predictions_mean = predictions .mean (axis = 0 )
184156 predictions_lower = np .percentile (predictions , 100 * lower_q , axis = 0 )
185157 predictions_upper = np .percentile (predictions , 100 * upper_q , axis = 0 )
0 commit comments