I am using the statsmodels python package to perform multivariate linear regression. I want to produce 80% prediction interval bands as part of my result.
The statsmodels package can produce prediction intervals for a given alpha and new predictor(s). Fortunately my residuals are normally distributed so the conventional prediction interval for normally distributed residuals is valid. My understanding is that statsmodels uses the conventional prediction interval calculation:https://online.stat.psu.edu/stat501/lesson/3/3.3
I have attached a plot of a sample of my actual target variable / predicted equivalent. The shaded blue region represents my 80% prediction interval (I have bounded it to zero, because negative values are not possible). The model was trained on data of exactly the same shape i.e., Null, rising to some peak, and then returning to 0 again.
My question is why doesn't the prediction interval vary as much as I'd expect it to along the x-axis? I'd expect it to drop to zero in the first and final third of the x-axis, given that this happens for all of the sample data I used to train the model.
It is worth mentioning that the prediction interval I have is not constant - it is dependent on the predictor values.
I know I can use bootstrapping to generate a more accurate prediction interval, but I'm curious why this conventional statistical PI isn't working for my use case.
EDIT: I cannot post my training data, but here is an example code excerpt to show the method I was using (ordinary least-squares):
from statsmodels.regression.linear_model import OLS, OLSResults
"""
X_train is a 5-feature (5 column) training set
y_train is a single-column of target variables
X_test is a 5-feature testing set
y_test is a single-column of un-seen target variables
prediction_summary_frame is the dataframe of predictions produced by statsmodels. It
has a row of predictions per sample in X_new, with columns:
mean
mean_se
obs_ci_lower
obs_ci_upper
mean_ci_lower
mean_ci_upper
Where mean_ci_upper and mean_ci_lower values represent the confidence interval, and
obs_ci_lower and obs_ci_upper represent the prediction intervals.
"""
model = OLS(X_train, y_train)
ols_results = OLS.fit()
prediction_summary_frame = ols_results.get_prediction(X_test).summary_frame(
alpha=0.1
)
ax.plot(
X_test.index,
prediction_summary_frame["mean"],
label="predicted",
)
ax.fill_between(
X_test.index,
prediction_summary_frame["obs_ci_upper"],
prediction_summary_frame["obs_ci_lower"],
alpha=0.4,
)
ax.plot(
X_test.index,
y_test,
label="actual",
)
plt.show()
```