如何用Seaborn或其他工具制作Graphpad Prism风格带全数据点小提琴图
复刻Graphpad Prism风格的小提琴图(Python/R实现)
Python方案(优先Seaborn)
要实现Prism那种「仅在数据密集区水平分散点、尾部点保留中心线」的效果,核心思路是根据数据密度动态调整点的水平偏移:密度高的区域偏移大(避免重叠),密度低的尾部偏移为0。
步骤与代码示例
import seaborn as sns import matplotlib.pyplot as plt import numpy as np from scipy.stats import gaussian_kde # 加载示例数据 tips = sns.load_dataset("tips") # 初始化画布 fig, ax = plt.subplots(figsize=(8, 6)) # 绘制基础小提琴图 sns.violinplot(data=tips, x="day", y="total_bill", ax=ax, inner=None, color="lightgray") # 自定义点的位置:按密度调整水平偏移 for idx, day in enumerate(tips["day"].unique()): # 获取当前类别的数据 data = tips[tips["day"] == day]["total_bill"].values # 计算核密度估计(KDE) kde = gaussian_kde(data) density = kde(data) # 归一化密度值,用于生成偏移 norm_density = (density - density.min()) / (density.max() - density.min()) # 设置偏移范围:密度最高的点偏移±0.2,尾部点偏移0 offset = norm_density * np.random.uniform(-0.2, 0.2, size=len(data)) # 绘制带偏移的点 ax.scatter(np.full(len(data), idx) + offset, data, s=20, alpha=0.6, color="#3377bb") # 调整坐标轴与样式 ax.set_xlabel("Day") ax.set_ylabel("Total Bill") plt.tight_layout() plt.show()
关键细节
- 用
gaussian_kde计算数据密度,确保偏移只发生在数据密集区 - 可通过调整
np.random.uniform的范围(比如±0.15)控制偏移幅度,避免过度分散 - 如果需要更精准的对称偏移,可替换随机偏移为基于密度的对称分布值
替代工具:plotnine(仿ggplot2)
若习惯ggplot语法,plotnine结合自定义位置调整函数也能实现:
from plotnine import ggplot, aes, geom_violin, geom_point, scale_x_discrete from plotnine.data import tips import numpy as np from scipy.stats import gaussian_kde # 自定义位置调整:仅在高密度区抖动 def position_prism_jitter(width=0.2): def adjust(data): kde = gaussian_kde(data['y']) density = kde(data['y']) norm_density = (density - density.min())/(density.max()-density.min()) data['x'] += norm_density * np.random.uniform(-width, width, len(data)) return data return adjust (ggplot(tips, aes(x='day', y='total_bill')) + geom_violin(fill='lightgray', inner=None) + geom_point(position=position_prism_jitter(width=0.2), alpha=0.6, color='#3377bb') + scale_x_discrete() ).draw()
R方案(ggplot2)
R中用ggplot2可通过自定义位置函数或扩展包实现类似效果:
自定义偏移实现
library(ggplot2) library(dplyr) library(KernSmooth) # 加载示例数据 data(tips) # 自定义位置调整函数 position_prism <- function(width = 0.2) { function(data) { # 按分组计算密度并生成偏移 data <- data %>% group_by(x) %>% mutate( density = bkde(y, kernel = "normal")$y[findInterval(y, bkde(y)$x)], norm_density = (density - min(density))/(max(density) - min(density)), x = x + norm_density * runif(n(), -width, width) ) %>% ungroup() data } } # 绘制图形 ggplot(tips, aes(x = day, y = total_bill)) + geom_violin(fill = "lightgray", inner = NULL) + geom_point(position = position_prism(width = 0.2), alpha = 0.6, color = "#3377bb") + labs(x = "Day", y = "Total Bill") + theme_minimal()
简化方案:ggbeeswarm扩展包
若不需要严格复刻尾部无偏移,ggbeeswarm的position_quasirandom会自动根据密度调整点位置,效果接近Prism:
library(ggbeeswarm) ggplot(tips, aes(x = day, y = total_bill)) + geom_violin(fill = "lightgray", inner = NULL) + geom_quasirandom(width = 0.2, alpha = 0.6, color = "#3377bb") + labs(x = "Day", y = "Total Bill")
内容的提问来源于stack exchange,提问作者Dan K
相关产品推荐
相关产品推荐

