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
1418 point_effects : np .ndarray # (T_post,) mean effect per time point
1519 point_effect_lower : np .ndarray # (T_post,) lower CI per time point
1620 point_effect_upper : np .ndarray # (T_post,) upper CI per time point
1721 ci_lower : float # lower CI bound on average effect
1822 ci_upper : float # upper CI bound on average effect
1923 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
2027 cumulative_effect : np .ndarray # (T_post,) cumulative point effects
2128 cumulative_effect_lower : np .ndarray # (T_post,) lower cumulative CI
2229 cumulative_effect_upper : np .ndarray # (T_post,) upper cumulative CI
2330 cumulative_effect_total : float # total cumulative effect
31+ cumulative_effect_sd : float # std of per-sample cumulative effects
32+
33+ # Relative effects
2434 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
2540 p_value : float # Bayesian one-sided tail probability
41+
42+ # Counterfactual predictions
2643 predictions_mean : np .ndarray # (T_post,) mean counterfactual
44+ predictions_sd : np .ndarray # (T_post,) std of predictions per time point
2745 predictions_lower : np .ndarray # (T_post,) lower CI counterfactual
2846 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
2953
3054
3155class CausalAnalysis :
@@ -78,12 +102,74 @@ def compute_effects(
78102 )
79103 cumulative_effect_total = float (cumulative_effect [- 1 ])
80104
81- # Relative effect
82- pred_mean_total = predictions .mean ()
83- if abs (pred_mean_total ) > 1e-10 :
84- relative_effect_mean = point_effect_mean / pred_mean_total
105+ # Actual observed values
106+ actual = y_post .copy ()
107+
108+ # Per-time-point std of predictions across samples
109+ if n_samples == 1 :
110+ predictions_sd_arr = np .zeros (predictions .shape [1 ])
85111 else :
86- relative_effect_mean = 0.0
112+ predictions_sd_arr = np .std (predictions , axis = 0 , ddof = 1 )
113+
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,)
117+
118+ if n_samples == 1 :
119+ average_prediction_sd = 0.0
120+ cumulative_prediction_sd = 0.0
121+ else :
122+ average_prediction_sd = float (np .std (avg_pred_per_sample , ddof = 1 ))
123+ cumulative_prediction_sd = float (np .std (cum_pred_per_sample , ddof = 1 ))
124+
125+ average_prediction_lower = float (
126+ np .percentile (avg_pred_per_sample , 100 * lower_q )
127+ )
128+ average_prediction_upper = float (
129+ np .percentile (avg_pred_per_sample , 100 * upper_q )
130+ )
131+ cumulative_prediction_lower = float (
132+ np .percentile (cum_pred_per_sample , 100 * lower_q )
133+ )
134+ cumulative_prediction_upper = float (
135+ np .percentile (cum_pred_per_sample , 100 * upper_q )
136+ )
137+
138+ # Effect s.d. scalars
139+ cum_effects_per_sample = effects .sum (axis = 1 ) # (n_samples,)
140+
141+ if n_samples == 1 :
142+ average_effect_sd = 0.0
143+ cumulative_effect_sd = 0.0
144+ else :
145+ average_effect_sd = float (np .std (avg_effects , ddof = 1 ))
146+ cumulative_effect_sd = float (np .std (cum_effects_per_sample , ddof = 1 ))
147+
148+ # Relative effect per sample
149+ avg_pred_per_sample_safe = np .where (
150+ np .abs (avg_pred_per_sample ) > 1e-10 ,
151+ avg_pred_per_sample ,
152+ np .nan ,
153+ )
154+ rel_effects_per_sample = np .where (
155+ np .abs (avg_pred_per_sample ) > 1e-10 ,
156+ avg_effects / avg_pred_per_sample_safe ,
157+ 0.0 ,
158+ )
159+
160+ relative_effect_mean = float (rel_effects_per_sample .mean ())
161+
162+ if n_samples == 1 :
163+ relative_effect_sd = 0.0
164+ else :
165+ relative_effect_sd = float (np .std (rel_effects_per_sample , ddof = 1 ))
166+
167+ relative_effect_lower = float (
168+ np .percentile (rel_effects_per_sample , 100 * lower_q )
169+ )
170+ relative_effect_upper = float (
171+ np .percentile (rel_effects_per_sample , 100 * upper_q )
172+ )
87173
88174 # p-value: proportion of samples where average effect has opposite sign
89175 if point_effect_mean >= 0 :
@@ -99,19 +185,32 @@ def compute_effects(
99185 predictions_upper = np .percentile (predictions , 100 * upper_q , axis = 0 )
100186
101187 return CausalImpactResults (
188+ actual = actual ,
102189 point_effects = point_effects ,
103190 point_effect_lower = point_effect_lower ,
104191 point_effect_upper = point_effect_upper ,
105192 ci_lower = ci_lower ,
106193 ci_upper = ci_upper ,
107194 point_effect_mean = point_effect_mean ,
195+ average_effect_sd = average_effect_sd ,
108196 cumulative_effect = cumulative_effect ,
109197 cumulative_effect_lower = cumulative_effect_lower ,
110198 cumulative_effect_upper = cumulative_effect_upper ,
111199 cumulative_effect_total = cumulative_effect_total ,
200+ cumulative_effect_sd = cumulative_effect_sd ,
112201 relative_effect_mean = relative_effect_mean ,
202+ relative_effect_sd = relative_effect_sd ,
203+ relative_effect_lower = relative_effect_lower ,
204+ relative_effect_upper = relative_effect_upper ,
113205 p_value = p_value ,
114206 predictions_mean = predictions_mean ,
207+ predictions_sd = predictions_sd_arr ,
115208 predictions_lower = predictions_lower ,
116209 predictions_upper = predictions_upper ,
210+ average_prediction_sd = average_prediction_sd ,
211+ average_prediction_lower = average_prediction_lower ,
212+ average_prediction_upper = average_prediction_upper ,
213+ cumulative_prediction_sd = cumulative_prediction_sd ,
214+ cumulative_prediction_lower = cumulative_prediction_lower ,
215+ cumulative_prediction_upper = cumulative_prediction_upper ,
117216 )
0 commit comments