@@ -1041,11 +1041,28 @@ def __init__(
1041
1041
(new_x ,) = build_design_matrices ([self ._x_design_info ], self .x_pred )
1042
1042
self .pred = self .model .predict (X = np .asarray (new_x ))
1043
1043
1044
- # Calculate the change in gradient by evaluating the function below the kink
1045
- # point, at the kink point, and above the kink point.
1046
- # NOTE: `"treated": np.array([0, 1])`` assumes treatment is applied above
1047
- # (not below) the threshold
1048
- self .x_discon = pd .DataFrame (
1044
+ # evaluate gradient change around kink point
1045
+ mu_kink_left , mu_kink , mu_kink_right = self ._probe_kink_point ()
1046
+ self .gradient_change = self ._eval_gradient_change (
1047
+ mu_kink_left , mu_kink , mu_kink_right , epsilon
1048
+ )
1049
+
1050
+ @staticmethod
1051
+ def _eval_gradient_change (mu_kink_left , mu_kink , mu_kink_right , epsilon ):
1052
+ """Evaluate the gradient change at the kink point.
1053
+ It works by evaluating the model below the kink point, at the kink point,
1054
+ and above the kink point.
1055
+ This is a static method for ease of testing.
1056
+ """
1057
+ gradient_left = (mu_kink - mu_kink_left ) / epsilon
1058
+ gradient_right = (mu_kink_right - mu_kink ) / epsilon
1059
+ gradient_change = gradient_right - gradient_left
1060
+ return gradient_change
1061
+
1062
+ def _probe_kink_point (self ):
1063
+ # Create a dataframe to evaluate predicted outcome at the kink point and either
1064
+ # side
1065
+ x_predict = pd .DataFrame (
1049
1066
{
1050
1067
self .running_variable_name : np .array (
1051
1068
[
@@ -1057,18 +1074,13 @@ def __init__(
1057
1074
"treated" : np .array ([0 , 1 , 1 ]),
1058
1075
}
1059
1076
)
1060
- (new_x ,) = build_design_matrices ([self ._x_design_info ], self .x_discon )
1061
- self .pred_discon = self .model .predict (X = np .asarray (new_x ))
1062
-
1063
- self .gradient_left = (
1064
- self .pred_discon ["posterior_predictive" ].sel (obs_ind = 1 )["mu" ]
1065
- - self .pred_discon ["posterior_predictive" ].sel (obs_ind = 0 )["mu" ]
1066
- ) / self .epsilon
1067
- self .gradient_right = (
1068
- self .pred_discon ["posterior_predictive" ].sel (obs_ind = 2 )["mu" ]
1069
- - self .pred_discon ["posterior_predictive" ].sel (obs_ind = 1 )["mu" ]
1070
- ) / self .epsilon
1071
- self .gradient_change = self .gradient_right - self .gradient_left
1077
+ (new_x ,) = build_design_matrices ([self ._x_design_info ], x_predict )
1078
+ predicted = self .model .predict (X = np .asarray (new_x ))
1079
+ # extract predicted mu values
1080
+ mu_kink_left = predicted ["posterior_predictive" ].sel (obs_ind = 0 )["mu" ]
1081
+ mu_kink = predicted ["posterior_predictive" ].sel (obs_ind = 1 )["mu" ]
1082
+ mu_kink_right = predicted ["posterior_predictive" ].sel (obs_ind = 2 )["mu" ]
1083
+ return mu_kink_left , mu_kink , mu_kink_right
1072
1084
1073
1085
def _input_validation (self ):
1074
1086
"""Validate the input data and model formula for correctness"""
0 commit comments