当前位置:首页>python>《15节课Python绘图从入门到精通》5——直方图与核密度曲线的调参

《15节课Python绘图从入门到精通》5——直方图与核密度曲线的调参

  • 2026-04-15 20:31:54
《15节课Python绘图从入门到精通》5——直方图与核密度曲线的调参

前置数据准备

为保证所有程序可直接运行,先运行以下代码生成三组实验数据(对照组、处理组A、处理组B),每组包含一个连续型测量指标。

import pandas as pdimport numpy as npnp.random.seed(2024)n_per_group = 200# 对照组:正态分布,均值50,标准差8control = np.random.normal(508, n_per_group)# 处理组A:右偏分布(对数正态),均值约55,标准差约12treatment_a = np.random.lognormal(mean=4.0, sigma=0.25, size=n_per_group)  # 中位数约54.6# 处理组B:双峰分布(混合两个正态)treatment_b = np.concatenate([    np.random.normal(405, int(n_per_group * 0.6)),    np.random.normal(656, int(n_per_group * 0.4))])# 构建长格式DataFrame,便于分组操作df_long = pd.DataFrame({'测量值': np.concatenate([control, treatment_a, treatment_b]),'组别': ['对照组'] * n_per_group + ['处理组A'] * n_per_group + ['处理组B'] * n_per_group})df_long.to_csv('三组实验数据.csv', index=False, encoding='utf-8-sig')print("数据生成完成:三组实验数据.csv(含'测量值'和'组别'两列)")print(f"每组样本量:{n_per_group}")

执行结果如下(部分截图):

实操任务1:读取三组实验数据,绘制半透明堆叠直方图,对比分布形态差异

本任务从最基础的半透明直方图入手,逐步进阶到密度归一化、核密度曲线叠加、多面板排列及Seaborn快速绘制,符合顶刊数据分布对比规范。

基础版:半透明堆叠直方图(基本对比)

程序读取三组数据,在同一坐标系中绘制半透明直方图,通过颜色区分组别,直观对比分布形态。

import pandas as pdimport numpy as npimport matplotlib.pyplot as plt# 学术样式设置defset_academic_style():    plt.rcParams['font.family'] = ['Times New Roman''SimSun']    plt.rcParams['font.size'] = 9    plt.rcParams['axes.unicode_minus'] = False    plt.rcParams['axes.linewidth'] = 1.0    plt.rcParams['xtick.major.width'] = 1.0    plt.rcParams['ytick.major.width'] = 1.0    plt.rcParams['xtick.major.size'] = 3.5    plt.rcParams['ytick.major.size'] = 3.5    plt.rcParams['xtick.direction'] = 'in'    plt.rcParams['ytick.direction'] = 'in'    plt.rcParams['legend.frameon'] = False    plt.rcParams['pdf.fonttype'] = 42    plt.rcParams['savefig.dpi'] = 300set_academic_style()# 读取数据df = pd.read_csv('三组实验数据.csv')# 定义各组颜色(色盲友好配色)colors = {'对照组''#4472C4''处理组A''#ED7D31''处理组B''#70AD47'}fig, ax = plt.subplots(figsize=(4.53.5))# 分组绘制直方图,alpha控制透明度for group in df['组别'].unique():    subset = df[df['组别'] == group]['测量值']    ax.hist(subset, bins=25, alpha=0.5, color=colors[group],             edgecolor='black', linewidth=0.3, label=group)ax.set_xlabel('测量值')ax.set_ylabel('频数')ax.legend(loc='upper right', fontsize=8)plt.tight_layout()plt.show()fig.savefig('半透明堆叠直方图_基础.pdf', bbox_inches='tight', pad_inches=0.05)fig.savefig('半透明堆叠直方图_基础.png', bbox_inches='tight', pad_inches=0.05)

执行结果分析:

三组直方图重叠显示,半透明效果使重叠区域可见,便于比较分布中心、离散度和形态差异。

对照组呈对称分布,处理组A右偏,处理组B呈现双峰特征,直观展示组间异质性。

调整binsalpha可优化视觉效果;alpha=0.5为常用透明度值。

进阶1:密度归一化直方图(纵轴为概率密度)

将直方图纵轴转换为概率密度,使各组面积均为1,消除样本量差异影响,便于比较分布形态。

import pandas as pdimport numpy as npimport matplotlib.pyplot as pltdefset_academic_style():    plt.rcParams['font.family'] = ['Times New Roman''SimSun']    plt.rcParams['font.size'] = 9    plt.rcParams['axes.unicode_minus'] = False    plt.rcParams['axes.linewidth'] = 1.0    plt.rcParams['xtick.major.width'] = 1.0    plt.rcParams['ytick.major.width'] = 1.0    plt.rcParams['xtick.major.size'] = 3.5    plt.rcParams['ytick.major.size'] = 3.5    plt.rcParams['xtick.direction'] = 'in'    plt.rcParams['ytick.direction'] = 'in'    plt.rcParams['legend.frameon'] = False    plt.rcParams['pdf.fonttype'] = 42    plt.rcParams['savefig.dpi'] = 300set_academic_style()df = pd.read_csv('三组实验数据.csv')colors = {'对照组''#4472C4''处理组A''#ED7D31''处理组B''#70AD47'}fig, ax = plt.subplots(figsize=(4.53.5))for group in df['组别'].unique():    subset = df[df['组别'] == group]['测量值']    ax.hist(subset, bins=25, density=True, alpha=0.5, color=colors[group],            edgecolor='black', linewidth=0.3, label=group)ax.set_xlabel('测量值')ax.set_ylabel('概率密度')ax.legend(loc='upper right', fontsize=8)plt.tight_layout()plt.show()fig.savefig('密度归一化直方图.pdf', bbox_inches='tight', pad_inches=0.05)fig.savefig('密度归一化直方图.png', bbox_inches='tight', pad_inches=0.05)

执行结果分析:

使用density=True使各组直方图面积归一化为1,纵轴变为概率密度,即使样本量不同也能公平比较分布形状。

处理组B的双峰在密度尺度下更明显,峰面积比例反映混合权重。

推荐在样本量不等或关注分布形态时使用。

进阶2:直方图叠加核密度曲线(平滑分布估计)

在直方图基础上叠加核密度估计(KDE)曲线,提供更平滑的分布形态参考。

import pandas as pdimport numpy as npimport matplotlib.pyplot as pltfrom scipy.stats import gaussian_kdedefset_academic_style():    plt.rcParams['font.family'] = ['Times New Roman''SimSun']    plt.rcParams['font.size'] = 9    plt.rcParams['axes.unicode_minus'] = False    plt.rcParams['axes.linewidth'] = 1.0    plt.rcParams['xtick.major.width'] = 1.0    plt.rcParams['ytick.major.width'] = 1.0    plt.rcParams['xtick.major.size'] = 3.5    plt.rcParams['ytick.major.size'] = 3.5    plt.rcParams['xtick.direction'] = 'in'    plt.rcParams['ytick.direction'] = 'in'    plt.rcParams['legend.frameon'] = False    plt.rcParams['pdf.fonttype'] = 42    plt.rcParams['savefig.dpi'] = 300set_academic_style()df = pd.read_csv('三组实验数据.csv')colors = {'对照组''#4472C4''处理组A''#ED7D31''处理组B''#70AD47'}fig, ax = plt.subplots(figsize=(4.53.5))for group in df['组别'].unique():    data = df[df['组别'] == group]['测量值'].values# 直方图(密度归一化)    ax.hist(data, bins=25, density=True, alpha=0.4, color=colors[group],            edgecolor='black', linewidth=0.3, label=f'{group} (直方图)')# KDE曲线    kde = gaussian_kde(data)    x_grid = np.linspace(data.min(), data.max(), 200)    ax.plot(x_grid, kde(x_grid), color=colors[group], linewidth=1.8, label=f'{group} (KDE)')# 为避免图例重复,可精简图例;此处保留完整图例便于理解handles, labels = ax.get_legend_handles_labels()# 仅保留KDE曲线图例(可选)ax.legend(handles[3:], labels[3:], loc='upper right', fontsize=8)ax.set_xlabel('测量值')ax.set_ylabel('概率密度')plt.tight_layout()plt.show()fig.savefig('直方图叠加KDE.pdf', bbox_inches='tight', pad_inches=0.05)fig.savefig('直方图叠加KDE.png', bbox_inches='tight', pad_inches=0.05)

执行结果分析:

KDE曲线平滑展示分布形状,避免了直方图因分箱造成的人为锯齿。

处理组B的双峰在KDE中清晰呈现为两个峰,而直方图可能因分箱掩盖细节。

实际应用中,可仅保留KDE曲线图例,使图形更简洁。

进阶3:分面子图对比(各组分开展示)

当组间重叠严重时,将各组直方图分面绘制,并保持坐标轴范围一致,便于单独观察。

import pandas as pdimport numpy as npimport matplotlib.pyplot as pltdefset_academic_style():    plt.rcParams['font.family'] = ['Times New Roman''SimSun']    plt.rcParams['font.size'] = 9    plt.rcParams['axes.unicode_minus'] = False    plt.rcParams['axes.linewidth'] = 1.0    plt.rcParams['xtick.major.width'] = 1.0    plt.rcParams['ytick.major.width'] = 1.0    plt.rcParams['xtick.major.size'] = 3.5    plt.rcParams['ytick.major.size'] = 3.5    plt.rcParams['xtick.direction'] = 'in'    plt.rcParams['ytick.direction'] = 'in'    plt.rcParams['legend.frameon'] = False    plt.rcParams['pdf.fonttype'] = 42    plt.rcParams['savefig.dpi'] = 300set_academic_style()df = pd.read_csv('三组实验数据.csv')groups = df['组别'].unique()colors = {'对照组''#4472C4''处理组A''#ED7D31''处理组B''#70AD47'}fig, axes = plt.subplots(13, figsize=(9.03.2), sharex=True, sharey=True)# 确定全局x轴范围x_min = df['测量值'].min() * 0.95x_max = df['测量值'].max() * 1.05for ax, group in zip(axes, groups):    data = df[df['组别'] == group]['测量值']    ax.hist(data, bins=25, density=True, alpha=0.7, color=colors[group],            edgecolor='black', linewidth=0.3)    ax.set_title(group, fontsize=10)    ax.set_xlabel('测量值')    ax.set_ylabel('概率密度')    ax.set_xlim(x_min, x_max)plt.tight_layout()plt.show()fig.savefig('分面直方图对比.pdf', bbox_inches='tight', pad_inches=0.05)fig.savefig('分面直方图对比.png', bbox_inches='tight', pad_inches=0.05)

执行结果分析:

分面显示消除了重叠干扰,各组的分布特征一目了然。

共享坐标轴使组间可直接比较位置和离散度。

适用于组间分布差异显著且需要独立展示的情况。

进阶4:Seaborn快速绘制

利用Seaborn库简化绘图代码,可以一行代码实现分组直方图+KDE,同时保持出版级美观。

import pandas as pdimport matplotlib.pyplot as pltimport seaborn as snsdefset_academic_style():    plt.rcParams['font.family'] = ['Times New Roman''SimSun']    plt.rcParams['font.size'] = 9    plt.rcParams['axes.unicode_minus'] = False    plt.rcParams['axes.linewidth'] = 1.0    plt.rcParams['xtick.major.width'] = 1.0    plt.rcParams['ytick.major.width'] = 1.0    plt.rcParams['xtick.major.size'] = 3.5    plt.rcParams['ytick.major.size'] = 3.5    plt.rcParams['xtick.direction'] = 'in'    plt.rcParams['ytick.direction'] = 'in'    plt.rcParams['legend.frameon'] = False    plt.rcParams['pdf.fonttype'] = 42    plt.rcParams['savefig.dpi'] = 300set_academic_style()df = pd.read_csv('三组实验数据.csv')fig, ax = plt.subplots(figsize=(4.53.5))# 使用seaborn的histplot,设置元素类型和透明度sns.histplot(data=df, x='测量值', hue='组别', bins=25, alpha=0.5,             kde=True, palette=['#4472C4''#ED7D31''#70AD47'],             edgecolor='black', linewidth=0.3, ax=ax)ax.set_xlabel('测量值')ax.set_ylabel('频数')# Seaborn图例默认有边框,需手动去除legend = ax.get_legend()if legend:    legend.set_frame_on(False)plt.tight_layout()plt.show()fig.savefig('Seaborn分组直方图_KDE.pdf', bbox_inches='tight', pad_inches=0.05)fig.savefig('Seaborn分组直方图_KDE.png', bbox_inches='tight', pad_inches=0.05)

执行结果分析:

Seaborn一行代码实现分组直方图叠加KDE,显著减少代码量。

内置美观配色和自动图例,适合快速探索性分析。

可通过kde=True直接添加密度曲线,无需手动计算。

实操任务2:调整bins参数

直方图的bins(分箱数)选择对分布形态的呈现至关重要。本任务通过可视化对比不同bins的效果,并介绍几种自动确定最优bins的统计方法。

基础版:bins参数对比网格(欠平滑 → 过平滑)

使用同一组数据,绘制不同bins值的直方图,直观展示bins对分布解读的影响。

import pandas as pdimport numpy as npimport matplotlib.pyplot as pltdefset_academic_style():    plt.rcParams['font.family'] = ['Times New Roman''SimSun']    plt.rcParams['font.size'] = 9    plt.rcParams['axes.unicode_minus'] = False    plt.rcParams['axes.linewidth'] = 1.0    plt.rcParams['xtick.major.width'] = 1.0    plt.rcParams['ytick.major.width'] = 1.0    plt.rcParams['xtick.major.size'] = 3.5    plt.rcParams['ytick.major.size'] = 3.5    plt.rcParams['xtick.direction'] = 'in'    plt.rcParams['ytick.direction'] = 'in'    plt.rcParams['legend.frameon'] = False    plt.rcParams['pdf.fonttype'] = 42    plt.rcParams['savefig.dpi'] = 300set_academic_style()df = pd.read_csv('三组实验数据.csv')# 使用对照组数据作为示例data = df[df['组别'] == '对照组']['测量值'].values# 定义不同bins值bins_list = [5153060]fig, axes = plt.subplots(22, figsize=(6.55.0))axes = axes.flatten()for ax, bins in zip(axes, bins_list):    ax.hist(data, bins=bins, color='#4472C4', alpha=0.7, edgecolor='black', linewidth=0.3)    ax.set_title(f'bins = {bins}', fontsize=10)    ax.set_xlabel('测量值')    ax.set_ylabel('频数')# 添加描述标签if bins == 5:        ax.text(0.950.95'欠平滑', transform=ax.transAxes, ha='right', va='top',                bbox=dict(boxstyle='round', facecolor='white', alpha=0.8))elif bins == 15:        ax.text(0.950.95'适中', transform=ax.transAxes, ha='right', va='top',                bbox=dict(boxstyle='round', facecolor='white', alpha=0.8))elif bins == 60:        ax.text(0.950.95'过平滑', transform=ax.transAxes, ha='right', va='top',                bbox=dict(boxstyle='round', facecolor='white', alpha=0.8))plt.tight_layout()plt.show()fig.savefig('bins参数对比网格.pdf', bbox_inches='tight', pad_inches=0.05)fig.savefig('bins参数对比网格.png', bbox_inches='tight', pad_inches=0.05)

执行结果分析:

bins=5:分箱过宽,丢失分布细节,呈“欠平滑”状态。

bins=15:清晰展示分布的单峰对称形态,为“适中”选择。

bins=30~60:分箱过细,出现大量空箱和噪声,呈“过平滑/过拟合”状态,干扰分布判断。

最优bins通常在15~30之间,取决于样本量和数据范围。

进阶1:自动计算最优bins(Sturges、Scott、Freedman-Diaconis规则)

使用统计学公式自动推荐bins数,并可视化对比。

import pandas as pdimport numpy as npimport matplotlib.pyplot as pltdefset_academic_style():    plt.rcParams['font.family'] = ['Times New Roman''SimSun']    plt.rcParams['font.size'] = 9    plt.rcParams['axes.unicode_minus'] = False    plt.rcParams['axes.linewidth'] = 1.0    plt.rcParams['xtick.major.width'] = 1.0    plt.rcParams['ytick.major.width'] = 1.0    plt.rcParams['xtick.major.size'] = 3.5    plt.rcParams['ytick.major.size'] = 3.5    plt.rcParams['xtick.direction'] = 'in'    plt.rcParams['ytick.direction'] = 'in'    plt.rcParams['legend.frameon'] = False    plt.rcParams['pdf.fonttype'] = 42    plt.rcParams['savefig.dpi'] = 300set_academic_style()df = pd.read_csv('三组实验数据.csv')data = df[df['组别'] == '对照组']['测量值'].valuesn = len(data)# 计算三种规则推荐的bins数# Sturges: k = ceil(log2(n) + 1)bins_sturges = int(np.ceil(np.log2(n) + 1))# Scott: bin_width = 3.5 * std / n^(1/3) → bins = range / widthbin_width_scott = 3.5 * np.std(data) / (n ** (1/3))bins_scott = int(np.ceil((data.max() - data.min()) / bin_width_scott))# Freedman-Diaconis: bin_width = 2 * IQR / n^(1/3)iqr = np.percentile(data, 75) - np.percentile(data, 25)bin_width_fd = 2 * iqr / (n ** (1/3))bins_fd = int(np.ceil((data.max() - data.min()) / bin_width_fd))print(f"Sturges推荐bins: {bins_sturges}")print(f"Scott推荐bins: {bins_scott}")print(f"Freedman-Diaconis推荐bins: {bins_fd}")# 绘制三个推荐bins的直方图对比fig, axes = plt.subplots(13, figsize=(9.03.0))bins_dict = {'Sturges': bins_sturges, 'Scott': bins_scott, 'F-D': bins_fd}for ax, (name, b) in zip(axes, bins_dict.items()):    ax.hist(data, bins=b, color='#4472C4', alpha=0.7, edgecolor='black', linewidth=0.3)    ax.set_title(f'{name} (bins={b})')    ax.set_xlabel('测量值')    ax.set_ylabel('频数')plt.tight_layout()plt.show()fig.savefig('自动bins规则对比.pdf', bbox_inches='tight', pad_inches=0.05)fig.savefig('自动bins规则对比.png', bbox_inches='tight', pad_inches=0.05)

执行结果分析:

Sturges规则适用于正态分布小样本,n=200时推荐约9 bins,略显保守。

Scott规则基于标准差,对正态数据表现良好,推荐约9 bins。

Freedman-Diaconis规则基于IQR,对异常值鲁棒,推荐约12 bins。

实际应用中,可将这些值作为起点,结合领域知识微调。

进阶2:分组数据的最优bins对比(各组独立确定)

不同组数据分布差异大时,可为每组独立计算最优bins并绘制。

import pandas as pdimport numpy as npimport matplotlib.pyplot as pltdefset_academic_style():    plt.rcParams['font.family'] = ['Times New Roman''SimSun']    plt.rcParams['font.size'] = 9    plt.rcParams['axes.unicode_minus'] = False    plt.rcParams['axes.linewidth'] = 1.0    plt.rcParams['xtick.major.width'] = 1.0    plt.rcParams['ytick.major.width'] = 1.0    plt.rcParams['xtick.major.size'] = 3.5    plt.rcParams['ytick.major.size'] = 3.5    plt.rcParams['xtick.direction'] = 'in'    plt.rcParams['ytick.direction'] = 'in'    plt.rcParams['legend.frameon'] = False    plt.rcParams['pdf.fonttype'] = 42    plt.rcParams['savefig.dpi'] = 300set_academic_style()df = pd.read_csv('三组实验数据.csv')groups = df['组别'].unique()colors = {'对照组''#4472C4''处理组A''#ED7D31''处理组B''#70AD47'}fig, axes = plt.subplots(13, figsize=(9.03.2), sharex=True, sharey=False)for ax, group in zip(axes, groups):    data = df[df['组别'] == group]['测量值'].values    n = len(data)# 使用Freedman-Diaconis规则计算bins    iqr = np.percentile(data, 75) - np.percentile(data, 25)    bin_width = 2 * iqr / (n ** (1/3))    bins = int(np.ceil((data.max() - data.min()) / bin_width))    ax.hist(data, bins=bins, density=True, color=colors[group], alpha=0.7,            edgecolor='black', linewidth=0.3)    ax.set_title(f'{group} (bins={bins})')    ax.set_xlabel('测量值')    ax.set_ylabel('概率密度')plt.tight_layout()plt.show()fig.savefig('分组最优bins直方图.pdf', bbox_inches='tight', pad_inches=0.05)fig.savefig('分组最优bins直方图.png', bbox_inches='tight', pad_inches=0.05)

执行结果分析:

处理组A(右偏)和B(双峰)所需bins数与对照组不同,独立确定能更好地揭示分布特征。

进阶3:直方图与核密度曲线联合确定最优bins(视觉校准)

通过叠加核密度曲线,评估直方图bins是否与平滑分布估计一致。

import pandas as pdimport numpy as npimport matplotlib.pyplot as pltfrom scipy.stats import gaussian_kdedefset_academic_style():    plt.rcParams['font.family'] = ['Times New Roman''SimSun']    plt.rcParams['font.size'] = 9    plt.rcParams['axes.unicode_minus'] = False    plt.rcParams['axes.linewidth'] = 1.0    plt.rcParams['xtick.major.width'] = 1.0    plt.rcParams['ytick.major.width'] = 1.0    plt.rcParams['xtick.major.size'] = 3.5    plt.rcParams['ytick.major.size'] = 3.5    plt.rcParams['xtick.direction'] = 'in'    plt.rcParams['ytick.direction'] = 'in'    plt.rcParams['legend.frameon'] = False    plt.rcParams['pdf.fonttype'] = 42    plt.rcParams['savefig.dpi'] = 300set_academic_style()df = pd.read_csv('三组实验数据.csv')data = df[df['组别'] == '对照组']['测量值'].valueskde = gaussian_kde(data)x_grid = np.linspace(data.min(), data.max(), 200)bins_list = [51530]fig, axes = plt.subplots(13, figsize=(9.03.0))for ax, bins in zip(axes, bins_list):    ax.hist(data, bins=bins, density=True, color='#4472C4', alpha=0.6,            edgecolor='black', linewidth=0.3, label=f'bins={bins}')    ax.plot(x_grid, kde(x_grid), color='#C00000', linewidth=1.8, label='KDE')    ax.set_xlabel('测量值')    ax.set_ylabel('概率密度')    ax.legend(loc='upper right', fontsize=8)plt.tight_layout()plt.show()fig.savefig('bins与KDE联合校准.pdf', bbox_inches='tight', pad_inches=0.05)fig.savefig('bins与KDE联合校准.png', bbox_inches='tight', pad_inches=0.05)

执行结果分析:

KDE曲线作为“真值”参考,bins=15时直方图与KDE吻合最佳。

bins=5过于平滑,丢失峰部细节;bins=30出现噪声波动,与KDE偏离。

此方法可帮助研究者可视化地确定最优bins。

实操任务3:绘制累积分布函数曲线,对比对照组与处理组在各百分位数上的差异

累积分布函数(CDF)能清晰展示数据的百分位数、分布形状及组间差异,无需分箱,不受主观参数影响。本任务从基础ECDF绘制进阶到多组对比、KS检验标注及对数坐标。

基础版:两组数据的ECDF曲线对比

绘制对照组与处理组A的经验累积分布函数,直观比较分布位置和形状。

import pandas as pdimport numpy as npimport matplotlib.pyplot as pltdefset_academic_style():    plt.rcParams['font.family'] = ['Times New Roman''SimSun']    plt.rcParams['font.size'] = 9    plt.rcParams['axes.unicode_minus'] = False    plt.rcParams['axes.linewidth'] = 1.0    plt.rcParams['xtick.major.width'] = 1.0    plt.rcParams['ytick.major.width'] = 1.0    plt.rcParams['xtick.major.size'] = 3.5    plt.rcParams['ytick.major.size'] = 3.5    plt.rcParams['xtick.direction'] = 'in'    plt.rcParams['ytick.direction'] = 'in'    plt.rcParams['legend.frameon'] = False    plt.rcParams['pdf.fonttype'] = 42    plt.rcParams['savefig.dpi'] = 300set_academic_style()df = pd.read_csv('三组实验数据.csv')control = df[df['组别'] == '对照组']['测量值'].valuestreat_a = df[df['组别'] == '处理组A']['测量值'].values# 计算ECDFdefecdf(data):    x = np.sort(data)    y = np.arange(1, len(data)+1) / len(data)return x, yx_ctrl, y_ctrl = ecdf(control)x_a, y_a = ecdf(treat_a)fig, ax = plt.subplots(figsize=(4.23.4))ax.step(x_ctrl, y_ctrl, where='post', color='#4472C4', linewidth=1.8, label='对照组')ax.step(x_a, y_a, where='post', color='#ED7D31', linewidth=1.8, label='处理组A')ax.set_xlabel('测量值')ax.set_ylabel('累积概率')ax.legend(loc='lower right', fontsize=8)ax.grid(alpha=0.3, linestyle='--', linewidth=0.5)plt.tight_layout()plt.show()fig.savefig('ECDF两组对比.pdf', bbox_inches='tight', pad_inches=0.05)fig.savefig('ECDF两组对比.png', bbox_inches='tight', pad_inches=0.05)

执行结果分析:

阶梯状ECDF曲线显示数据的累积比例,处理组A曲线整体右移,表明其值普遍高于对照组。

曲线斜率反映概率密度:陡峭处对应高密度区域,平缓处对应低密度区域。

可轻松读取任意分位数,如中位数(y=0.5对应的x值)。

进阶1:标注关键百分位数(中位数、四分位数)

在ECDF曲线上添加水平线和垂直线,突出显示特定百分位数。

import pandas as pdimport numpy as npimport matplotlib.pyplot as pltdefset_academic_style():    plt.rcParams['font.family'] = ['Times New Roman''SimSun']    plt.rcParams['font.size'] = 9    plt.rcParams['axes.unicode_minus'] = False    plt.rcParams['axes.linewidth'] = 1.0    plt.rcParams['xtick.major.width'] = 1.0    plt.rcParams['ytick.major.width'] = 1.0    plt.rcParams['xtick.major.size'] = 3.5    plt.rcParams['ytick.major.size'] = 3.5    plt.rcParams['xtick.direction'] = 'in'    plt.rcParams['ytick.direction'] = 'in'    plt.rcParams['legend.frameon'] = False    plt.rcParams['pdf.fonttype'] = 42    plt.rcParams['savefig.dpi'] = 300set_academic_style()df = pd.read_csv('三组实验数据.csv')control = df[df['组别'] == '对照组']['测量值'].valuestreat_a = df[df['组别'] == '处理组A']['测量值'].valuesdefecdf(data):    x = np.sort(data)    y = np.arange(1, len(data)+1) / len(data)return x, yx_ctrl, y_ctrl = ecdf(control)x_a, y_a = ecdf(treat_a)fig, ax = plt.subplots(figsize=(4.53.5))ax.step(x_ctrl, y_ctrl, where='post', color='#4472C4', linewidth=1.8, label='对照组')ax.step(x_a, y_a, where='post', color='#ED7D31', linewidth=1.8, label='处理组A')# 绘制水平参考线(只画一次)ax.axhline(y=0.5, color='gray', linestyle=':', linewidth=0.8, alpha=0.6)ax.axhline(y=0.25, color='gray', linestyle=':', linewidth=0.5, alpha=0.4)ax.axhline(y=0.75, color='gray', linestyle=':', linewidth=0.5, alpha=0.4)# 标注中位数和四分位数# 对照组median_ctrl = np.median(control)q1_ctrl = np.percentile(control, 25)q3_ctrl = np.percentile(control, 75)ax.axvline(x=median_ctrl, color='#4472C4', linestyle='--', linewidth=0.8, alpha=0.7)ax.axvline(x=q1_ctrl, color='#4472C4', linestyle=':', linewidth=0.6, alpha=0.5)ax.axvline(x=q3_ctrl, color='#4472C4', linestyle=':', linewidth=0.6, alpha=0.5)ax.text(median_ctrl, 0.53f'中位数={median_ctrl:.1f}', color='#4472C4', fontsize=7, ha='center')ax.text(q1_ctrl, 0.28f'Q1={q1_ctrl:.1f}', color='#4472C4', fontsize=7, ha='center')ax.text(q3_ctrl, 0.78f'Q3={q3_ctrl:.1f}', color='#4472C4', fontsize=7, ha='center')# 处理组Amedian_a = np.median(treat_a)q1_a = np.percentile(treat_a, 25)q3_a = np.percentile(treat_a, 75)ax.axvline(x=median_a, color='#ED7D31', linestyle='--', linewidth=0.8, alpha=0.7)ax.axvline(x=q1_a, color='#ED7D31', linestyle=':', linewidth=0.6, alpha=0.5)ax.axvline(x=q3_a, color='#ED7D31', linestyle=':', linewidth=0.6, alpha=0.5)ax.text(median_a, 0.47f'中位数={median_a:.1f}', color='#ED7D31', fontsize=7, ha='center')ax.text(q1_a, 0.22f'Q1={q1_a:.1f}', color='#ED7D31', fontsize=7, ha='center')ax.text(q3_a, 0.72f'Q3={q3_a:.1f}', color='#ED7D31', fontsize=7, ha='center')ax.set_xlabel('测量值')ax.set_ylabel('累积概率')ax.legend(loc='lower right', fontsize=8)ax.grid(alpha=0.3, linestyle='--', linewidth=0.5)plt.tight_layout()plt.show()fig.savefig('ECDF标注百分位数.pdf', bbox_inches='tight', pad_inches=0.05)fig.savefig('ECDF标注百分位数.png', bbox_inches='tight', pad_inches=0.05)

执行结果分析:

灰色水平线标记累积概率0.25、0.5、0.75,垂直虚线标出各组中位数位置。

处理组A中位数(52.5)明显高于对照组(50.4)

此类标注使百分位数对比定量化,适合报告关键统计量。

进阶2:多组ECDF对比 + Kolmogorov-Smirnov检验结果标注

计算两组间KS统计量及p值,并标注在图上,量化分布差异显著性。

import pandas as pdimport numpy as npimport matplotlib.pyplot as pltfrom scipy.stats import ks_2sampdefset_academic_style():    plt.rcParams['font.family'] = ['Times New Roman''SimSun']    plt.rcParams['font.size'] = 9    plt.rcParams['axes.unicode_minus'] = False    plt.rcParams['axes.linewidth'] = 1.0    plt.rcParams['xtick.major.width'] = 1.0    plt.rcParams['ytick.major.width'] = 1.0    plt.rcParams['xtick.major.size'] = 3.5    plt.rcParams['ytick.major.size'] = 3.5    plt.rcParams['xtick.direction'] = 'in'    plt.rcParams['ytick.direction'] = 'in'    plt.rcParams['legend.frameon'] = False    plt.rcParams['pdf.fonttype'] = 42    plt.rcParams['savefig.dpi'] = 300set_academic_style()df = pd.read_csv('三组实验数据.csv')groups = ['对照组''处理组A''处理组B']colors = ['#4472C4''#ED7D31''#70AD47']defecdf(data):    x = np.sort(data)    y = np.arange(1, len(data)+1) / len(data)return x, yfig, ax = plt.subplots(figsize=(4.83.6))for group, color in zip(groups, colors):    data = df[df['组别'] == group]['测量值'].values    x_ecdf, y_ecdf = ecdf(data)    ax.step(x_ecdf, y_ecdf, where='post', color=color, linewidth=1.8, label=group)# 对照组 vs 处理组A KS检验ctrl = df[df['组别'] == '对照组']['测量值'].valuestreat_a = df[df['组别'] == '处理组A']['测量值'].valuesks_stat, p_value = ks_2samp(ctrl, treat_a)ax.text(0.050.85f'KS检验 (对照 vs 处理A):\nD = {ks_stat:.3f}, p = {p_value:.3e}',        transform=ax.transAxes, fontsize=8,        bbox=dict(boxstyle='round', facecolor='white', alpha=0.8))ax.set_xlabel('测量值')ax.set_ylabel('累积概率')ax.legend(loc='lower right', fontsize=8)ax.grid(alpha=0.3, linestyle='--', linewidth=0.5)plt.tight_layout()plt.show()fig.savefig('ECDF多组_KS检验.pdf', bbox_inches='tight', pad_inches=0.05)fig.savefig('ECDF多组_KS检验.png', bbox_inches='tight', pad_inches=0.05)

执行结果分析:

三组ECDF曲线清晰展示分布差异,处理组B双峰导致曲线在中段出现平台。

KS检验p值极小,表明对照组与处理组A分布差异极显著。

在学术论文中,ECDF配合KS检验是严谨的分布对比方法。

进阶3:使用statsmodels的ECDF绘图函数(简化代码)

statsmodels库提供ECDF类,可直接生成ECDF对象并绘图,减少手动编码。

import pandas as pdimport numpy as npimport matplotlib.pyplot as pltimport statsmodels.api as smdefset_academic_style():    plt.rcParams['font.family'] = ['Times New Roman''SimSun']    plt.rcParams['font.size'] = 9    plt.rcParams['axes.unicode_minus'] = False    plt.rcParams['axes.linewidth'] = 1.0    plt.rcParams['xtick.major.width'] = 1.0    plt.rcParams['ytick.major.width'] = 1.0    plt.rcParams['xtick.major.size'] = 3.5    plt.rcParams['ytick.major.size'] = 3.5    plt.rcParams['xtick.direction'] = 'in'    plt.rcParams['ytick.direction'] = 'in'    plt.rcParams['legend.frameon'] = False    plt.rcParams['pdf.fonttype'] = 42    plt.rcParams['savefig.dpi'] = 300set_academic_style()df = pd.read_csv('三组实验数据.csv')control = df[df['组别'] == '对照组']['测量值'].valuestreat_a = df[df['组别'] == '处理组A']['测量值'].valuesecdf_ctrl = sm.distributions.ECDF(control)ecdf_a = sm.distributions.ECDF(treat_a)fig, ax = plt.subplots(figsize=(4.23.4))x_plot = np.linspace(df['测量值'].min(), df['测量值'].max(), 200)ax.step(x_plot, ecdf_ctrl(x_plot), where='post', color='#4472C4', linewidth=1.8, label='对照组')ax.step(x_plot, ecdf_a(x_plot), where='post', color='#ED7D31', linewidth=1.8, label='处理组A')ax.set_xlabel('测量值')ax.set_ylabel('累积概率')ax.legend(loc='lower right', fontsize=8)ax.grid(alpha=0.3, linestyle='--', linewidth=0.5)plt.tight_layout()plt.show()fig.savefig('ECDF_statsmodels.pdf', bbox_inches='tight', pad_inches=0.05)fig.savefig('ECDF_statsmodels.png', bbox_inches='tight', pad_inches=0.05)

执行结果分析:

statsmodels的ECDF类封装了计算逻辑,调用ecdf(x)即可获得累积概率,代码更简洁。

适合快速生成ECDF图,尤其在与statsmodels其他统计功能联用时。

进阶4:对数坐标轴下的ECDF(处理长尾数据)

当数据跨越多个数量级时,将x轴转换为对数坐标,可更好展示低值区域的分布细节。

import pandas as pdimport numpy as npimport matplotlib.pyplot as pltdefset_academic_style():    plt.rcParams['font.family'] = ['Times New Roman''SimSun']    plt.rcParams['font.size'] = 9    plt.rcParams['axes.unicode_minus'] = False    plt.rcParams['axes.linewidth'] = 1.0    plt.rcParams['xtick.major.width'] = 1.0    plt.rcParams['ytick.major.width'] = 1.0    plt.rcParams['xtick.major.size'] = 3.5    plt.rcParams['ytick.major.size'] = 3.5    plt.rcParams['xtick.direction'] = 'in'    plt.rcParams['ytick.direction'] = 'in'    plt.rcParams['legend.frameon'] = False    plt.rcParams['pdf.fonttype'] = 42    plt.rcParams['savefig.dpi'] = 300set_academic_style()# 模拟长尾数据(处理组A本身是对数正态,已具长尾;为演示更明显,可生成新数据)np.random.seed(2024)long_tail = np.random.lognormal(mean=4.0, sigma=0.8, size=500)  # 更明显的长尾normal_data = np.random.normal(508500)defecdf(data):    x = np.sort(data)    y = np.arange(1, len(data)+1) / len(data)return x, yx_lt, y_lt = ecdf(long_tail)x_norm, y_norm = ecdf(normal_data)fig, (ax1, ax2) = plt.subplots(12, figsize=(7.03.2))# 线性坐标ax1.step(x_lt, y_lt, where='post', color='#ED7D31', linewidth=1.8, label='长尾分布')ax1.step(x_norm, y_norm, where='post', color='#4472C4', linewidth=1.8, label='正态分布')ax1.set_xlabel('测量值')ax1.set_ylabel('累积概率')ax1.legend(loc='lower right', fontsize=8)ax1.grid(alpha=0.3, linestyle='--', linewidth=0.5)ax1.set_title('线性坐标')# 对数坐标ax2.step(x_lt, y_lt, where='post', color='#ED7D31', linewidth=1.8, label='长尾分布')ax2.step(x_norm, y_norm, where='post', color='#4472C4', linewidth=1.8, label='正态分布')ax2.set_xscale('log')ax2.set_xlabel('测量值 (对数坐标)')ax2.set_ylabel('累积概率')ax2.legend(loc='lower right', fontsize=8)ax2.grid(alpha=0.3, linestyle='--', linewidth=0.5)ax2.set_title('对数坐标')plt.tight_layout()plt.show()fig.savefig('ECDF对数坐标.pdf', bbox_inches='tight', pad_inches=0.05)fig.savefig('ECDF对数坐标.png', bbox_inches='tight', pad_inches=0.05)

执行结果分析:

线性坐标下,长尾分布的高值区域被压缩,低值区域细节难以辨认。

对数坐标拉伸低值区域,清晰展示长尾分布的上升形态,便于比较不同分布的低百分位数。

适用于收入、基因表达量、粒径等对数正态分布数据。

实操任务4:绘制小提琴图

小提琴图结合了箱线图和核密度估计,既能展示分布形态,又能显示关键统计量。本任务从基础绘制进阶到自定义美化、分组对比及散点叠加。

基础版:Seaborn快速绘制带内嵌箱线图的小提琴图

Seaborn的violinplot函数可直接在内部绘制微型箱线图,参数inner='box'即可实现。

import pandas as pdimport matplotlib.pyplot as pltimport seaborn as snsdefset_academic_style():    plt.rcParams['font.family'] = ['Times New Roman''SimSun']    plt.rcParams['font.size'] = 9    plt.rcParams['axes.unicode_minus'] = False    plt.rcParams['axes.linewidth'] = 1.0    plt.rcParams['xtick.major.width'] = 1.0    plt.rcParams['ytick.major.width'] = 1.0    plt.rcParams['xtick.major.size'] = 3.5    plt.rcParams['ytick.major.size'] = 3.5    plt.rcParams['xtick.direction'] = 'in'    plt.rcParams['ytick.direction'] = 'in'    plt.rcParams['legend.frameon'] = False    plt.rcParams['pdf.fonttype'] = 42    plt.rcParams['savefig.dpi'] = 300set_academic_style()df = pd.read_csv('三组实验数据.csv')fig, ax = plt.subplots(figsize=(4.54.0))sns.violinplot(data=df, x='组别', y='测量值', inner='box',               palette=['#4472C4''#ED7D31''#70AD47'],               linewidth=0.8, ax=ax)ax.set_xlabel('组别')ax.set_ylabel('测量值')# 去除上、右 spinessns.despine()plt.tight_layout()plt.show()fig.savefig('小提琴图_基础.pdf', bbox_inches='tight', pad_inches=0.05)fig.savefig('小提琴图_基础.png', bbox_inches='tight', pad_inches=0.05)

执行结果分析:

小提琴宽度反映概率密度,处理组A右偏、处理组B双峰形态一目了然。

内部白色箱线图显示中位数(白点)、四分位距(粗黑线)及须线范围。

Seaborn自动处理分组与配色,代码极简。

进阶1:自定义小提琴图细节(线宽、颜色、透明度)

使用matplotlib底层violinplot函数实现更精细控制。

import pandas as pdimport numpy as npimport matplotlib.pyplot as pltdefset_academic_style():    plt.rcParams['font.family'] = ['Times New Roman''SimSun']    plt.rcParams['font.size'] = 9    plt.rcParams['axes.unicode_minus'] = False    plt.rcParams['axes.linewidth'] = 1.0    plt.rcParams['xtick.major.width'] = 1.0    plt.rcParams['ytick.major.width'] = 1.0    plt.rcParams['xtick.major.size'] = 3.5    plt.rcParams['ytick.major.size'] = 3.5    plt.rcParams['xtick.direction'] = 'in'    plt.rcParams['ytick.direction'] = 'in'    plt.rcParams['legend.frameon'] = False    plt.rcParams['pdf.fonttype'] = 42    plt.rcParams['savefig.dpi'] = 300set_academic_style()df = pd.read_csv('三组实验数据.csv')groups = df['组别'].unique()data_list = [df[df['组别'] == g]['测量值'].values for g in groups]colors = ['#4472C4''#ED7D31''#70AD47']fig, ax = plt.subplots(figsize=(4.54.0))# 绘制小提琴图parts = ax.violinplot(data_list, positions=[123], showmeans=False, showmedians=False)# 自定义小提琴外观for i, pc in enumerate(parts['bodies']):    pc.set_facecolor(colors[i])    pc.set_edgecolor('black')    pc.set_linewidth(0.8)    pc.set_alpha(0.7)# 叠加箱线图(手动绘制关键统计量)for i, data in enumerate(data_list):    pos = i + 1# 计算统计量    q1, median, q3 = np.percentile(data, [255075])    iqr = q3 - q1    whisker_low = np.max([data.min(), q1 - 1.5 * iqr])    whisker_high = np.min([data.max(), q3 + 1.5 * iqr])# 绘制箱线图元素# 中位线    ax.plot([pos-0.2, pos+0.2], [median, median], color='white', linewidth=2, solid_capstyle='butt')# 箱体(IQR)    box = plt.Rectangle((pos-0.15, q1), 0.3, iqr, fill=False, edgecolor='white', linewidth=1.5)    ax.add_patch(box)# 须线    ax.plot([pos, pos], [whisker_low, q1], color='white', linewidth=1.2)    ax.plot([pos, pos], [q3, whisker_high], color='white', linewidth=1.2)    ax.plot([pos-0.1, pos+0.1], [whisker_low, whisker_low], color='white', linewidth=1.2)    ax.plot([pos-0.1, pos+0.1], [whisker_high, whisker_high], color='white', linewidth=1.2)ax.set_xticks([123])ax.set_xticklabels(groups)ax.set_xlabel('组别')ax.set_ylabel('测量值')ax.set_xlim(0.53.5)plt.tight_layout()plt.show()fig.savefig('小提琴图_自定义箱线.pdf', bbox_inches='tight', pad_inches=0.05)fig.savefig('小提琴图_自定义箱线.png', bbox_inches='tight', pad_inches=0.05)

执行结果分析:

手动绘制箱线图元素可完全控制线条颜色、粗细,实现白色内嵌箱线图,与深色小提琴形成对比。

小提琴颜色半透明,内部结构清晰可见。

此方法适合需要高度定制化的出版级图表。

进阶2:小提琴图 + 散点叠加(展示所有数据点)

在小提琴图旁或内部叠加原始数据点,增强数据透明度,尤其适合样本量不大的情况。

import pandas as pdimport numpy as npimport matplotlib.pyplot as pltimport seaborn as snsdefset_academic_style():    plt.rcParams['font.family'] = ['Times New Roman''SimSun']    plt.rcParams['font.size'] = 9    plt.rcParams['axes.unicode_minus'] = False    plt.rcParams['axes.linewidth'] = 1.0    plt.rcParams['xtick.major.width'] = 1.0    plt.rcParams['ytick.major.width'] = 1.0    plt.rcParams['xtick.major.size'] = 3.5    plt.rcParams['ytick.major.size'] = 3.5    plt.rcParams['xtick.direction'] = 'in'    plt.rcParams['ytick.direction'] = 'in'    plt.rcParams['legend.frameon'] = False    plt.rcParams['pdf.fonttype'] = 42    plt.rcParams['savefig.dpi'] = 300set_academic_style()df = pd.read_csv('三组实验数据.csv')fig, ax = plt.subplots(figsize=(5.04.0))# 绘制小提琴图(半透明)sns.violinplot(data=df, x='组别', y='测量值', inner=None,               palette=['#4472C4''#ED7D31''#70AD47'],               alpha=0.5, linewidth=0.8, ax=ax)# 叠加散点(带抖动)sns.stripplot(data=df, x='组别', y='测量值', jitter=0.15, size=3,              alpha=0.6, palette=['#4472C4''#ED7D31''#70AD47'], ax=ax)ax.set_xlabel('组别')ax.set_ylabel('测量值')sns.despine()plt.tight_layout()plt.show()fig.savefig('小提琴图_散点叠加.pdf', bbox_inches='tight', pad_inches=0.05)fig.savefig('小提琴图_散点叠加.png', bbox_inches='tight', pad_inches=0.05)

执行结果分析:

散点展示了每个样本的具体值,处理组B的双峰结构在散点图中表现为两个簇。

抖动(jitter)避免点重叠,但需注意不要误导分布形状;小提琴轮廓提供准确密度估计。

适合小样本(n<200)或需要展示所有数据点的场景。

进阶3:分组小提琴图(按第二类别细分)

当存在两个分类变量时,使用hue参数绘制分组小提琴图。

import pandas as pdimport numpy as npimport matplotlib.pyplot as pltimport seaborn as snsdefset_academic_style():    plt.rcParams['font.family'] = ['Times New Roman''SimSun']    plt.rcParams['font.size'] = 9    plt.rcParams['axes.unicode_minus'] = False    plt.rcParams['axes.linewidth'] = 1.0    plt.rcParams['xtick.major.width'] = 1.0    plt.rcParams['ytick.major.width'] = 1.0    plt.rcParams['xtick.major.size'] = 3.5    plt.rcParams['ytick.major.size'] = 3.5    plt.rcParams['xtick.direction'] = 'in'    plt.rcParams['ytick.direction'] = 'in'    plt.rcParams['legend.frameon'] = False    plt.rcParams['pdf.fonttype'] = 42    plt.rcParams['savefig.dpi'] = 300set_academic_style()# 扩展数据:添加“时间点”变量np.random.seed(2024)df = pd.read_csv('三组实验数据.csv')# 复制数据,增加时间点维度df_time = df.copy()df_time['时间点'] = 'T0'df2 = df.copy()df2['时间点'] = 'T1'df2['测量值'] = df2['测量值'] * np.random.uniform(1.11.3, len(df2))  # T1整体上移df_long = pd.concat([df_time, df2], ignore_index=True)fig, ax = plt.subplots(figsize=(6.04.0))sns.violinplot(data=df_long, x='组别', y='测量值', hue='时间点',               split=False, inner='box', palette='Set2', linewidth=0.8, ax=ax)ax.set_xlabel('组别')ax.set_ylabel('测量值')ax.legend(title='时间点', frameon=False, fontsize=8)sns.despine()plt.tight_layout()plt.show()fig.savefig('小提琴图_分组对比.pdf', bbox_inches='tight', pad_inches=0.05)fig.savefig('小提琴图_分组对比.png', bbox_inches='tight', pad_inches=0.05)

执行结果分析:

每组内并排展示T0和T1的小提琴图,直观比较时间效应。

可通过split=True将两个时间点的小提琴在组内左右对称绘制(需两组hue),适合配对比较。

适用于多因素实验设计的数据展示。

进阶4:调整核密度带宽(bw参数)优化小提琴平滑度

小提琴图的平滑程度由核密度估计的带宽决定,通过调整带宽可突出或抑制分布细节。

import pandas as pdimport numpy as npimport matplotlib.pyplot as pltimport seaborn as snsdefset_academic_style():    plt.rcParams['font.family'] = ['Times New Roman''SimSun']    plt.rcParams['font.size'] = 9    plt.rcParams['axes.unicode_minus'] = False    plt.rcParams['axes.linewidth'] = 1.0    plt.rcParams['xtick.major.width'] = 1.0    plt.rcParams['ytick.major.width'] = 1.0    plt.rcParams['xtick.major.size'] = 3.5    plt.rcParams['ytick.major.size'] = 3.5    plt.rcParams['xtick.direction'] = 'in'    plt.rcParams['ytick.direction'] = 'in'    plt.rcParams['legend.frameon'] = False    plt.rcParams['pdf.fonttype'] = 42    plt.rcParams['savefig.dpi'] = 300set_academic_style()df = pd.read_csv('三组实验数据.csv')# 使用处理组B(双峰)演示带宽影响data_b = df[df['组别'] == '处理组B']['测量值'].valuesfig, axes = plt.subplots(13, figsize=(8.03.5))bw_values = [0.52.05.0]  # 相对于标准带宽的缩放因子for ax, bw in zip(axes, bw_values):# 使用matplotlib的violinplot,可控制带宽    parts = ax.violinplot([data_b], positions=[1], bw_method=bw, showmeans=False, showmedians=False)    parts['bodies'][0].set_facecolor('#70AD47')    parts['bodies'][0].set_alpha(0.7)# 添加箱线信息    q1, med, q3 = np.percentile(data_b, [255075])    ax.scatter(1, med, color='white', edgecolor='black', s=30, zorder=3)    ax.plot([0.91.1], [q1, q1], color='black', linewidth=1)    ax.plot([0.91.1], [q3, q3], color='black', linewidth=1)    ax.plot([11], [q1, q3], color='black', linewidth=1.5)    ax.set_title(f'bw_method = {bw}')    ax.set_xticks([1])    ax.set_xticklabels(['处理组B'])    ax.set_ylabel('测量值')    ax.set_xlim(0.51.5)plt.tight_layout()plt.show()fig.savefig('小提琴图_带宽调整.pdf', bbox_inches='tight', pad_inches=0.05)fig.savefig('小提琴图_带宽调整.png', bbox_inches='tight', pad_inches=0.05)

执行结果分析:

bw_method=0.5:带宽小,小提琴显示更多细节,双峰结构突出。

bw_method=2.0:适中带宽,双峰仍可见但更平滑。

bw_method=5.0:带宽过大,双峰被过度平滑,呈现单峰假象。

推荐使用Seaborn默认带宽或根据Scott规则自动选择,特殊情况下可手动调整以强调或抑制细节。

- END -

最新文章

随机文章

基本 文件 流程 错误 SQL 调试
  1. 请求信息 : 2026-04-16 01:17:44 HTTP/2.0 GET : https://f.mffb.com.cn/a/486451.html
  2. 运行时间 : 0.106830s [ 吞吐率:9.36req/s ] 内存消耗:5,266.07kb 文件加载:140
  3. 缓存信息 : 0 reads,0 writes
  4. 会话信息 : SESSION_ID=c3f7230bb6195877334f8023f39434e8
  1. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/public/index.php ( 0.79 KB )
  2. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/autoload.php ( 0.17 KB )
  3. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_real.php ( 2.49 KB )
  4. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/platform_check.php ( 0.90 KB )
  5. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/ClassLoader.php ( 14.03 KB )
  6. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/composer/autoload_static.php ( 4.90 KB )
  7. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper.php ( 8.34 KB )
  8. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/helper.php ( 2.19 KB )
  9. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/helper.php ( 1.47 KB )
  10. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/stubs/load_stubs.php ( 0.16 KB )
  11. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Exception.php ( 1.69 KB )
  12. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Facade.php ( 2.71 KB )
  13. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/deprecation-contracts/function.php ( 0.99 KB )
  14. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap.php ( 8.26 KB )
  15. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/polyfill-mbstring/bootstrap80.php ( 9.78 KB )
  16. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/Resources/functions/dump.php ( 1.49 KB )
  17. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-dumper/src/helper.php ( 0.18 KB )
  18. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/symfony/var-dumper/VarDumper.php ( 4.30 KB )
  19. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/App.php ( 15.30 KB )
  20. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-container/src/Container.php ( 15.76 KB )
  21. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/container/src/ContainerInterface.php ( 1.02 KB )
  22. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/provider.php ( 0.19 KB )
  23. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Http.php ( 6.04 KB )
  24. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Str.php ( 7.29 KB )
  25. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Env.php ( 4.68 KB )
  26. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/common.php ( 0.03 KB )
  27. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/helper.php ( 18.78 KB )
  28. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Config.php ( 5.54 KB )
  29. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/app.php ( 0.95 KB )
  30. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cache.php ( 0.78 KB )
  31. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/console.php ( 0.23 KB )
  32. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/cookie.php ( 0.56 KB )
  33. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/database.php ( 2.48 KB )
  34. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Env.php ( 1.67 KB )
  35. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/filesystem.php ( 0.61 KB )
  36. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/lang.php ( 0.91 KB )
  37. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/log.php ( 1.35 KB )
  38. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/middleware.php ( 0.19 KB )
  39. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/route.php ( 1.89 KB )
  40. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/session.php ( 0.57 KB )
  41. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/trace.php ( 0.34 KB )
  42. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/config/view.php ( 0.82 KB )
  43. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/event.php ( 0.25 KB )
  44. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Event.php ( 7.67 KB )
  45. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/service.php ( 0.13 KB )
  46. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/AppService.php ( 0.26 KB )
  47. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Service.php ( 1.64 KB )
  48. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Lang.php ( 7.35 KB )
  49. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/lang/zh-cn.php ( 13.70 KB )
  50. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/Error.php ( 3.31 KB )
  51. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/RegisterService.php ( 1.33 KB )
  52. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/services.php ( 0.14 KB )
  53. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/PaginatorService.php ( 1.52 KB )
  54. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ValidateService.php ( 0.99 KB )
  55. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/service/ModelService.php ( 2.04 KB )
  56. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Service.php ( 0.77 KB )
  57. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Middleware.php ( 6.72 KB )
  58. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/initializer/BootService.php ( 0.77 KB )
  59. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Paginator.php ( 11.86 KB )
  60. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-validate/src/Validate.php ( 63.20 KB )
  61. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/Model.php ( 23.55 KB )
  62. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Attribute.php ( 21.05 KB )
  63. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/AutoWriteData.php ( 4.21 KB )
  64. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/Conversion.php ( 6.44 KB )
  65. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/DbConnect.php ( 5.16 KB )
  66. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/ModelEvent.php ( 2.33 KB )
  67. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/concern/RelationShip.php ( 28.29 KB )
  68. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Arrayable.php ( 0.09 KB )
  69. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/contract/Jsonable.php ( 0.13 KB )
  70. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/model/contract/Modelable.php ( 0.09 KB )
  71. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Db.php ( 2.88 KB )
  72. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/DbManager.php ( 8.52 KB )
  73. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Log.php ( 6.28 KB )
  74. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Manager.php ( 3.92 KB )
  75. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerTrait.php ( 2.69 KB )
  76. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/log/src/LoggerInterface.php ( 2.71 KB )
  77. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cache.php ( 4.92 KB )
  78. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/psr/simple-cache/src/CacheInterface.php ( 4.71 KB )
  79. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/helper/Arr.php ( 16.63 KB )
  80. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/driver/File.php ( 7.84 KB )
  81. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/cache/Driver.php ( 9.03 KB )
  82. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/CacheHandlerInterface.php ( 1.99 KB )
  83. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/Request.php ( 0.09 KB )
  84. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Request.php ( 55.78 KB )
  85. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/middleware.php ( 0.25 KB )
  86. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Pipeline.php ( 2.61 KB )
  87. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/TraceDebug.php ( 3.40 KB )
  88. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/middleware/SessionInit.php ( 1.94 KB )
  89. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Session.php ( 1.80 KB )
  90. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/driver/File.php ( 6.27 KB )
  91. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/SessionHandlerInterface.php ( 0.87 KB )
  92. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/session/Store.php ( 7.12 KB )
  93. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Route.php ( 23.73 KB )
  94. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleName.php ( 5.75 KB )
  95. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Domain.php ( 2.53 KB )
  96. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleGroup.php ( 22.43 KB )
  97. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Rule.php ( 26.95 KB )
  98. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/RuleItem.php ( 9.78 KB )
  99. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/route/app.php ( 1.72 KB )
  100. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/Route.php ( 4.70 KB )
  101. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/dispatch/Controller.php ( 4.74 KB )
  102. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/route/Dispatch.php ( 10.44 KB )
  103. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/controller/Index.php ( 4.81 KB )
  104. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/app/BaseController.php ( 2.05 KB )
  105. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/facade/Db.php ( 0.93 KB )
  106. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/connector/Mysql.php ( 5.44 KB )
  107. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/PDOConnection.php ( 52.47 KB )
  108. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Connection.php ( 8.39 KB )
  109. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/ConnectionInterface.php ( 4.57 KB )
  110. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/builder/Mysql.php ( 16.58 KB )
  111. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Builder.php ( 24.06 KB )
  112. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseBuilder.php ( 27.50 KB )
  113. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/Query.php ( 15.71 KB )
  114. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/BaseQuery.php ( 45.13 KB )
  115. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TimeFieldQuery.php ( 7.43 KB )
  116. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/AggregateQuery.php ( 3.26 KB )
  117. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ModelRelationQuery.php ( 20.07 KB )
  118. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ParamsBind.php ( 3.66 KB )
  119. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/ResultOperation.php ( 7.01 KB )
  120. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/WhereQuery.php ( 19.37 KB )
  121. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/JoinAndViewQuery.php ( 7.11 KB )
  122. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/TableFieldInfo.php ( 2.63 KB )
  123. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-orm/src/db/concern/Transaction.php ( 2.77 KB )
  124. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/driver/File.php ( 5.96 KB )
  125. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/LogHandlerInterface.php ( 0.86 KB )
  126. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/log/Channel.php ( 3.89 KB )
  127. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/event/LogRecord.php ( 1.02 KB )
  128. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-helper/src/Collection.php ( 16.47 KB )
  129. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/facade/View.php ( 1.70 KB )
  130. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/View.php ( 4.39 KB )
  131. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Response.php ( 8.81 KB )
  132. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/response/View.php ( 3.29 KB )
  133. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/Cookie.php ( 6.06 KB )
  134. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-view/src/Think.php ( 8.38 KB )
  135. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/framework/src/think/contract/TemplateHandlerInterface.php ( 1.60 KB )
  136. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/Template.php ( 46.61 KB )
  137. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/driver/File.php ( 2.41 KB )
  138. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-template/src/template/contract/DriverInterface.php ( 0.86 KB )
  139. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/runtime/temp/067d451b9a0c665040f3f1bdd3293d68.php ( 11.98 KB )
  140. /yingpanguazai/ssd/ssd1/www/f.mffb.com.cn/vendor/topthink/think-trace/src/Html.php ( 4.42 KB )
  1. CONNECT:[ UseTime:0.000719s ] mysql:host=127.0.0.1;port=3306;dbname=f_mffb;charset=utf8mb4
  2. SHOW FULL COLUMNS FROM `fenlei` [ RunTime:0.000859s ]
  3. SELECT * FROM `fenlei` WHERE `fid` = 0 [ RunTime:0.000311s ]
  4. SELECT * FROM `fenlei` WHERE `fid` = 63 [ RunTime:0.000327s ]
  5. SHOW FULL COLUMNS FROM `set` [ RunTime:0.000555s ]
  6. SELECT * FROM `set` [ RunTime:0.000208s ]
  7. SHOW FULL COLUMNS FROM `article` [ RunTime:0.000517s ]
  8. SELECT * FROM `article` WHERE `id` = 486451 LIMIT 1 [ RunTime:0.007926s ]
  9. UPDATE `article` SET `lasttime` = 1776273464 WHERE `id` = 486451 [ RunTime:0.010753s ]
  10. SELECT * FROM `fenlei` WHERE `id` = 66 LIMIT 1 [ RunTime:0.000342s ]
  11. SELECT * FROM `article` WHERE `id` < 486451 ORDER BY `id` DESC LIMIT 1 [ RunTime:0.000717s ]
  12. SELECT * FROM `article` WHERE `id` > 486451 ORDER BY `id` ASC LIMIT 1 [ RunTime:0.000446s ]
  13. SELECT * FROM `article` WHERE `id` < 486451 ORDER BY `id` DESC LIMIT 10 [ RunTime:0.001213s ]
  14. SELECT * FROM `article` WHERE `id` < 486451 ORDER BY `id` DESC LIMIT 10,10 [ RunTime:0.006241s ]
  15. SELECT * FROM `article` WHERE `id` < 486451 ORDER BY `id` DESC LIMIT 20,10 [ RunTime:0.007071s ]
0.108555s