根据《科研论文配图绘制指南——基于Python》第三章中密度图的绘制部分,对章节重点进行归纳总结,主要介绍了绘制密度图方法对比、针对不同数值分布情况的多组样本数据的密度图绘制、渐变颜色的填充绘制。
密度图
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。
1.3 种绘制密度图方法对比
下面使用 Seaborn 中的 kdeplot() 函数、Scipy 库中的 gaussian_kde() 函数,以及 KDEpy 库的计算结果结合 Matplotlib 中的 axes.Axes.plot()、axes.Axes.fill() 函数 3 种方法分别绘制密度图。
使用 Seaborn 中的 kdeplot() 函数绘制密度图较为简单,结合 rugplot() 函数可以绘制沿 X 轴的数据分布情况。其他两种方法较 kdeplot() 函数麻烦一些,但这两种方法绘制出的密度图更为清楚。注意,这里的核密度估计结果都是通过高斯核函数得到的。
上图核心绘制代码如下:
# 图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)
2.多组数据、同一个核函数
对于具有不同数值分布情况的多组样本数据,我们经常使用同一个核函数对它进行拟合并将结果绘制成密度图。这种情况一般发生在数据探索阶段,上述方法常用于查看每个维度数据的分布情况或对不同数据间的差异进行对比。
下图为 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()
3.渐变颜色填充
有时,为了更好地呈现绘图数据的数值范围和密度图的视觉效果,我们需要对绘制密度图的原始数据值进行颜色映射,即用一个连续渐变颜色条表示具体的绘图数值,且对应颜色填充在密度图曲线范围内。在 Python 绘图体系中,我们需要通过自定义绘制方式实现该效果。
下图展示了利用 Matplotlib、ProPlot、SciencePlots 库分别绘制的带渐变颜色(gradientcolor)填充的密度图。
需要注意的是,这里的连续填充颜色系为自定义的 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',w=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()方法进行裁剪
im.set_clip_path(fill_line)
colorbar = fig.colorbar(im,ax=ax,aspect=12,label="Values")