You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Seaborn绘制值与频率分列小提琴图的大数据量优化方案

问题场景

测试数据构造代码如下:

import pandas as pd
import numpy as np
import seaborn as sns

np.random.seed(1)
data = {'values': range(0,200,1), 'frequency': np.random.randint(low=0, high=2000, size=200)}
df = pd.DataFrame(data)

绘图需求:

  • y轴映射values列
  • 小提琴不同高度位置的宽度对应该取值下frequency列的频次

原有实现逻辑:按照frequency的数值重复每一行,把聚合频次数据展开为逐行的单样本格式,再调用seaborn接口绘图,代码如下:

repeat_df = df.loc[df['values'].repeat(df['frequency'])]
sns.violinplot(y=repeat_df['values'])

原有实现生成的示例图
该实现的缺陷:当频次总和过大,展开后行数超过5000万行时,会产生极高的内存占用和计算开销,性能问题严重。

高性能实现方案

小提琴图的核心是核密度估计(KDE) 结果,展开重复行本质是为了让KDE计算时自动给高频值更高的权重,完全不需要真的生成冗余的重复行,直接传入权重参数即可得到数学上完全等价的密度结果,内存占用仅和原始聚合表的行数相关,和总频次规模无关。

最简方案(适用于seaborn 0.13及以上版本)

0.13版本后的seaborn为violinplot新增了原生weights参数,直接传入频次列即可,不需要做任何数据展开:

sns.violinplot(y=df['values'], weights=df['frequency'])

这行代码生成的图表和原有展开方案的结果完全一致,哪怕总频次数亿级,只要原始聚合表只有几百上千行,计算耗时都在毫秒级。

兼容低版本seaborn的方案

如果使用的seaborn版本低于0.13,可以手动计算加权KDE后调用matplotlib原生接口绘图:

from scipy.stats import gaussian_kde
import matplotlib.pyplot as plt

# 直接读取原始聚合数据,无冗余拷贝
vals = df['values'].to_numpy()
freqs = df['frequency'].to_numpy()

# 传入频次作为权重计算KDE,结果和展开全量样本完全等价
kde = gaussian_kde(vals, weights=freqs/freqs.sum())
# 生成绘图用的密度网格
y_grid = np.linspace(vals.min(), vals.max(), 1000)
density = kde(y_grid)

# 绘制小提琴主体
fig, ax = plt.subplots(figsize=(4,6))
violin_parts = ax.violinplot(
    dataset=[vals], vert=True,
    showmeans=False, showmedians=False, showextrema=False
)
# 替换默认密度路径为加权计算结果
for pc in violin_parts['bodies']:
    x_left = 0 - density / density.max() * 0.4
    x_right = 0 + density / density.max() * 0.4
    verts = np.column_stack([
        np.concatenate([x_left, x_right[::-1]]),
        np.concatenate([y_grid, y_grid[::-1]])
    ])
    pc.set_vertices(verts)
    pc.set_facecolor('#1f77b4')
    pc.set_alpha(1)

# 补充四分位、中位数、极值线,匹配seaborn默认样式
q1, med, q3 = np.percentile(vals, [25,50,75], weights=freqs)
vmin, vmax = vals.min(), vals.max()
ax.vlines(1, q1, q3, color='white', lw=3)
ax.scatter(1, med, marker='o', color='white', s=20, zorder=3)
ax.vlines(1, vmin, vmax, color='#1f77b4', lw=1)
ax.set_xticks([])
ax.set_ylabel('values')
sns.despine()
plt.show()

方案优势

  • 内存开销极低:不需要拷贝生成冗余的重复行,仅需处理原始聚合后的小规模DataFrame
  • 计算速度快:避免了千万/亿级行数据的遍历、拷贝、KDE计算开销,性能提升可达数百倍
  • 结果一致性:加权KDE的计算结果和展开全量样本的KDE结果数学等价,生成的图表视觉上无差异

内容的提问来源于stack exchange,提问作者Cactus Philosopher

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 15:45:49