Seaborn中文教程(四):线性关系的展示

许多数据集含有多个定量变量(数值型变量),而我们分析的目的往往是将他们关联起来。我们曾讨论过通过两个变量的联合分布来实现这一点。然而,使用统计模型来为两组带有噪声数据的观测值评估出一个简单的关系可以是非常有用的。这一章节我们讨论的函数将会在线性回归的框架下实现这种预测。

seaborn中的回归图主要是为了在EDA(探索数据分析)阶段为发掘数据中存在的规律提供一些视觉指引,也就是说,seaborn本身并非是一个用于统计分析的库。想要得到关于回归模型拟合效果的一些量化指标,你需要使用statsmodels库。seaborn的终极目标就是让我们通过可视化快速、轻易地探索数据,毕竟对于探索数据来说,可视化的重要性不比得到一个统计表格低。

import numpy as np
import seaborn as sns
import matplotlib.pyplot as plt
sns.set(color_codes=True)
tips = sns.load_dataset("tips")

一、绘制线性回归模型

seaborn主要通过两个函数来展示通过回归得到的线性关系,regplot()lmplot()。它们紧密相关,而且共享大多数的核心功能。但是弄清楚他们的区别非常重要,这样我们就可以在针对特定工作时快速判断哪个工具更适合。

在最基本的调用过程中,他们都会画出关于x、y两个变量的散点图,同时用数据拟合一个y ~ x的模型出来,并将对应的直线和95%的置信区间绘制出来:

sns.regplot(x="total_bill", y="tip", data=tips);
image
sns.lmplot(x="total_bill", y="tip", data=tips);
image

我们注意到除了图片的形状略有差异,其他地方都是一致的。需要知道的是,他们之间最主要的区别在于regplot()中的x, y参数接受多种数据类型,包括numpy数组、pandas序列(Series),或者将pandas DataFrame传递给data参数。作为对比,lmplot()data参数是不能为空的,同时xy参数必须以字符串形式指定。这种数据格式(这里是指regplot()支持而lmplot()不支持的类似一维数组的数据格式)被称为“long-form data”或“tidy data”。除了这一输入格式的灵活性以外,regplot()仅提供了lmplot()特性的一部分,所以我们用后者来演示它们的使用。

seaborn支持其中一个变量属于离散变量的情况,不过这种数据集产生的散点图效果往往一般:

sns.lmplot(x="size", y="tip", data=tips);
image

增加一些随机的偏移量会让这些分布看起来更清晰,需要注意的是这些偏移量仅仅会影响散点图的效果,不会对拟合的回归线产生干扰:

sns.lmplot(x="size", y="tip", data=tips, x_jitter=.05);
image

另一个选择是将每个离散桶内的观测值隐藏起来,用代表集中趋势的统计量以及置信区间作为替代:

sns.lmplot(x="size", y="tip", data=tips, x_estimator=np.mean);
image

二、拟合不同的模型

简单线性回归的模型非常容易拟合,然而它并不适用于所有数据集。Anscombe's quartet数据集展示了一些例子,在这些例子中简单线性回归提供了变量间关系一致的估计,但是却与我们视觉上的直观判断存在一些差异。比如,在第一个例子中,线性回归是一个很不错的模型:

anscombe = sns.load_dataset("anscombe")
sns.lmplot(x="x", y="y", data=anscombe.query("dataset == 'I'"),
           ci=None, scatter_kws={"s": 80});
image

第二个数据集中有着同样的线性关系,但是我们瞬间就能判断线性回归并不是一个最佳的模型:

sns.lmplot(x="x", y="y", data=anscombe.query("dataset == 'II'"),
           ci=None, scatter_kws={"s": 80});
image

在展示这种更高阶的关系时,lmplot()regplot()可以通过拟合多项式回归模型来应对数据集中的一些简单的非线性趋势:

sns.lmplot(x="x", y="y", data=anscombe.query("dataset == 'II'"),
           order=2, ci=None, scatter_kws={"s": 80});
image

另一个问题是由异常观测点导致的,这些观测点明显偏离了我们想要得到的主要趋势关系:

sns.lmplot(x="x", y="y", data=anscombe.query("dataset == 'III'"),
           ci=None, scatter_kws={"s": 80});
image

在异常观测值存在时,我们可以拟合一个鲁棒回归(稳定回归),它使用了不同的损失函数,对较大的残差做了降权:

sns.lmplot(x="x", y="y", data=anscombe.query("dataset == 'III'"),
           robust=True, ci=None, scatter_kws={"s": 80});
image

当因变量y是二元变量时,简单线性回归会提供一个不太有说服力的预测:

tips["big_tip"] = (tips.tip / tips.total_bill) > .15
sns.lmplot(x="total_bill", y="big_tip", data=tips,
           y_jitter=.03);
image

这种情况下的解决方案是拟合一个逻辑回归模型,此时回归线的含义是在给定的xy = 1的概率:

sns.lmplot(x="total_bill", y="big_tip", data=tips,
           logistic=True, y_jitter=.03);
image

要注意的是,相对于简单线性回归,逻辑回归估算具有相当高的计算复杂度(稳健回归也是)。考虑到围绕着回归线的置信区间是通过自主抽样过程计算的(也会花费大量时间),在拟合逻辑回归等模型时最好把置信区间的计算给关掉(ci=None)。

一个完全不同的方法是通过局部加权回归散点平滑法(LOWESS)拟合一个非参数回归模型。这种方法有最少的假设前提,不过它也会耗费大量计算资源,因此置信区间并不会被计算:

sns.lmplot(x="total_bill", y="tip", data=tips, lowess=True);
image

residplot()函数可以检查一个简单的回归模型对于某个数据集是否合适。它先拟合一个简单线性回归模型并移除它,然后将每个观测点与预测值的残差画出来。理想情况下,这些残差应该随机地分布在x轴上下方:

sns.residplot(x="x", y="y", data=anscombe.query("dataset == 'I'"),
              scatter_kws={"s": 80});
image

如果残差的分布具有某种规律,那说明简单线性回归或许并不是一个好的选择:

sns.residplot(x="x", y="y", data=anscombe.query("dataset == 'II'"),
              scatter_kws={"s": 80});
image

三、考虑其他变量的影响

上边我们展示了很多探索一对变量之间关系的可视化方式。然而在很多情况下,“两个变量之间的关系如何受到第三个变量的影响”是一个更加有趣的问题。在这里regplot()lmplot()的差异就出现了:regplot()只能展示简单的关系,而lmplot()则融合了regplot()FacetGrid。因此,lmplot()可以轻易地探索最多三个额外变量带来的交互作用。

分离一个关系最好的方式就是将不通水平的数据绘制在同一个坐标轴体系内,并且用颜色来区分他们:

sns.lmplot(x="total_bill", y="tip", hue="smoker", data=tips);
image

除了颜色,我们还可以通过标记样式来区分不同的水平(分类),这样就可以更好的应对黑白色调或色盲读者。我们可以控制调色板:

sns.lmplot(x="total_bill", y="tip", hue="smoker", data=tips,
           markers=["o", "x"], palette="Set1");
image

我们可以通过更多的子图(行、列)来引入新的变量:

sns.lmplot(x="total_bill", y="tip", hue="smoker", col="time", data=tips);
image
sns.lmplot(x="total_bill", y="tip", hue="smoker",
           col="time", row="sex", data=tips);
image

四、控制图形大小和形状

前边我们提到regplot()lmplot()所创建的图形在默认大小和形状上不一致。这是因为regplot()是一个坐标轴级别的绘图函数,这意味着我们可以在一张图中生成多个子图,随心所欲地控制它们的大小和位置。如果我们没有指定坐标轴参数(ax)的haul,它会默认使用当前活跃的坐标轴,这就是为什么它创建的图形有着和其他matplotlib函数绘制的图形一样的默认大小和形状。想要控制它们的话,我们需要自己创建一个图形对象:

f, ax = plt.subplots(figsize=(5, 6))
sns.regplot(x="total_bill", y="tip", data=tips, ax=ax);
image

不同的是,lmplot()绘制的图形是通过FacetGrid接口中的heightaspect(宽高比)参数控制大小形状的,这些指定的大小和比例是针对每张子图而非整张图的:

sns.lmplot(x="total_bill", y="tip", col="day", 
           data=tips, col_wrap=2, height=3);
image
sns.lmplot(x="total_bill", y="tip", col="day", 
           data=tips, aspect=.5);
image

五、在其他背景中添加回归图

有一些seaborn函数会在其他更大、更复杂的图形背景中使用regplot(),第一个就是我们在数据分布教程中提到的jointplot()函数。除了我们之前讨论过的其他样式,jointplot()可以通过kind="reg"来调用regplot()绘制线性关系:

sns.jointplot(x="total_bill", y="tip", data=tips, kind="reg");
image

pairplot()传入kind="reg"参数则会融合regplot()PairGrid来展示变量间的线性关系。注意这里和lmplot()的区别,lmplot()绘制的行(或列)是将一个变量的多个水平(分类、取值)展开,而在这里,PairGrid则是绘制了不同变量之间的线性关系:

sns.pairplot(tips, x_vars=["total_bill", "size"], y_vars=["tip"],
             height=5, aspect=.8, kind="reg");
image

lmplot()类似,但是与jointplot()不同,额外的分类变量是使用hue参数传递给pairplot()来处理的:

sns.pairplot(tips, x_vars=["total_bill", "size"], y_vars=["tip"],
             hue="smoker", height=5, aspect=.8, kind="reg");
image
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,219评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,363评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,933评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,020评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,400评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,640评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,896评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,597评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,327评论 1 244
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,581评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,072评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,399评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,054评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,849评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,672评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,585评论 2 270

推荐阅读更多精彩内容