You can have both a random intercept for subjects and a random pre/post slope, as long as you constrain these random effects to be independent.
I will discuss this in the setting where everyone has exactly one pre-treatment and one post-treatment measure (but the logic applies more generally).
If the coding is pre=0 and post=1 for pre_post, then the random slope for pre_post allows you to have "treatment effect heterogeneity" -- the treatment impacts different people in different ways.
The reason that we need the two random effects to be independent is that there are only 3 parameters in the marginal covariance model, since everything is determined by the 2x2 covariance matrix for (pre, post) values. Thus we can only have three variance/covariance parameters in the model (in this case these will be the variances for the subject intercept and pre_post random effects, and the residual variance). One way to achieve this in statsmodels is to use the variance components syntax below (there is another approach that directly constrains the covariance parameter to equal zero).
In the model discussed above, the post measures are more dispersed than the pre measures. In principle you could also have the opposite pattern, where the post measures are more concentrated than the pre measures. To capture this pattern, you could replace pre_post
with 1 - pre_post
and refit, then select whichever of the two models fits the data better.
Mixed Linear Model Regression Results
=========================================================
Model: MixedLM Dependent Variable: dv
No. Observations: 10000 Method: REML
No. Groups: 5000 Scale: 0.1999
Min. group size: 2 Likelihood: -20154.5347
Max. group size: 2 Converged: Yes
Mean group size: 2.0
---------------------------------------------------------
Coef. Std.Err. z P>|z| [0.025 0.975]
---------------------------------------------------------
Intercept -0.001 0.022 -0.022 0.982 -0.044 0.043
pre_post -0.983 0.030 -33.326 0.000 -1.041 -0.926
Group RE 2.301 1.315
v RE 3.955 2.290
=========================================================
Here is the code used to generate the data and fit the model:
import numpy as np
import pandas as pd
import statsmodels.api as sm
n = 5000
pre_post = np.kron(np.ones(n), np.r_[0, 1])
person_id = np.kron(np.arange(n), np.r_[1, 1])
person_effects = 1.5 * np.random.normal(size=n)
het = 2 * np.random.normal(size=n)
dv = 0 # Fixed intercept is zero
dv -= pre_post.copy() # Post fixed effect is -1
dv += person_effects[person_id] # Random person intercept has variance 1.5^2
dv += het[person_id] * pre_post # Treatment effect heterogeneity has variance 4
dv += 0.5*np.random.normal(size=2*n) # Residual variance is 0.25
df = pd.DataFrame({"dv": dv, "pre_post": pre_post, "person_id": person_id})
model = sm.MixedLM.from_formula('dv ~ pre_post', data=df, re_formula='1', vc_formula={"v": "0+pre_post"}, groups='person_id')
result = model.fit(method='cg')