在之前讲解distribution分布一节中, 主要围绕的是单(双)样本间各自样本的形态, 或者是两个样本间的形态差异. 还未涉及到分析多个样本间的依赖关系. 后者需要借助于更复杂的工具来实现, 比如用线性函数来表达这一关系的线性回归模型, seaborn专门为此制定了强大的lmplot可以解决大多数本原线性模型.
这一节将会对定量数据, 线性模型做一个详尽的讲解.首先是 lmplot (plot for linear model).
Visualizing Multiple regression with lmplot()
lmplot, 首先要明确的是:它的输入数据必须是一个Pandas的’DataFrame Like’ 对象, 然后从这个DataFrame中挑选一些参数进入绘图充当不同的身份.观察下面几行代码, 你就能一目了然了:
# 有的时候, 受测试环境制约, 我有可能会把数据从本地导入; 数据集文件同样是来自于seaborn-data tips = pd.read_csv('c:/tips.csv', index_col=False) # tips = sns.load_dataset('tips') # 网络环境正常的话也可以这样直接导入 # 直接作图, 使用我喜欢的xkcd style with plt.xkcd(): sns.color_palette('husl', 8) sns.set_context('paper') sns.lmplot(x='total_bill', y='tip', data=tips, ci=65, color='indianred')
参数解释,
在上面的lmplot()中, x, y, data(前三个不可省略)分别表示回归的自变量, 回归变量和数据源;
ci用于描述置信区域(confidential interval)的大小, 往往是65, 97这样的标准差的整数倍取值; color是颜色控制.
有的人可能会看到"线点分离"的效果, 是的, 我在一开始也是抱着这样的想法 — 希望用更突出的颜色来强调回归直线的位置, 而不是将它与scatter群混在一起.
控制散点图和直线的参数分别是 scatter_kws, line_kws先观察下面的代码:
sns.lmplot("total_bill", "tip", tips, scatter_kws={"marker": ".", "color": "slategray"}, line_kws={"linewidth": 1, "color": "indianred"}); #{scatter, line}_kws : dictionaries, optional #Additional keyword arguments passed to scatter() and plot() for drawing the components of the plot.
解释: 实际上 scatter_kws
与 line_kws
两个参数是来自于 regplot, 因为lmplot是继承于regplot 因此顺利得到这对参数.
Plotting with discrete predictor variables
x, y均是连续型取值应该是简单情形, 联想到最小二乘, 联想到一个凸优化问题, …, 下面来起讨论x, y二者之一取离散值(descrete int).因为若x, y均是离散水平就是涉及到的离散型卡方检验(Chi-Square Test)/列联表(cross table)之类的范畴了, 非本节讨论范围.
还是利用小费数据集, 这次把total_bill 账单金额替换为size 权当是用餐人数好了.
sns.lmplot(‘size’, ‘tip’, tips)
可以看到, 在代码的结构组成, 与上面的形式看不出任何差异. 不过输出的形式却有很大不同, 而且其难点在于对数据的解读. 这种图形结合到现实的话稍有难度. 现实意义: 人越多小费越多(当然前面的总账单金额和小费的正向关系也是直觉的产物), 但不同人数的权重是否有考虑;是否直线被极端group产生了杠杆导致有偏严重? 这类信息还是很难在图中读取出来.
plt.figure() # 为了进一步查看各size水平下tip的分布, 可能还需要展示更多 sns.lmplot('size', 'tip', tips, x_estimator= np.mean) # 如果说回归直线基本是在各组mean值上下微小浮动, 说明直线并没有受到部分size水平的影响. # 关于 x_estimator 我想不难理解 - > 对拥有相同x水平的y值进行映射
我想对于 y-x 模型中 x连续与x离散的差异性还有很多没有提到的. 这里只是学生的粗浅直觉.
jitter是个很有意思的参数, 特别是处理靶数据的overlapping过于严重的情况时, 通过增加一定程度的噪声(noise)实现数据的区隔化, 这样原始数据是若干 点簇 变成一系列密集邻近的点群. 另外, 有的人会经常将 rug
与 jitter
结合使用. 这依人吧.
对于横轴取离散水平的时候, 用x_jitter可以让数据点发生水平的扰动.但扰动的幅度不宜过大, 比如看官可以尝试一下超过0.5会发生什么.
sns.lmplot(‘size’, ‘tip’, tips, x_jitter=.15)
注. jitter 与 estimator参数是冲突的. 不应共同使用.
将一个连续型数据映射为有限个(1=2量级的缩小水平)的方法有很多, 对于一维-一维的常用思路是通过一个breaks列表中的各个cut points实现对原始数据的有限切分, 即让原始数据落在目标区间群中唯一的区间中.然后用区间位置作为新的变量. 因为多数情况下是数值型, 所以组标识是离散整数表示, 这样起到离散化,同时保留了Order的知识.
说了这么多, 其实就是想讲lmplot中的这个参数 x_bins
bins = [10, 20, 30, 40] fig = plt.figure(figsize=(16, 10)) sns.lmplot('total_bill', 'tip', tips, x_bins = bins) plt.xlabel('bins-list')
若 x_bins 换成一个整数? 请你自己试试吧
我明白的另一个事实就是, 现实模型是远比 y~x要复杂的.不过学习还是一点一点深入的过程.
数据的Facet Plot要借助于第三变量, 起到切片/切面的效果.
hue是个很重要的参数, 不只出现在lmplot.还是用之前的参数, 以及在本节第一行正式代码的结尾参加这个参数, 记住:要指定一个categorical variable!
hue通过指定一个分组变量, 将原来的y~x关系划分成若干个分组:
y1~x1 | y2~x2 | …
然后再用不同的color将数据统一展示到一张图中.
with plt.xkcd(): sns.lmplot('total_bill', 'tip', data=tips, hue='day') plt.xlabel('hue = day')
类似的, 你还可以绘制tips数据集中以其它分组变量的图形, 如smoker.
通过比较不同分组的斜率, 还是能得到一些有意思的结论, 比如相同账单金额的水平, 不抽烟的顾客可能给付更高的小费.
对于很多情况下的categorical-label类的值, seaborn多能提供一些xxx_order的参数来控制展示的顺序(从左-右, 上-下 诸如此类)hue_order但是用来 控制hue组类别的排序的. 比如tips中day参数拥有超过2个水平时, 排序问题就应该注意了. (在我看来, 水平为2的情况 不須指定排序)
# hue_order g = sns.lmplot("total_bill", "tip", tips, hue="day", palette="Set2", hue_order=["Thur", "Fri", "Sat", "Sun"]) g.set_axis_labels("Total bill (US Dollars)", "Tip"); g.set(xticks=[10, 30, 50], ylim=(0, 10), yticks=[0, 2.5, 5, 7.5, 10]);
为什么要指定顺序呢? 这是因为分组的属性是一周时间内的日期 == 一个明显的order/interval变量, 所以人工指定新的顺序, 不然系统只能用字典排列了.
这里还引用了另一种Handler 句柄化的操作方式.
即sns生成的绘图对象, 然后调用对象的set类函数, 实现对g的局部配置. (上面的set_axis_labels, set(xticks, ylim, yticks))
plt.xkcd() sns.lmplot("total_bill", "tip", tips, hue="smoker", markers=["x", "o"]);
如果要将不同水平单独画出来了就可用这个参数, col表示column.
sns.lmplot(‘total_bill’, ‘tip’, data=tips, col=’smoker’)
当写完hue col的展示之后, 我不禁产生这样的疑问.于是我也分为两种情况, 第一种比较容易想到, 就是hue = col的取值.
sns.lmplot('total_bill', 'tip', data=tips, col='smoker', hue='smoker')
那如果col指定day, 而hue指定smoker呢?我还是希望自己去试试吧!
在研究生期间, 我接触过非线性模型又再次分为本原线性和本原非线性模型, 即通过对x,y进行transform从而得到一个线性模型. 如果是这样的模型也是在广义线性模型的范畴内的.
我也是初识seaborn, 通过文档介绍也发现它也是支持一些简单的非线性模型的.
实际上一个lmplot在一开始的图中能看出就是由简单的scatter图和一条简单的线组成(ci置信区域不妨就当作直线的衍生产物). 因此. 能不能只显示一种?
非线性关系里比较简单的方式是过渡至多项式关系, 一般用2-3次的关系来查看是否比一次线性更好的拟合数据.
sns.set_style('dark') sns.set_context('talk') sns.lmplot('size', 'total_bill', tips, order=2) plt.title('# poly order = 2') plt.figure() sns.lmplot('size', 'total_bill', tips, order=3) plt.title('# poly order = 3')
LOESS and LOWESS (locally weighted scatterplot smoothing)
对于Lowess不熟悉的人, 可能要事先作一些homework了. 简单的理解是对简单最小二乘作了复杂化.通过分类, 聚集, 计算变换, 加权等方式改变了最小二乘的原始最优问题, 也许看结果会像是指数平滑的结果, 但其机理还要比k-neighbors平滑稍稍麻烦一些.
如果有兴趣, 可以学习一下 wiki, 或者曾记得stanford前老师Ng老师课中有提到.
sns.set_style('dark') sns.lmplot('total_bill', 'tip', tips, lowess=True, line_kws={'color': '.2'}) plt.title('figure with lowerss=True') plt.figure() sns.set_style('white') sns.lmplot('total_bill', 'tip', tips, lowess=False, line_kws={'color': '.4'}) plt.title('figure with lowerss=False')
L回归是描述y[0,1] ~ x(连续)的问题注: (y多水平的情况就先… 不考虑了)
首先, 由于原数据tips中没有符合logistics建模条件y变量, 我们先人工计算一个, 计算规则是小费占比是否大于10%.
# make a target Y as logistic predit variable tips['big_tip'] = (tips['tip'] / tips['total_bill'] ) > .1 print tips.head() sns.lmplot('total_bill' , 'big_tip',y_jitter=.15, data=tips)
依照着之前设置xjitter的方式, 这里设置了yjitter.
# logistic model sns.set_style('whitegrid') sns.lmplot("total_bill", "big_tip", tips, y_jitter=.05, logistic=True);
sns.lmplot("total_bill", "tip", tips, hue="time", palette="Set1", fit_reg=False);
为了减少Outliers对模型整体的影响(outliers的占比应该是很少的, 而不是和其它大众群体一样权重). 如有兴趣搜索bootstrap.
sns.lmplot('total_bill', 'tip', data=tips, robust=True, n_boot=500)
others
import seaborn as sns iris = sns.load_dataset("iris") sns.regplot("sepal_length", "sepal_width", data=iris, x_partial=iris[["petal_length", "petal_width"]])
- truncate
之前曾经提过lmplot是regplot(regression plot)的上级函数, 很多参数是继承自regplot的.
用下面一个简单例子来演示.
f, (ax1, ax2) = plt.subplots(1, 2, sharey=True) sns.regplot('total_bill', 'tip', data=tips, ax=ax1) sns.boxplot(tips['tip'], tips['size'], color='Blues_r', ax=ax2).set_ylabel("") f.tight_layout()
不同类型的图形,如何组合, 挑选哪样的图形类型, 这其中这也是数据作图的哲学. 我想还得要有大量的经验积累.
残差图是用来检视一个回归模型优劣的一个快速, 直观的工具.一个回归模型的本质就是为了"提纯", 因为回归模型其前提是我们假定
Y = F(X) + err,
所以回归曲线如果已经描述出Y = F(x)
那么剩下的err 的分布就应是如同white noise一样的分布(随机正态分布, 方差为给定值)
当然这一系列的一厢情愿只是美好的理论层幻想.
为了演示residplot, 需要自建一些测试数据.
说明, multivariate_normal 这是生成多元正态分布的numpy-random 不常用函数(因为生活中更多的还是一维啦). 其中第一位置是指定多元各元的期望均值; 第位置是一个n*n 的协方差矩阵, 然后最后一个位置是sclar型的, 描述多元样本的容量.
# sample data x , y =np.random.multivariate_normal([1, 5], [(2,-.8), (-.8, 2)], 80).T ax = sns.regplot(x, y , color='slategray') ax.set(xlabel='xxx', ylabel= 'yyy')
能看到x y 之间有较显著的线性关系. 再用残差图来识别此模型.
sns.residplot(x, y , color=’indianred’)
基本没问题, 残差线分布 y = 0 边缘, 几乎重合.
当然了, 残差图的内部也是要考虑x y 之间的关系类型的, 用线性残差图不能很解释二次关系.
y = x + 1.5 * x ** 2 + np.random.randn(len(x)) sns.residplot(x, y, color='indianred') plt.figure() sns.residplot(x, y , color='slategray', lowess=True)
在上一节中我曾经提过用jointplot画x, y 两个连续值的分布, 以及用hex(正六边形)的集中色块来突显出x*y 笛卡尔空间中密集的hot area.这一节再次出现jointplot, 也是为了说明这个函数的强大之处.
首先可以先想象一下这段代码的显示结果.
sns.jointplot("total_bill", "tip", tips);
还记得kind参数吗? 如果不指定的话 kind默认是point, 一个点表示一个观测. 上一次用的形式为kind=’hex’ , 现在我们来查看另一个有意思的参数.
为了强调这一部分, 我还找了几种有意思的RGB颜色, 不过我的FF水平比较差, 请见谅.
# 实际上Jointplots 可以画出reg直线来. 多提一句,jointplot调用了JointGrid Layout, 未来若有机会我还打算深入学习关于 SpecLayout等几种 Subplot的高级扩展方式. sns.jointplot('total_bill', 'tip', data=tips,kind='reg', color='#ddddff')
这是reg回归模块, 同样的 jointplot还支持resid残差模块.
# 类似的, 能把Reg 换成Resid sns.jointplot('total_bill', 'tip', data=tips, kind='resid', color='#774400')
插入, 冷知识 – 德比郡足球俱乐部. 成立于1884年. 2002年开始凋零, 目前在哪徘徊还不清楚.
这个函数我之前没见过, 现在暂时没有完全掌握. 这里不提.
查看数据集中两两相关的程度, 在R中是用correlation matrix plot来实现的.seaborn中使用corrplot.
titanic = sns.load_dataset('titanic')#.dropna() sns.corrplot(titanic)
titanic = sns.load_dataset('titanic').dropna() sns.corrplot(titanic)
比较结果的差异.
基本上corrplot的图形组成分为几部分: 相关系数矩阵被cmap映射后的色块矩阵.主对角线是默认的变量名称. 如果想要隐藏一部分, 要进行相关的参数调整.
# cmap 控制颜色映射帽子 cbar 设置是否显示右边小条 f, ax = plt.subplots(figsize=(10, 10)) cmap = sns.blend_palette(["#00008B", "#6A5ACD", "#F0F8FF", "#FFE6F8", "#C71585", "#8B0000"], as_cmap=True) d = np.random.standard_t(20, (100, 30)) sns.corrplot(d, annot=False, diag_names=False, cmap=cmap) ax.grid(False);
了解更多的corrplot可以参见参数说明 corrplot
corrplot在刚一开始对输入变量的colinearity(共线性)的识别还是很有帮助的,
例如下图:
在回归模型中, 回归系数是模型结果, 模型评估的重要参考. 对于每个进入到回归模型的 x_i.coefplot 会使用如同R中回归模型中的模型表达式来指定预测变量和自变量, 然后输出各自的系数(以及每个系数估计的置信区间).
sns.coefplot('tip ~ scale(total_bill) + size + time + sex + smoker', tips)
coefplot的默认参数列表:
seaborn.coefplot(formula, data, groupby=None, intercept=False, ci=95, palette='husl')
intercept=对于截距项并不是在公式中指定, 默认值False,即回归曲线经过原点, 若要增加常数效应,要用intercept=True来指派.
palette=
色板的设置. 默认Husl, 可用其它Set代替.
sns.coefplot("score ~ center(solutions) * attention", attention, intercept=True, palette="Set1");