小提琴图(Violin Plot)是一种结合箱线图与核密度估计(Kernel Density Estimation, KDE)的先进统计图表,1998年由Hintze和Nelson首次提出。它能同时展示数据的分布形态、概率密度及关键统计量(如中位数、四分位数),弥补了传统箱线图在多模态分布和连续密度展示上的不足,尤其适用于大数据分析和复杂分布比较。
一、小提琴图的核心组成与数学原理
1. 基本结构
- 密度曲线:
通过核密度估计得到,左右对称的曲线宽度表示数据在对应值处的概率密度,面积总和为1。 - 内部标记:
- 中位数线(50%分位数):垂直线标记数据集的中间值。
- 箱体范围(可选):使用短粗线或矩形表示Q1-Q3(25%-75%分位数)。
- 须线(可选):显示1.5×IQR范围,或直接调整密度曲线尾部以反映数据范围。
2. 核密度估计(KDE)的数学背景
3. 与箱线图的对比
特性 | 箱线图 | 小提琴图 |
---|---|---|
分布形态展示 | 仅五数概括,无法显示多峰 | 显示完整密度曲线,包括双峰、偏态 |
数据复杂度 | 适合中小样本量对比 | 适合大样本或高密度区间分析 |
异常值检测 | 明确标记离群点 | 依赖密度尾部辨识,可能不够直观 |
可视化空间需求 | 紧凑,适合多组对比 | 需要更多横向空间以展示密度 |
二、MATLAB实现基础与进阶
1. 基础小提琴图绘制
MATLAB官方未直接提供小提琴图函数,但可通过ksdensity
与patch
手动创建:
% 调用示例
data1 = randn(1000,1)*0.8 + 2;
data2 = randn(1000,1)*1.5 + 5;
figure; hold on;
plotViolin(data1, 1, [0.2 0.6 0.8]);
plotViolin(data2, 2, [0.8 0.4 0.2]);
xlim([0.5 2.5]);
set(gca, 'XTick', [1 2], 'XTickLabel', {'Group A', 'Group B'});
ylabel('Value');
title('基础小提琴图');
function plotViolin(data, position, color)
% 核密度估计
[f, xi] = ksdensity(data);
f = f / max(f) * 0.4; % 缩放宽度
% 绘制左半部分
patch([position - f, position + fliplr(f)], ...
[xi, fliplr(xi)], color, 'FaceAlpha', 0.6, 'EdgeColor', 'none');
% 添加中位数线
median_val = median(data);
line([position - 0.05, position + 0.05], [median_val, median_val], ...
'Color', 'k', 'LineWidth', 2);
end
2. 增强型小提琴图
(1) 叠加箱线图与散点
% 绘制小提琴图基础
plotViolin(data1, 1, [0.9 0.2 0.2]);
plotViolin(data2, 2, [0.2 0.7 0.3]);
% 在每个小提琴中心添加箱线图
boxplot(data1, 'Positions', 0.9, 'Widths', 0.15, 'Colors', 'k');
boxplot(data2, 'Positions', 2.1, 'Widths', 0.15, 'Colors', 'k');
% 叠加抖动散点
jitter1 = 0.9 + (rand(size(data1)) - 0.5)*0.1;
jitter2 = 2.1 + (rand(size(data2)) - 0.5)*0.1;
scatter(jitter1, data1, 20, [0.9 0.2 0.2], 'filled', 'MarkerEdgeColor', 'k');
scatter(jitter2, data2, 20, [0.2 0.7 0.3], 'filled', 'MarkerEdgeColor', 'k');
(2) 调整带宽与核函数
% 自定义带宽和Epanechnikov核
[f, xi] = ksdensity(data1, 'Bandwidth', 0.5, 'Kernel', 'Epanechnikov');
% 小提琴图生成程序 - 高级版
clear; clc; close all;
% 生成示例数据(三种不同的分布)
rng(42); % 设置随机种子保证可重复性
data{1} = 1.5*randn(500,1) + 3; % 正态分布N(3, 1.5^2)
data{2} = 0.8*randn(300,1) - 1; % 正态分布N(-1, 0.8^2)
data{3} = 0.6*randn(400,1) + 6; % 正态分布N(6, 0.6^2)
data{4} = 0.4*randn(250,1) + 8; % 正态分布N(8, 0.4^2)
% 参数设置
positions = 1:length(data); % 小提琴位置
bandwidth = []; % 核密度估计带宽(空表示自动选择)
violinWidth = 0.6; % 小提琴最大宽度
colorMap = parula(length(data)); % 颜色映射
medianColor = [1 0.2 0.2]; % 中位数线颜色
meanSymbol = 'd'; % 均值点符号
outlierThresh = 1.5; % 离群值阈值(IQR倍数)
% 初始化图形
figure('Color','white', 'Position', [100, 100, 800, 600])
hold on;
% 为每个数据集绘制小提琴图
for k = 1:length(data)
% 核密度估计
[f, xi] = ksdensity(data{k}, 'Bandwidth', bandwidth);
f = violinWidth * f/max(f); % 归一化到小提琴宽度
% 统计量计算
q = quantile(data{k}, [0.25 0.5 0.75]);
iqr = q(3) - q(1);
lowerWhisker = max(min(data{k}), q(1) - outlierThresh*iqr);
upperWhisker = min(max(data{k}), q(3) + outlierThresh*iqr);
% 绘制小提琴主体
fill([positions(k)-f, fliplr(positions(k)+f)],...
[xi, fliplr(xi)], colorMap(k,:),...
'FaceAlpha', 0.6, 'EdgeColor', 'none',...
'LineWidth', 1.2, 'EdgeAlpha', 0.8);
% 绘制箱线图元素
rectangle('Position', [positions(k)-0.1, q(1), 0.2, q(3)-q(1)],...
'FaceColor', 'w', 'EdgeColor', 'k', 'LineWidth', 1.5);
line([positions(k)-0.1 positions(k)+0.1], [q(2) q(2)],...
'Color', medianColor, 'LineWidth', 2.5); % 中位数线
% 绘制须线
line([positions(k) positions(k)], [lowerWhisker q(1)],...
'Color', 'k', 'LineStyle', '-', 'LineWidth', 1.2);
line([positions(k) positions(k)], [q(3) upperWhisker],...
'Color', 'k', 'LineStyle', '-', 'LineWidth', 1.2);
% 绘制均值标记
meanValue = mean(data{k});
plot(positions(k), meanValue, meanSymbol,...
'MarkerSize', 10, 'MarkerFaceColor', 'w',...
'MarkerEdgeColor', 'k', 'LineWidth', 1.5);
% 标记离群值
outliers = data{k}(data{k} < lowerWhisker | data{k} > upperWhisker);
plot(positions(k)*ones(size(outliers)), outliers, 'ko',...
'MarkerSize', 4, 'LineWidth', 1);
end
% 图形美化
ax = gca;
ax.XTick = positions;
ax.XTickLabel = {'Group A', 'Group B', 'Group C', 'Group D'};
ax.TickLength = [0 0];
ax.FontName = 'Arial';
ax.FontSize = 12;
ax.LineWidth = 1.5;
grid on;
xlim([0.5 length(data)+0.5])
ylabel('Value', 'FontSize',14, 'FontWeight','bold')
title('Advanced Violin Plot with Statistical Markers',...
'FontSize',16, 'FontWeight','bold')
% 添加图例
h = zeros(3,1);
h(1) = plot(NaN,NaN,'-', 'Color', colorMap(1,:), 'LineWidth',5);
h(2) = plot(NaN,NaN,'-', 'Color', medianColor, 'LineWidth',2);
h(3) = plot(NaN,NaN,meanSymbol, 'MarkerFaceColor','w','MarkerEdgeColor','k');
legend(h, {'Distribution', 'Median', 'Mean'},...
'Location','northwest', 'FontSize',12)
% 添加色标
colormap(colorMap)
c = colorbar;
c.Ticks = [];
c.Label.String = 'Group Density Distribution';
c.Label.FontSize = 12;
% 附加说明
annotation('textbox', [0.15 0.82 0.2 0.1],...
'String', {'• Whiskers show 1.5IQR range',...
'• Circles denote outliers'},...
'FitBoxToText','on', 'EdgeColor','none',...
'FontSize',10, 'Color',[0.3 0.3 0.3])
% 调整布局
set(gcf, 'Color','w')
hold off