# 常规导入 %matplotlib inline import numpy as np import pandas as pd import seaborn as sns import matplotlib as mpl import matplotlib.pyplot as plt np.random.seed(1428)
今天我开始切入了 Linear models with quantitative data 这一Tutorial, 让seaborn拼图增加了一块大大的领土.
在之前讲解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");
seaborn 目前还处于"初创"阶段, 还有更多复杂的功能等待挖掘. 例如,
Tagged: pydata datavisualization seaborn