В ответе, который вы связали, критическим шагом является применение модели ко всей сетке сетки путем предоставления «экзогенных» данных. В этом случае вы можете легко это сделать, создав новый фрейм данных, содержащий развернутую сетку, и передав его как exog
в statsmodels.regression.linear_model.OLS.predict
. Демонстрация этого на вашем примере:
import numpy as np
import seaborn as sns
import statsmodels.formula.api as smf
import matplotlib.pyplot as plt
from mpl_toolkits import mplot3d
df = sns.load_dataset('mpg')
df.dropna(inplace=True)
model = smf.ols(formula='mpg ~ horsepower + acceleration', data=df)
results = model.fit()
x, y = model.exog_names[1:]
x_range = np.arange(df[x].min(), df[x].max())
y_range = np.arange(df[y].min(), df[y].max())
X, Y = np.meshgrid(x_range, y_range)
exog = pd.DataFrame({x: X.ravel(), y: Y.ravel()})
Z = results.predict(exog = exog).values.reshape(X.shape)
fig = plt.figure(figsize=plt.figaspect(1)*2)
ax = plt.axes(projection='3d')
ax.scatter(df[x].values, df[y].values, results.fittedvalues.values,
marker='.', label="Fits")
cond = df[model.endog_names].values > results.fittedvalues.values
ax.scatter(df[x][cond].values, df[y][cond].values, df[model.endog_names]
[cond].values, label="Raw")
ax.plot_surface(X, Y, Z, rstride=1, cstride=1, alpha = 0.4)
ax.scatter(df[x][cond == False].values, df[y][cond == False].values,
df[model.endog_names][cond == False].values)
ax.legend()
plt.show()
Что даст вам
Я включил отдельные точки соответствия в график рассеяния в дополнение к точкам данных, чтобы указать, что этот подход правильно генерирует соответствующую поверхность. Я также отфильтровал данные в две группы: те, которые должны быть нанесены перед поверхности, и те, которые должны быть нанесены позади поверхности. Это сделано для того, чтобы совместить слои matplotlib с художниками в 3D-рендеринге. Геометрия просмотра была изменена по умолчанию в попытке максимизировать четкость 3D-свойств.
Редактировать
Добавление проекции поверхности регрессии на одну из плоскостей осей довольно тривиально - вы просто наносите на график данные с одним измерением, установленным на предел оси, то есть
ax.plot_surface(X, Y, np.full_like(X, ax.get_zlim()[0]), alpha = 0.2)
, который затем дает вам