Python 使用Seaborn和Statsmodels在一个图中显示数据和模型预测
Seaborn是一个很好的软件包,可以用漂亮的输出进行高级绘图。然而,我正在努力使用Seaborn覆盖外部拟合模型的数据和模型预测。在本例中,我在Statsmodels中拟合模型,这些模型对于Seaborn来说过于复杂,无法开箱即用,但我认为问题更一般(即,如果我有模型预测,并希望使用Seaborn将它们和数据可视化) 让我们从导入和数据集开始:Python 使用Seaborn和Statsmodels在一个图中显示数据和模型预测,python,matplotlib,statsmodels,seaborn,Python,Matplotlib,Statsmodels,Seaborn,Seaborn是一个很好的软件包,可以用漂亮的输出进行高级绘图。然而,我正在努力使用Seaborn覆盖外部拟合模型的数据和模型预测。在本例中,我在Statsmodels中拟合模型,这些模型对于Seaborn来说过于复杂,无法开箱即用,但我认为问题更一般(即,如果我有模型预测,并希望使用Seaborn将它们和数据可视化) 让我们从导入和数据集开始: import numpy as np import pandas as pd import seaborn as sns import statsmo
import numpy as np
import pandas as pd
import seaborn as sns
import statsmodels.formula.api as smf
import patsy
import itertools
import matplotlib.pyplot as plt
np.random.seed(12345)
# make a data frame with one continuous and two categorical variables:
df = pd.DataFrame({'x1': np.random.normal(size=100),
'x2': np.tile(np.array(['a', 'b']), 50),
'x3': np.repeat(np.array(['c', 'd']), 50)})
# create a design matrix using patsy:
X = patsy.dmatrix('x1 * x2 * x3', df)
# some random beta weights:
betas = np.random.normal(size=X.shape[1])
# create the response variable as the noisy linear combination of predictors:
df['y'] = np.inner(X, betas) + np.random.normal(size=100)
我们在包含所有预测变量及其相互作用的statsmodels中拟合一个模型:
# fit a model with all interactions
fit = smf.ols('y ~ x1 * x2 * x3', df).fit()
print(fit.summary())
因为在这种情况下,我们指定了所有变量的组合,并且我们的模型预测是线性的,所以在包含模型预测的数据框中添加一个新的“预测”列就足够了。然而,这不是很普遍(假设我们的模型是非线性的,所以我们希望我们的曲线图显示平滑的曲线),所以我用所有预测值的组合创建一个新的数据帧,然后生成预测:
# create a new dataframe of predictions, using pandas' expand grid:
def expand_grid(data_dict):
""" A port of R's expand.grid function for use with Pandas dataframes.
from http://pandas.pydata.org/pandas-docs/stable/cookbook.html?highlight=expand%20grid
"""
rows = itertools.product(*data_dict.values())
return pd.DataFrame.from_records(rows, columns=data_dict.keys())
# build a new matrix with expand grid:
preds = expand_grid(
{'x1': np.linspace(df['x1'].min(), df['x1'].max(), 2),
'x2': ['a', 'b'],
'x3': ['c', 'd']})
preds['yhat'] = fit.predict(preds)
preds
数据帧如下所示:
x3 x1 x2 yhat
0 c -2.370232 a -1.555902
1 c -2.370232 b -2.307295
2 c 3.248944 a -1.555902
3 c 3.248944 b -2.307295
4 d -2.370232 a -1.609652
5 d -2.370232 b -2.837075
6 d 3.248944 a -1.609652
7 d 3.248944 b -2.837075
由于Seaborn绘图命令(与R中的ggplot2
命令不同)似乎只接受一个数据帧,因此我们需要将预测合并到原始数据中:
# append to df:
merged = df.append(preds)
现在,我们可以用连续变量x1
作为x轴,绘制模型预测和数据:
# plot using seaborn:
sns.set_style('white')
sns.set_context('talk')
g = sns.FacetGrid(merged, hue='x2', col='x3', size=5)
# use the `map` method to add stuff to the facetgrid axes:
g.map(plt.plot, "x1", "yhat")
g.map(plt.scatter, "x1", "y")
g.add_legend()
g.fig.subplots_adjust(wspace=0.3)
sns.despine(offset=10);
到目前为止还不错。现在假设我们没有测量连续变量x1
,我们只知道其他两个(分类)变量(即,我们有一个2x2阶乘设计)在这种情况下,我们如何根据数据绘制模型预测?
fit = smf.ols('y ~ x2 * x3', df).fit()
print(fit.summary())
preds = expand_grid(
{'x2': ['a', 'b'],
'x3': ['c', 'd']})
preds['yhat'] = fit.predict(preds)
print(preds)
# append to df:
merged = df.append(preds)
我们可以使用sns.pointplot
或类似方法绘制模型预测,如下所示:
# plot using seaborn:
g = sns.FacetGrid(merged, hue='x3', size=4)
g.map(sns.pointplot, 'x2', 'yhat')
g.add_legend();
sns.despine(offset=10);
或者使用sns.factorplot
这样的数据:
g = sns.factorplot('x2', 'y', hue='x3', kind='point', data=merged)
sns.despine(offset=10);
g.savefig('tmp3.png')
但我不知道如何生成类似于第一个图的图(即使用
plt.plot
的模型预测线,使用plt.scatter
的数据点分散)。原因是我试图用作x轴的x2
变量是一个字符串/对象,因此pyplot命令不知道如何处理它们。正如我在评论中提到的,有两种方法可以考虑这样做
首先是定义一个进行拟合的函数,然后绘制并将其传递给FaceGrid.map
:
import pandas as pd
import seaborn as sns
tips = sns.load_dataset("tips")
def plot_good_tip(day, total_bill, **kws):
expected_tip = (total_bill.groupby(day)
.mean()
.apply(lambda x: x * .2)
.reset_index(name="tip"))
sns.pointplot(expected_tip.day, expected_tip.tip,
linestyles=["--"], markers=["D"])
g = sns.FacetGrid(tips, col="sex", size=5)
g.map(sns.pointplot, "day", "tip")
g.map(plot_good_tip, "day", "total_bill")
g.set_axis_labels("day", "tip")
第二个是计算预测值,然后将它们与一个额外的变量合并到数据帧中,该变量标识什么是数据,什么是模型:
tip_predict = (tips.groupby(["day", "sex"])
.total_bill
.mean()
.apply(lambda x: x * .2)
.reset_index(name="tip"))
tip_all = pd.concat(dict(data=tips[["day", "sex", "tip"]], model=tip_predict),
names=["kind"]).reset_index()
sns.factorplot("day", "tip", "kind", data=tip_all, col="sex",
kind="point", linestyles=["-", "--"], markers=["o", "D"])
请注意,我认识到最后一个图中的线与第二个图中的线相同(即,模型预测只是平均值之间的线)。不过,这并不总是正确的,所以我想要一个更一般的方法。还要注意的是,由于一些未知的原因,第二个情节中的图例没有显示“c”和“d”的情况,只有图例标题。我不知道为什么。您可以将任何函数传递到
FaceGrid.map
,只要它需要x
,y
位置参数并在“当前”活动轴上绘图。因此,您应该能够定义一个从类别映射到[0,1,2,…]的函数,并使用它。这有帮助吗?也许另一种策略是重新格式化数据帧,使数据和模型预测位于同一列中,然后使用hue
变量指示点和轨迹是数据还是预测。使用不同的标记和线型会有所帮助。