Interactions Module
The xpyrment.interactions module contains submodules and components for interactions.
interactions
Multi-factor experimental design and covariate-treatment interaction analysis.
This package provides tools to discover, model, and visualize interactions within experimental studies. Understanding interactions is critical to determine whether multiple treatments conflict or work synergistically, and whether a treatment effect is heterogeneous across user characteristics.
Submodules:
- detector: Orchestrates and dispatches multi-model interaction checks across covariates and factors.
- anova: Performs Factorial Analysis of Variance (ANOVA) and partitions sum of squares.
- regression: Fits interactive regression models and conducts Likelihood Ratio Tests (LRT).
- hstat: Quantifies model-agnostic interaction strengths using Friedman's H-statistic.
- shap: Calculates second-order game-theoretic Shapley interaction values (TreeSHAP).
- plots: Renders symmetric heatmaps and diagnostic visualization charts.
| MODULE | DESCRIPTION |
|---|---|
anova |
Factorial Analysis of Variance (ANOVA) and statistical interaction testing. |
detector |
Interaction dispatcher and multi-model statistical screening. |
hstat |
Model-agnostic interaction measurement using Friedman's H-statistic. |
plots |
Visualizations and plot generation utilities for multi-factor interactions. |
regression |
Interactive regression modeling and Likelihood Ratio Testing (LRT). |
shap |
Game-theoretic feature interaction analysis using SHAP interaction indices. |
| CLASS | DESCRIPTION |
|---|---|
InteractionDetector |
Dispatches multi-factor, covariate-treatment, and non-linear interactions. |
| FUNCTION | DESCRIPTION |
|---|---|
run_factorial_anova |
Computes factorial ANOVA tables with interaction terms for DoE factors. |
check_treatment_covariate_interaction |
Computes a Likelihood Ratio Test (LRT) to check if a covariate significantly interacts with the treatment split. |
calculate_shap_interactions |
Computes SHAP interaction values to decompose multi-factor combinations (computationally expensive). |
compute_friedman_h_statistic |
Computes model-agnostic Friedman's H-statistic representing the degree of interaction between two features. |
plot_interaction_heatmap |
Generates an interaction term heatmap using matplotlib. |
plot_interaction_effects |
Plots interaction effects between a treatment and a covariate on a metric. |
InteractionDetector
Dispatches multi-factor, covariate-treatment, and non-linear interactions.
In complex online and physical experiments, interventions rarely operate in a vacuum. The treatment effect of
one change may depend heavily on the status of other features (multi-factor interaction) or the characteristics
of the experimental unit (covariate-treatment interaction, or Heterogeneous Treatment Effect).
The InteractionDetector screens for these interactions automatically.
Mathematical Interaction Categories
- Multi-Factor Synergy or Interference (Factor-Factor Interaction): Evaluates whether combining Treatment 1 (\(T_1\)) and Treatment 2 (\(T_2\)) yields a response that differs from the sum of their individual effects: $$ Y = \beta_0 + \beta_1 T_1 + \beta_2 T_2 + \beta_3 (T_1 \times T_2) + \varepsilon $$
- If \(\\beta_3 > 0\), the factors are synergistic.
-
If \(\\beta_3 < 0\), the factors interfere with each other (redundancy or collision).
-
Covariate-Treatment Interaction (Heterogeneous Treatment Effects - HTE): Evaluates whether the treatment effect varies systematically across pre-experiment characteristics \(C\) (e.g., country, browser, mobile vs. desktop, or historical spending): $$ Y = \beta_0 + \beta_1 T + \beta_2 C + \beta_3 (T \times C) + \varepsilon $$ A significant \(\\beta_3\) (\(p < 0.05\)) indicates that the treatment effect varies across subpopulations.
-
Response Surface Curvature (Non-linear Interaction): In continuous Design of Experiments (DoE), evaluates quadratic curvatures and continuous interaction slopes: $$ Y = \beta_0 + \beta_1 X_1 + \beta_2 X_2 + \beta_3 X_1^2 + \beta_4 X_2^2 + \beta_5 (X_1 \times X_2) + \varepsilon $$ where \(\\beta_5\) captures the continuous twisting of the response landscape, and \(\\beta_3, \\beta_4\) capture curvature.
Algorithmic Screening Workflow
function detect_all(experiment):
Initialize interaction_results = {}
For each pair of factors (A, B) in design:
Run OLS: Y ~ A * B
Extract interaction p-value.
If p-value < 0.05:
Add to interaction_results["factor_factor"]
For each covariate C in experiment.covariates:
Run OLS: Y ~ Treatment * C
Extract interaction p-value.
If p-value < 0.05:
Add to interaction_results["heterogeneous_treatment_effects"]
Return interaction_results
| ATTRIBUTE | DESCRIPTION |
|---|---|
experiment |
The completed or active experiment container.
TYPE:
|
| PARAMETER | DESCRIPTION |
|---|---|
experiment
|
The experiment container containing datasets, metrics, and factor definitions.
TYPE:
|
| METHOD | DESCRIPTION |
|---|---|
detect_all |
Runs ANOVA and regression checks to identify interaction terms across factors and covariates. |
Source code in src\xpyrment\interactions\detector.py
detect_all
Runs ANOVA and regression checks to identify interaction terms across factors and covariates.
Fits multi-variable linear regression models with product terms and flags statistically significant interactions.
| RETURNS | DESCRIPTION |
|---|---|
dict
|
A dictionary grouping detected interactions, their estimated coefficients, standard errors, and p-values.
TYPE:
|
Source code in src\xpyrment\interactions\detector.py
run_factorial_anova
Computes factorial ANOVA tables with interaction terms for DoE factors.
Factorial ANOVA decomposes the total variability of an experimental outcome into portions attributable to main factor effects, multi-factor interaction effects, and random error. This is crucial for verifying which process factors have a statistically significant impact on the response variable, and whether factors behave synergetically or antagonistically when combined.
Mathematical Formulation
For a two-factor experimental design (Factor \(A\) with \(I\) levels, Factor \(B\) with \(J\) levels, and \(K\) replicates per cell), the response \(Y_{ijk}\) is modeled as: $$ Y_{ijk} = \mu + \alpha_i + \beta_j + (\alpha\beta){ij} + \varepsilon{ijk} $$ where: - \(\\mu\): The grand mean of the response. - \(\\alpha_i\): The main effect of Factor \(A\) at level \(i\) (subject to \(\\sum_{i=1}^I \\alpha_i = 0\)). - \(\\beta_j\): The main effect of Factor \(B\) at level \(j\) (subject to \(\\sum_{j=1}^J \\beta_j = 0\)). - \((\\alpha\\beta)_{ij}\) smokes: The interaction effect between Factor \(A\) at level \(i\) and Factor \(B\) at level \(j\) (subject to \(\\sum_{i=1}^I (\\alpha\\beta)_{ij} = \\sum_{j=1}^J (\\alpha\\beta)_{ij} = 0\)). - \(\\varepsilon_{ijk}\): Independent and identically distributed normal error terms, \(\\varepsilon_{ijk} \\sim \\mathcal{N}(0, \\sigma^2)\).
Decomposition of Sum of Squares (SS): The total sum of squares (\(SS_{\\text{Total}}\)) measures total sample variation: $$ SS_{\text{Total}} = SS_A + SS_B + SS_{AB} + SS_{\text{Error}} $$ where: - \(SS_A = J K \\sum_{i=1}^I (\\bar{Y}_{i\\cdot\\cdot} - \\bar{Y}_{\\cdot\\cdot\\cdot})^2\) (Main effect \(A\)) - \(SS_B = I K \\sum_{j=1}^J (\\bar{Y}_{\\cdot j\\cdot} - \\bar{Y}_{\\cdot\\cdot\\cdot})^2\) (Main effect \(B\)) - \(SS_{AB} = K \\sum_{i=1}^I \\sum_{j=1}^J (\\bar{Y}_{ij\\cdot} - \\bar{Y}_{i\\cdot\\cdot} - \\bar{Y}_{\\cdot j\\cdot} + \\bar{Y}_{\\cdot\\cdot\\cdot})^2\) (Interaction effect \(AB\)) - \(SS_{\\text{Error}} = \\sum_{i=1}^I \\sum_{j=1}^J \\sum_{k=1}^K (Y_{ijk} - \\bar{Y}_{ij\\cdot})^2\) (Residual variation)
F-Test Ratios
Significance of each effect is evaluated by comparing the Mean Square (\(MS = SS / df\)) against the residual Mean Square (\(MS_{\\text{Error}}\)): - For Factor \(A\): $$ F_A = \frac{MS_A}{MS_{\text{Error}}} = \frac{SS_A / (I-1)}{SS_{\text{Error}} / [IJ(K-1)]} \sim F_{I-1, \ IJ(K-1)} $$ - For Interaction \(AB\): $$ F_{AB} = \frac{MS_{AB}}{MS_{\text{Error}}} = \frac{SS_{AB} / [(I-1)(J-1)]}{SS_{\text{Error}} / [IJ(K-1)]} \sim F_{(I-1)(J-1), \ IJ(K-1)} $$ A significant \(F_{AB}\) (\(p < 0.05\)) proves that the effect of Factor \(A\) depends on the level of Factor \(B\). This indicates that interpreting main effects alone is statistically misleading; the interaction must be evaluated.
| PARAMETER | DESCRIPTION |
|---|---|
df
|
The experimental dataset.
TYPE:
|
formula
|
R-style regression formula (e.g.,
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
DataFrame
|
pd.DataFrame: A standard ANOVA table detailing Sum of Squares, degrees of freedom (\(df\)), F-statistics, and p-values for each term. |
Source code in src\xpyrment\interactions\anova.py
check_treatment_covariate_interaction
check_treatment_covariate_interaction(
df: DataFrame,
treatment_col: str,
covariate_col: str,
target_col: str,
) -> float
Computes a Likelihood Ratio Test (LRT) to check if a covariate significantly interacts with the treatment split.
Evaluates whether the treatment effect varies across different values of a pre-period covariate. To determine if the interaction term is statistically necessary (rather than just overfitting the sample), we fit nested regression models and perform a classical Likelihood Ratio Test.
Mathematical Formulation of Nested Models
We define two models representing competing hypotheses: 1. Restricted Null Model (\(M_{\\text{null}}\)) (additive, assuming no interaction): $$ Y_i = \beta_0 + \beta_1 T_i + \beta_2 C_i + \varepsilon_i $$ 2. Unrestricted Alternative Model (\(M_{\\text{alt}}\)) (interactive, assuming interaction): $$ Y_i = \beta_0 + \beta_1 T_i + \beta_2 C_i + \beta_3 (T_i \times C_i) + \varepsilon_i $$ where: - \(Y_i\): The target outcome metric (\(target\\_col\)) for unit \(i\). - \(T_i\): The treatment group indicator (\(treatment\\_col\), e.g., \(0\) or \(1\)). - \(C_i\): The pre-period covariate (\(covariate\\_col\), e.g., device type or baseline revenue). - \(T_i \\times C_i\): The interaction/product term.
The Likelihood Ratio Test (LRT): Let \(\\ln L(M_{\\text{null}})\) and \(\\ln L(M_{\\text{alt}})\) be the maximized log-likelihood values of the nested models. The test statistic \(D\) is computed as: $$ D = 2 \left( \ln L(M_{\text{alt}}) - \ln L(M_{\text{null}}) \right) $$ Under the null hypothesis \(H_0: \\beta_3 = 0\) (no interaction), the test statistic \(D\) asymptotically follows a Chi-square distribution with degrees of freedom equal to the difference in the number of parameters: $$ D \sim \chi^2_{df_{\text{alt}} - df_{\text{null}}} = \chi^2_1 $$ (since we added exactly one interaction parameter, \(\\beta_3\)).
The resulting p-value is calculated as:
$$
p = 1 - F_{\\chi^2_1}(D)
$$
where $F$ is the cumulative distribution function of the Chi-square distribution with 1 degree of freedom.
If $p < 0.05$, the alternative model is selected, confirming that the treatment effect varies across levels of the covariate.
| PARAMETER | DESCRIPTION |
|---|---|
df
|
The experimental dataset.
TYPE:
|
treatment_col
|
Column containing treatment assignments.
TYPE:
|
covariate_col
|
Column containing the target pre-period covariate under evaluation.
TYPE:
|
target_col
|
Column containing the outcome response variable (\(Y\)).
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
float
|
The calculated p-value of the Likelihood Ratio Test. A value \(< 0.05\) indicates a significant interaction.
TYPE:
|
Source code in src\xpyrment\interactions\regression.py
calculate_shap_interactions
Computes SHAP interaction values to decompose multi-factor combinations (computationally expensive).
SHAP (SHapley Additive exPlanations) interaction values (Lundberg et al., 2018) are based on the coalitional game-theoretic Shapley Interaction Index (Grabisch and Roubens, 1999). While standard Shapley values partition a model's prediction additively among individual features, SHAP interaction values separate these contributions into pure main effects and pairwise interaction effects, providing an exact model-agnostic representation of how features cooperate or compete.
Mathematical Formulation and Coalition Deficits
Let \(M\) be the complete set of all features. The SHAP interaction value \(\\Phi_{i,j}\) between feature \(i\) and feature \(j\) (where \(i \\neq j\)) measures the pure interaction effect after accounting for all other subsets of features: $$ \Phi_{i,j} = \sum_{S \subseteq M \setminus \{i, j\}} \frac{|S|! (|M| - |S| - 2)!}{2 (|M| - 1)!} \Delta_{i,j}(S) $$ where the second-order marginal contribution difference \(\\Delta_{i,j}(S)\) is defined as: $$ \Delta_{i,j}(S) = f(S \cup \{i, j\}) - f(S \cup \{i\}) - f(S \cup \{j\}) + f(S) $$ The diagonal elements \(\\Phi_{i,i}\) capture the main effect of feature \(i\) after removing all of its pairwise interactions with other features: $$ \Phi_{i,i} = \phi_i - \sum_{j \neq i} \Phi_{i,j} $$ where \(\\phi_i\) is the standard Shapley value for feature \(i\).
The complete set of main effects and interaction values decomposes the model prediction \(f(x)\) exactly: $$ f(x) = \Phi_0 + \sum_{i=1}^{|M|} \Phi_{i,i} + \sum_{i \neq j} \Phi_{i,j} $$ where \(\\Phi_0 = E[f(x)]\) is the base value (expected prediction of the model across the background training distribution).
Computational Complexity
Evaluating the summation requires computing model predictions across \(2^{|M|}\) feature coalitions, which is NP-hard in the general case. To make this practical, modern libraries utilize TreeSHAP (Lundberg et al., 2020), which calculates exact tree-based Shapley interaction values in \(O(T L D^2)\) time where \(T\) is the number of trees, \(L\) is max leaves, and \(D\) is max depth.
| PARAMETER | DESCRIPTION |
|---|---|
model
|
A trained model (typically a tree ensemble like XGBoost or LightGBM).
TYPE:
|
X_data
|
The background evaluation matrix of covariate features.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
list
|
A nested list or 3D numpy array of shape
TYPE:
|
Source code in src\xpyrment\interactions\shap.py
compute_friedman_h_statistic
Computes model-agnostic Friedman's H-statistic representing the degree of interaction between two features.
Friedman's H-statistic (Friedman and Popescu, 2008) measures the strength of interaction between features by evaluating how much of the model's prediction variation is due to joint, non-additive behavior. Unlike linear regression product terms, the H-statistic is model-agnostic and can capture highly complex, non-linear interactions in machine learning models (e.g., gradient boosted trees, neural networks).
Mathematical Formulation and Partial Dependence
Let \(x_i\) and \(x_j\) be two features. Let \(PD_i(x_i)\) and \(PD_j(x_j)\) be the 1-way Partial Dependence (PD) functions, which represent the average prediction of the model when fixing the respective feature value: $$ PD_i(x_i) = \frac{1}{N} \sum_{k=1}^N f(x_i, \ x_{k, \setminus i}) $$ Let \(PD_{ij}(x_i, \\ x_j)\) be the 2-way joint Partial Dependence function: $$ PD_{ij}(x_i, \ x_j) = \frac{1}{N} \sum_{k=1}^N f(x_i, \ x_j, \ x_{k, \setminus \{i, j\}}) $$ If there is no interaction between \(x_i\) and \(x_j\) (meaning their combined effect on the prediction is perfectly additive), then the joint PD can be decomposed exactly as the sum of their individual PD functions: $$ PD_{ij}(x_i, \ x_j) = PD_i(x_i) + PD_j(x_j) $$ Friedman's \(H^2_{ij}\) statistic measures the normalized squared deviation from this additive null hypothesis over the empirical distribution of the dataset: $$ H^2_{ij} = \frac{\sum_{k=1}^N \left[ PD_{ij}(x_{k,i}, \ x_{k,j}) - PD_i(x_{k,i}) - PD_j(x_{k,j}) \right]^2}{\sum_{k=1}^N \left[ PD_{ij}(x_{k,i}, \ x_{k,j}) \right]^2} $$
Interpretation of the H-Statistic: - \(H^2_{ij} = 0\): No interaction whatsoever. The features affect the response in a perfectly additive manner. - \(H^2_{ij} = 1.0\): The combined prediction depends entirely on their interaction; the individual main effects explain \(0\\%\) of the joint variation. - In practice, a value of \(H_{ij} > 0.10\) (representing the square root of \(H^2_{ij}\)) suggests a substantial, non-negligible interaction effect.
| PARAMETER | DESCRIPTION |
|---|---|
model
|
A trained, black-box machine learning model (must implement
TYPE:
|
X_data
|
The covariate dataset used to evaluate the partial dependencies.
TYPE:
|
feature_i
|
Name of the first target feature.
TYPE:
|
feature_j
|
Name of the second target feature.
TYPE:
|
| RETURNS | DESCRIPTION |
|---|---|
float
|
The computed Friedman's H-statistic (\(H_{ij} \\in [0, 1]\)).
TYPE:
|
Source code in src\xpyrment\interactions\hstat.py
plot_interaction_heatmap
plot_interaction_heatmap(
df_interactions: DataFrame,
annot: Optional[bool] = None,
ax: Optional[Axes] = None,
**kwargs
) -> tuple
Generates an interaction term heatmap using matplotlib.
Visualizes a symmetric matrix of feature/factor interactions. Heatmaps are a highly effective diagnostic chart for screening complex multi-factor studies or high-dimensional covariate sets, allowing the user to instantly recognize clusters of strong synergy or severe interference.
Matrix Structure
Let \(F = \{f_1, f_2, \dots, f_m\}\) be the set of analyzed factors or covariates. The plotting engine constructs
a symmetric \(m \times m\) matrix \(H\):
- Cell \(H_{i,j}\) contains the strength of the interaction between \(f_i\) and \(f_j\). This value can represent
either:
1. The absolute regression interaction coefficient (\(|\beta_{\text{interaction}}|\)).
2. The model-agnostic Friedman's H-statistic (\(H_{ij}\)).
3. The statistical significance transformed index (\(-\log_{10}(p_{\text{value}})\)).
- Cells along the diagonal (\(H_{i,i}\)) are typically zeroed or set to represent the main effect of factor \(f_i\).
- The matrix is rendered using a divergent colormap (such as RdBu or seismic if mapping positive/negative coefficients)
or a sequential colormap (such as Viridis or YlOrRd if mapping absolute H-statistics or significance).
| PARAMETER | DESCRIPTION |
|---|---|
df_interactions
|
A rectangular or pivoted DataFrame representing the interaction strength matrix, with factor names as both index and column headings.
TYPE:
|
annot
|
Whether to annotate the cells with numeric values. If None, annotations are enabled automatically only if the matrix size is small (e.g., <= 20 features).
TYPE:
|
ax
|
Pre-existing axes for the plot. If None, a new figure and axes are created.
TYPE:
|
**kwargs
|
Additional keyword arguments to pass to
DEFAULT:
|
| RETURNS | DESCRIPTION |
|---|---|
tuple
|
A tuple
TYPE:
|
Source code in src\xpyrment\interactions\plots.py
14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | |
plot_interaction_effects
plot_interaction_effects(
data: DataFrame,
treatment_col: str,
metric_col: str,
covariate_col: str,
ax: Optional[Axes] = None,
**kwargs
) -> tuple
Plots interaction effects between a treatment and a covariate on a metric.
Generates a line plot showing the average metric value for different treatment groups across levels of the covariate. This helps visualize if the treatment effect varies depending on the covariate value (heterogeneous treatment effect).
Example
| PARAMETER | DESCRIPTION |
|---|---|
data
|
The experimental data containing treatments, covariates, and metrics.
TYPE:
|
treatment_col
|
The name of the column representing the treatment group.
TYPE:
|
metric_col
|
The name of the column representing the outcome metric.
TYPE:
|
covariate_col
|
The name of the column representing the interacting covariate.
TYPE:
|
ax
|
Pre-existing axes for the plot. If None, a new figure and axes are created.
TYPE:
|
**kwargs
|
Additional keyword arguments to pass to the underlying plotting functions.
DEFAULT:
|
| RETURNS | DESCRIPTION |
|---|---|
tuple
|
A tuple
TYPE:
|
Source code in src\xpyrment\interactions\plots.py
117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 | |