赞
踩
Seaborn 的 kdeplot() 函数是 Python 中绘制密度图的方式之一,Matplotlib 在现阶段则没有具体的绘制密度图的函数,一般是结合 Scipy 库中的 gaussian_kde() 函数结果进行绘制。
Python 的 scikit-learn 库中 neighbors.KernelDensity() 模块提供Gaussian、Tophat、Epanechnikov、Exponential、Linear 和 Cosine 6 种核函数来进行核密度估计计算。Python 的 KDEpy 库更是提供了多达 9 种核函数,包括 Gaussian、Exponential、Box、Tri、Epa、Biweight、Triweight、Tricube、
Cosine。
# 图3-2-5(a)核心绘制代码 import Seaborn as sns fig,ax = plt.subplots(figsize=(4,3.5),dpi=100,) kde_01 = sns.kdeplot(x="data_01",data=data_df, color="#1180D5",alpha=1,shade=True,ax=ax) sns.rugplot(data=data_df, x="data_01",color='k', height=.05,ax=ax) # 图3-2-5(b)核心绘制代码 from scipy import stats density = stats.kde.gaussian_kde(data_01) x = np.linspace(-2,25,500) y = density(x) fig,ax = plt.subplots(figsize=(4,3.5),dpi=100) ax.plot(x,y, lw=1,color="k") ax.fill(x,y,color="#07A6C5") # 添加单独的数据 ax.plot(data_01, [0.005]*len(data_01), '|',color='k',lw=1) # 图3-2-5(c)核心绘制代码 from KDEpy import FFTKDE x, y = FFTKDE(bw=2).fit(data_01).evaluate() fig,ax = plt.subplots(figsize=(4,3.5),dpi=100) ax.plot(x,y, lw=1,color="k",) ax.fill(x,y,color="#FBCD2D") # 添加单独的数据 ax.plot(data_01, [0.005]*len(data_01), '|', color='k',lw=1)
对于具有不同数值分布情况的多组样本数据,我们经常使用同一个核函数对它进行拟合并将结果绘制成密度图。这种情况一般发生在数据探索阶段,上述方法常用于查看每个维度数据的分布情况或对不同数据间的差异进行对比。
下图为 Matplotlib 绘制的多组样本数据使用同一个核函数的核密度图,展示了不同数据的分布情况,涉及多子图绘制、X 轴Y 轴共享、子图间布局等绘图技巧。
代码如下:
import numpy as np import pandas as pd from KDEpy import NaiveKDE import matplotlib.pyplot as plt nrow = 2 ncol = 2 ax_label = ["a.","b.","c.","d."] titles = ["Type One","Type Two","Type Three","Type Four"] indexs = [i for i in data_df.columns] fig,axs = plt.subplots(nrow,ncol,figsize=(5,4),dpi=100, sharey=True,sharex=True,constrained_layout=True) for ax, index,label in zip(axs.flat, indexs,ax_label): x,y = NaiveKDE(kernel="Gaussian",bw=2)\ fit(data_df[index].values).evaluate() ax.plot(x,y, lw=1,color="#1BB71B") ax.fill(x,y,color="#1BB71B",alpha=.6) # 添加单独的数据 ax.plot(data_df[index].values, [0.005]*len(data_df[index].values), '|', lw=1, color='k') ax.text(0.05, 0.95, label, transform=ax.transAxes, fontsize=16, fontweight='bold', va='top') ax.text(0.65, 0.95, index,transform=ax.transAxes, fontsize=14, fontweight='bold', va='top') # 添加子图共用坐标轴标签 fig.supxlabel('Values') fig.supylabel('Density') plt.show()
用一个连续渐变颜色条表示具体的绘图数值,且对应颜色填充在密度图曲线范围内。
在 Python 绘图体系中,我们需要通过自定义绘制方式实现该效果。图 3-2-8 展示了利用 Matplotlib、ProPlot、SciencePlots 库分别绘制的带渐变颜色(gradient color)填充的密度图。需要注意的是,这里的连续填充颜色系为自定义的 parula 颜色系(MATLAB 经典的颜色系),只需将 colormaps.py 文件添加到当前绘制环境中即可导入定义好的 parula 颜色系。
代码如下:
import numpy as np import pandas as pd from KDEpy import FFTKDE from colormaps import parula from matplotlib.axes import Axes import matplotlib.pyplot as plt cmap = parula x, y = FFTKDE(kernel="gaussian", bw=2).fit(data_01). evaluate() img_data = x.reshape(1, -1) fig,ax = plt.subplots(figsize=(4,3.5),dpi=100) ax.plot(x,y, lw=1,color="k") # 添加单独的数据 ax.plot(data_01, [0.005]*len(data_01), '|', color='k',lw=1) extent=[*ax.get_xlim(), *ax.get_ylim()] im = Axes.imshow(ax, img_data, aspect='auto',cmap=cmap,extent=extent) # 注意,这是使用了","符号 fill_line,= ax.fill(x, y, facecolor='none') # 利用set_clip_path()方法进行裁剪 23. im.set_clip_path(fill_line) 24. colorbar = fig.colorbar(im,ax=ax,aspect=12,label="Values")
在对多组数据进行密度图绘制时,除上述介绍的使用子图对每组数据进行绘制以外,我们还可以将多组数据绘制结果进行堆叠摆放,即使用“山脊”图(ridgeline chart)进行表示。“山脊”图通常用来表示不同类别的数据在同一因素的分布差异情况。在 Matplotlib 中,我们可以使用 Matplotlib 的“原生”方法绘制“山脊”图,也可以使用 JoyPy 库绘制。图 3-2-10 为利用Matplotlib 库原生方法和 JoyPy 库分别绘制的“山脊”图。
sord_index = [i for i in group_data.color.unique()] sord_index = sorted(sord_index, key=str.lower) fig,ax = plt.subplots(figsize=(5.5,4.5), dpi=100) for i,index in zip(range(len(sord_index)), sord_index): data = group_data.loc[group_data["color"]==index, "depth"].values x,y = NaiveKDE(kernel="Gaussian", bw=.8). fit(data).evaluate() ax.plot(x, 6*y+i, lw=.6, color="k", zorder=100-i) ax.fill(x, 6*y+i, lw=1, color="gray", alpha=.6, zorder=100-i) ax.grid(which="major", axis="y", ls="--", lw=.7, color="gray", zorder=-1) ax.yaxis.set_tick_params(labelleft=True) ax.set_yticks(np.arange(len(sord_index))) ax.set_yticklabels(sord_index)
如果想使用连续渐变颜色对“山脊”图中的每组数据进行填充,并且用连续渐变颜色值表示数据大小,那么可以参考渐变颜色填充密度图的绘制方法。需要注意的是,由于绘制脚本中涉及循环绘制语句,因此,在保存成矢量文件(如 PDF 文件)时,会出现裁剪失败问题。想要解决这一问题,我们只需要在编写脚本前添加如下代码。
# 在将多个绘图对象保存为PDF文件时,需要进行如下设置
plt.rcParams["image.composite_image"] = False
图 3-2-12 为利用 Matplotlib 绘制的渐变颜色填充“山脊”图,其中,图 3-2-12(a)使用了颜色映射样式“plasma”,图 3-2-12(b)使用了颜色映射样式“parula”,不同颜色代表不同的变量(Depth)数值大小。
# 在将多个绘图对象保存为PDF文件时,需要进行如下设置 plt.rcParams["image.composite_image"] = False fig,ax = plt.subplots(figsize=(5.5, 4.5), dpi=100) for i,index in zip(range(len(sord_index)), sord_index): data = group_data.loc[group_data["color"]==index,"depth"].values x,y = NaiveKDE(kernel="Gaussian", bw=.8).fit(data).evaluate() img_data = x.reshape(1, -1) ax.plot(x,6*y+i, lw=1, color="k", zorder=100-i) fill_line, = ax.fill(x, 6*y+i, facecolor="none") ax.grid(which="major", axis="y", ls="--", lw=.7, color="gray", zorder=-1) ax.set_xlim(50,72) ax.yaxis.set_tick_params(labelleft=True) ax.set_yticks(np.arange(len(sord_index))) ax.set_yticklabels(sord_index) ax.set_xlabel("Depth") ax.set_ylabel("Color") ax.tick_params(which ="both",top=False,right=False) ax.tick_params(which = "minor", axis="both", left=False,bottom=False) for spin in ["top", "right", "bottom", "left"]: ax.spines[spin].set_visible(False) extent=[*ax.get_xlim(), *ax.get_ylim()] im = Axes.imshow(ax, img_data, aspect='auto',cmap="plasma", extent=extent) im.set_clip_path(fill_line) colorbar = fig.colorbar(im, aspect=10, shrink=0.5) colorbar.ax.set_title("Values", fontsize=10)
在将多个密度图绘制在同一坐标系时,除了使用 Matplotlib 库进行循环绘制以外,还可以使用 Seaborn 库进行快速绘制。图 3-2-14 为使用 Matplotlib 和 Seaborn 分别绘制的“同一坐标系中的多个密度图”。注意,使用 Seaborn 绘制的同一坐标系中的多个密度图的默认顺序与Matplotlib 绘制结果不同。
# 利用Matplotlib 绘制 from scipy import stats palette = ["#352A87", "#108ED2", "#65BE86", "#FFC337"] fig,ax = plt.subplots(figsize=(4, 3.5), dpi=100) for i, index, color in zip(range(len(palette)), data_df.columns, palette): data = data_df[index].values density = stats.kde.gaussian_kde(data) x = np.linspace(-2, 25, 500) y = density(x) ax.plot(x, y, lw=.5, color="k", zorder=5-i) ax.fill(x, y, color=color, label=index, alpha=.7) ax.set_xlabel("Values") ax.set_ylabel("Density") ax.legend() # 利用Seaborn绘制 fig,ax = plt.subplots(figsize=(4, 3.5), dpi=100) sns.kdeplot(data=data_df, shade=True, palette= palette, alpha=.6, ax=ax) ax.set_xlabel("Values")
在 ProPlot 库的编辑环境中绘制 Seaborn 的绘图对象时,两者虽然都是基于 Matplotlib 开发的高级封装库,但二者之间还存在较大的差异,无法较好地在特定图形绘制中形成统一的语法标准,导致 ProPlot 库在绘制 Seaborn 图形对象时的绘图定制化操作较弱。图 3-2-15 为使用ProPlot、SciencePlots 库分别绘制的“同一坐标系中的多个密度图”。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。