作者 | FesianXu
导读
在理论工作中,咱们常常会遇到一堆数据,对数据的无效剖析至为要害,而数据的散布就是一种十分重要的数据属性,须要通过适合的可视化伎俩进行剖析。本文参考[1],基于seaborn库介绍一些罕用的数据分布可视化办法。全文8720字,预计浏览工夫22分钟
数据的散布,咱们能够了解为是“数据的形态”。一个“完满”的数据分布,会将数据所有可能的数据点都囊括其中,因而数据的散布表征了不同数据之间的本质区别。然而现实生活的数据不可能对所有可能的数据点都进行遍历(因为通常会有有限个数据点),因而咱们通常都是在某个采样的子集中,尝试对数据本原的散布进行剖析。常见的数据分布可视化办法有以下几种:
1.直方图(Histogram)
2.条件直方图(Conditional Histogram)
3.核密度估计图(Kernel Density Estimation,KDE)
4.累积散布函数图(Empirical Cumulative Distribution Function, ECDF)
5.箱型图(boxplot)
6.提琴图(violin plot)
7.二元直方图(bivariate histogram)
8.联结概率分布曲线(Joint Distribution Plot)
9.边缘概率分布曲线(Marginal Distribution Plot)
如Fig 1.所示,咱们以penguins数据集为例子别离进行介绍
△Fig 1. penguins数据集,一共有344条数据,每条数据有7个维度的属性。
01 单变量直方图
单变量直方图(univariate histogram)是一种单变量的散布可视化办法,将所有数据点进行分桶,而后统计落在某个桶里的数据点的频次,以柱形图的模式将每个桶的频次绘制进去。如Fig 1.1所示,咱们对penguins数据中的flipper_length_mm属性进行直方图绘制。
import seaborn as snsdata = sns.load_dataset("penguins", data_home="./data/seaborn-data")# 能够筛选不同的分桶数量bins,或者每个桶的宽度ret = sns.displot(data, x='flipper_length_mm', bins=20)ret = sns.displot(data, x='flipper_length_mm', bins=50)ret = sns.displot(data, x='flipper_length_mm', binwidth=5)
△Fig 1.1 对penguins数据的flipper_length_mm
属性进行绘制直方图,从左到右,别离是(a)分桶数是20,(b)分桶数50,(c)分桶宽度是5。
咱们发现,选取不同的分桶数量和分桶宽度对于整个散布可视化后果影响很大。分桶越多,散布越粗疏,但也越容易被某些噪声影响咱们剖析整体散布趋势,个别在理论中咱们通常会选取多个分桶数进行尝试。原始的直方图统计的是分桶中的数据频次,不同数据的总数不同因而频次并不可比,通常能够思考进行归一化解决。如Fig 1.2所示,通常有两种类型的归一化:密度归一化,概率归一化。密度归一化指的是所有柱形面积和为1,概率归一化指的是所有柱形的高度和为1。密度归一化的状况下,因为纵坐标的数值会受到横坐标数值尺度的影响,通常是不可比,而概率归一化不须要思考横坐标的数值尺度,因而通常是可比的。
ret = sns.displot(data, x='flipper_length_mm', bins=20, stat='density') # 密度归一模式的归一化ret = sns.displot(data, x='flipper_length_mm', bins=20, stat='probability') # 概率归一模式的归一化
△Fig 1.2 (a) 密度归一模式的归一化; (b)概率归一模式的归一化。
咱们既能够对连续变量进行直方图统计,也能够对离散变量进行直方图统计,如Fig 1.3所示,咱们通过设置shrink参数,能够管制柱形的宽度,使得可视化成果更像是一个离散变量的直方图。
tips = sns.load_dataset('tips')ret = sns.displot(tips, x='day', stat='probability')ret = sns.displot(tips, x='day', stat='probability', shrink=.8)
△Fig 1.3 (a)shrink=1.0的状况,柱形之间“肩靠肩”,没有间隙;(b)通过设置shrink=0.8,能够使得离散变量的柱形之间存在距离,看起来更“离散”一些,与连续变量直方图有所区别。
02 条件直方图
再回到Fig 1.2,咱们能显著地发现这个散布有显著的两个峰,这个flipper_length_mm代表的是企鹅的鳍肢的长度,这个属性会受到其余什么属性的影响呢?在之前的直方图中,咱们绘制的是概率分布\( P(X) \)现在咱们须要绘制条件概率分布\( P(X|Y)\),以考查到底是其余哪些属性影响了企鹅的鳍肢的长度。如Fig 2.1所示,因为笔者感觉品种,性别,和寓居的岛屿可能会影响到企鹅的鳍肢长度,我绘制了\( P(\mathrm{flipper_length_mm | species}) \) ,\( P(\mathrm{flipper_length_mm | sex}) \),\( P(\mathrm{flipper_length_mm | island}) \) 2.1(a)能够发现企鹅的品种确实有所影响(Adelie品种和Gentoo品种的企鹅的鳍肢散布显著不同),而雌性雄性企鹅的鳍肢长度都出现双峰分布,因而并不是导致Fig 1.2中呈现双峰分布的起因,同理,从Fig 2.1(c)中能够发现企鹅寓居的岛屿也是导致呈现双峰的起因。这些条件散布都能够通过设置displot()中的hue参数实现。
ret = sns.displot(data, x="flipper_length_mm", hue='species')ret = sns.displot(data, x="flipper_length_mm", hue='sex')ret = sns.displot(data, x="flipper_length_mm", hue='island')
△Fig 2.1 (a)-(c)是不同的条件散布直方图。
然而居住地会影响企鹅的鳍肢生长发育,这一点比拟奇怪,可能并不是一个间接起因,兴许是不同岛屿寓居的企鹅品种不同导致的?咱们能够绘制 \( P(\mathrm{island|species}) \),如Fig 2.2所示,其中的(a)是个别模式的条件散布柱形图,咱们发现不同条件下的柱形会存在层叠,容易看不清楚,此时能够通过设置multiple="stack"将其设置为stack模式,柱形图之间以重叠模式出现,不会存在层叠。咱们能够发现在Fig 2.1(c)中的Biscoe和Dream岛屿出现的两个峰,来自于这两个岛屿中的两大企鹅种群——Gentoo,Chinstrap+Adelie(这两个品种的散布较为靠近)散布导致。也就是说,鳍肢和企鹅品种的关联才是实质的因果关系。
ret = sns.displot(data, x="island", hue='species')ret = sns.displot(data, x="island", hue='species', multiple="stack", discrete=False
△Fig 2.2 (a)个别模式的条件散布绘制,有时候因为柱形图的层叠,容易看不出分明,此时能够采纳stack模式,如(b)所示,此时不同柱形之间不会有层叠。
咱们在Fig 1.2中说到过直方图的归一化,因为Fig 1.2中是单变量的散布归一化,因而体现到图中只是纵坐标的尺度变动,而整个图的形态是不会有变动的。在条件直方图中,咱们能够将common_norm设置为False,此时进行归一化会将不同条件下的条件散布进行独立的归一化,如Fig 2.3所示,其中(a)和(b)只有纵坐标上的区别,而(c)是将不同条件下的条件散布进行各自的归一化。
ret = sns.displot(data, x="flipper_length_mm", hue='species')ret = sns.displot(data, x="flipper_length_mm", hue='species', stat="probability")ret = sns.displot(data, x="flipper_length_mm", hue='species', stat="probability", common_norm=False)
△Fig 2.3 (a)条件散布原图;(b)对条件散布进行归一化,不思考不同条件下的区别;(c)进行不同条件下的别离归一化。
03 核密度估计曲线
直方图对数据进行分箱后,统计每个箱子中的数据点频次,因而绘制进去的直方图是离散的柱形图,即使数据是连续型数据。有什么办法能够更好地体现数据的间断性质呢?核密度估计(Kernel Density Estimation,KDE)是一种可行的办法,咱们假如每个数据点都是对数据分布的一次随机采样,采样自均值为察看值 \( x_i \)方差为 \( \sigma^2 \)如公式(3-1)所示,咱们对所有数据点的核密度估计曲线进行叠加,失去整个数据分布的核密度估计曲线。咱们的核散布通常能够采纳高斯分布,如公式(3-2)所示。
$$ \hat{f}{\sigma^2}(x) = \dfrac{1}{n}\sum{i=1}^{n} K_{\sigma^2}(x-x_i) $$
(3-1)
$$K_{\sigma^2}(x) = \dfrac{1}{\sqrt{2}{\sigma^2}}exp(-x^2/2{\sigma^2} )$$
(3-2)
如Fig 3.1所示,在给定了6个察看值,绘制出的直方图如Fig 3.1(a)所示,如Fig 3.1(b)所示,其中的红色曲线示意每个样本的高斯核密度估计,叠加起来失去蓝色曲线,为整个数据分布的核密度估计曲线。
△Fig 3.1 (a)直方图;(b)对每个样本进行核密度估计,而后叠加失去数据分布的拟合曲线。
回到咱们的seaborn,咱们通过设置参数kind="kde"进行设置,同时能够通过bw_adjust管制其方差,咱们通常把此处的方差称之为带宽(bandwidth)。如Fig 3.2所示,咱们发现不同的带宽下,核密度估计曲线的形态天差地别,Fig 3.2(b)中,过小的带宽能够看到散布的更多细节,然而也会收到更多数据噪声的影响,呈现过多的毛刺。如Fig 3.2(c)所示,过大的带宽会使得曲线过于平滑,使得一些散布细节被拆穿了,比方其中的双峰分布就被平滑得看不出来了。
sns.displot(data, x="flipper_length_mm", kind="kde", bw_adjust=1)sns.displot(data, x="flipper_length_mm", kind="kde", bw_adjust=0.25)sns.displot(data, x="flipper_length_mm", kind="kde", bw_adjust=1.5)
△Fig 3.2 不同的带宽参数对于核密度估计函数曲线的影响。
当然,核密度估计也能够在条件散布的状况下应用,如Fig 3.3所示,同样有几种不同的参数配置曲线的显式成果。
sns.displot(data, x="flipper_length_mm", hue="species", kind="kde")sns.displot(data, x="flipper_length_mm", hue="species", kind="kde", multiple="stack")sns.displot(data, x="flipper_length_mm", hue="species", kind="kde", fill=True)
△Fig 3.3 (a) 条件散布下的核密度估计曲线;(b)stack模式的条件散布核密度估计曲线;(c)填充曲线下面积的条件散布核密度估计曲线。
能够将直方图和核密度估计曲线绘制在同一张图中以便于剖析,如Fig 3.4所示。
sns.displot(data, x="flipper_length_mm", kde=True)sns.displot(data, x="flipper_length_mm", hue="species", kde=True)
△Fig 3.4 将直方图和核密度估计曲线绘制在同一张图中。
04 箱形图
直方图和核密度估计都太“重”了,很多时候在刚接触某个数据集的时候,一些统计性指标就足够让咱们对这份数据有足够的理解。罕用的统计指标有:中位数(50分位线数),均值,方差,25分位线数,75分位线数等,而这些指标大多数状况能够从箱型图(Boxplot)中高深莫测。如Fig 4.1所示,一个箱型图由五根线形成,Q1是25分位线,Q3是75分位线,指的是将数据从低到高排序(升序),前25%称之为25分位线,前75%称之为75分位线。Q3-Q1称之为四分位距(Inter Quartile Range,IQR),Q2示意50分位线,也即是中位线,小于Q1-1.5IQR 和 大于 Q3+1.5IQR的数据称之为离群点。
△Fig 4.1 箱型图的几种根本分位线。
如Fig 4.2所示,咱们能够用seaborn绘制箱型图,其中用红点示意均值,能够发现Fig 4.2(a)其实绘制了 \( P(\mathrm{flipper_length_mm|species}) \),当然也能够和Fig 4.2(c)一样绘制单变量的箱型图,这种也是咱们最常见到的模式。如Fig 4.2(b)所示,咱们还能够绘制2个条件下的箱型图,也即是\( P(\mathrm{flipper_length_mm|species,sex}) \)通过箱型图,咱们能够十分直观地察看到不同数据分布之间的差异,是一种轻量化的数据分布分析方法。
sns.boxplot(data=data, x="flipper_length_mm", y='species', showmeans=True, meanprops={"marker":"o", "markerfacecolor":"red", "markeredgecolor":"black", "markersize":"5"})sns.boxplot(data=data, x="flipper_length_mm", y='species', hue='sex', showmeans=True, meanprops={"marker":"o", "markerfacecolor":"red", "markeredgecolor":"black", "markersize":"5"})sns.boxplot(data=data, x="flipper_length_mm", showmeans=True, meanprops={"marker":"o", "markerfacecolor":"red", "markeredgecolor":"black", "markersize":"5"})
△Fig 4.2 (a)箱型图;(b)多个条件下的箱型图;(c)单变量箱型图。
05 提琴图
箱型图能够提供数据的直观印象,为了进一步剖析数据,咱们还是须要引入散布曲线。咱们心愿能够以箱型图的模式,同时把数据分布也可视化进去,这样咱们既能够复用箱型图失去的论断,而且能够进一步摸索数据分布的粗疏区别。提琴图(Violin Plot)就是为此设计的,如Fig 5.1所示,其将数据的核密度估计曲线以相似于箱型图的排版进行展现,其中的每条彩色竖线是每个实在的样本点数据。留神到提琴图实质是核密度估计曲线,因而如果样本数据过少其曲线是不精确的,所以通常咱们会把样本点也绘制进去(也即是彩色竖线),以判断数据数量是否会过于稠密导致KDE不相信。
sns.violinplot(data=data, x="flipper_length_mm", y='species', inner="stick")sns.violinplot(data=data, x="flipper_length_mm", y='species', hue='sex', split=True, inner="stick")sns.violinplot(data=data, x="flipper_length_mm", inner="stick")
△Fig 5.1 提琴图的不同模式,(a)提琴图;(b)两个条件下的提琴图;(c)单变量提琴图。
06 累计散布函数曲线
直方图须要抉择适合的分箱数,而KDE须要抉择适合的带宽,否则可能会影响数据分布的可视化成果进而影响剖析。有没有一种办法能够不必抉择任何参数就能表征数据的散布个性呢?教训累积散布函数(Empirical Cumulative Distribution Function,ECDF)兴许是一种可行的抉择。累积散布函数(Cumulative Distribution Function,CDF)\( F_X(x)\)对概率分布\( f_X(x) \)进行积分或者求和失去,如公式(6-1)所示。当对理论数据进行解决时候,因为咱们的数据无限且来自于理论察看,因而咱们通常称之为“教训”[2],并且ecdf是采纳求和,而不是积分。
$$ F_X(x) = P(X \leq x) = \int_{-\infty}^{x}f_X(t) \mathrm{d}t \tag{6-1} $$
如Fig 6.1所示,ecdf曲线是一个枯燥递增的曲线,其会思考每一个察看到的数据点,不须要指定其余额定的参数,如Fig 6.1(b)所示,咱们也能够绘制条件散布下的ecdf。ECDF的毛病在于不如直方图和核密度估计一样直观地表征了数据分布,然而实践上ECDF同样能够“坍缩”到概率分布,如公式(6-2)所示 。
$$ f_X(x) = \dfrac{\mathrm{d}F_X(x)}{\mathrm{d}x} \tag{6-2} $$
sns.displot(data, x="flipper_length_mm", kind="ecdf")sns.displot(data, x="flipper_length_mm", hue="species", kind="ecdf")
△Fig 6.1 ECDF曲线,(a)单变量的ECDF曲线;(b)条件散布下的ECDF曲线。
07 二元直方图
有时候咱们须要考查数据的联结概率分布,比方 \( P(\mathrm{flipper_length_mm, species}) \)此时能够绘制二元直方图。咱们能够指定displot()的y参数,绘制两元变量的直方图。这种类型的直方图相似于热值图(heatmap),以色彩的深浅示意数值的大小。如Fig 7.1(c)所示,同样能够指定kind参数进而绘制二元核密度估计曲线。在指定了hue的状况下,同样能够实现条件散布的绘制。
sns.displot(data, x="flipper_length_mm", y="species",cbar=True)sns.displot(data, x="bill_length_mm", y="bill_depth_mm", hue="species")sns.displot(data, x="bill_length_mm", y="bill_depth_mm", hue="species", kind="kde")
△Fig 7.1 二元直方图以及二元核密度估计曲线。
08 联结概率分布和边缘概率分布曲线
考查二元变量的联结概率分布,采纳散点图(scatter plot)也是一种不错的抉择,如Fig 8.1所示,能够将 \( P(\mathrm{bill_length_mm, bill_depth_mm})\)通过散点图的模式进行可视化,直观地考查两个变量之间的相干关系。散点图下面的直方图和左边的直方图别离是 \( P(\mathrm{bill_length_mm}) \) ,\( P(\mathrm{bill_depth_mm}) \),在这种情景下,咱们称之为边缘概率分布曲线(Marginal Distribution),其计算公式见(8-1)。
$$ P(x) = \sum_{y} P(x,y) \tag{8-1} $$
咱们也能够对联结概率分布的散点图和边缘概率分布的直方图进行核密度估计,如Fig 8.1(b)所示。
sns.jointplot(data=data, x="bill_length_mm", y="bill_depth_mm")sns.jointplot( data=data, x="bill_length_mm", y="bill_depth_mm", hue="species", kind="kde")
△Fig 8.1 联结概率分布与边缘概率分布的可视化。
通过应用seaborn的JointGrid性能,能够对联结概率分布和边缘概率分布的示意模式进行自定义组合(散点图,KDE,箱型图等),如Fig 8.2所示。
g = sns.JointGrid(data=data, x="bill_length_mm", y="bill_depth_mm")g.plot_joint(sns.histplot)g.plot_marginals(sns.kdeplot)g = sns.JointGrid(data=data, x="bill_length_mm", y="bill_depth_mm")g.plot_joint(sns.histplot)g.plot_marginals(sns.boxplot)g = sns.JointGrid(data=data, x="bill_length_mm", y="bill_depth_mm")g.plot_joint(sns.scatterplot)g.plot_marginals(sns.boxplot)
△Fig 8.2 通过JointGrid进行直方图,KDE,散点图,箱型图的组合。
END
参考资料:
[1]. https://seaborn.pydata.org/tutorial/distributions.html#plotti...
[2]. https://blog.csdn.net/LoseInVain/article/details/78746520,教训误差,泛化误差
举荐浏览:
数据库运维工作量间接缩小 50%,基于大模型构建智能问答零碎的技术分享
千万级高性能长连贯Go服务架构实际
百度搜寻Push个性化:新的冲破
智算让大模型触手可及
通过Python脚本反对OC代码重构实际(三):数据项应用模块接入数据通路的适配