第一次看到山脊线图时,我就被它独特的视觉效果吸引了。这种由多条密度曲线堆叠而成的图表,就像起伏的山脉一样,能够直观展示不同组别数据的分布差异。在实际工作中,我们经常需要比较不同类别数据的分布情况,比如不同产品的用户评分分布、不同地区的温度变化趋势等。传统的直方图或箱线图虽然也能完成这个任务,但当组别较多时,图表就会变得拥挤不堪,难以阅读。
山脊线图(Ridgeline Plots)完美解决了这个问题。它通过垂直堆叠的方式,让每个组别的密度曲线部分重叠,既节省了空间,又保持了可读性。这种图表最早由Claus Wilke在2017年重新推广,虽然概念并不新颖,但在数据科学领域却越来越受欢迎。我特别喜欢用它来分析时间序列数据的分布变化,比如一年中每天的温度分布,或者用户行为在不同时段的差异。
使用Python绘制山脊线图非常简单,主要依赖JoyPy这个轻量级库。它基于matplotlib和pandas构建,专为创建山脊线图而设计。下面我们就从安装开始,一步步掌握这个强大的可视化工具。
在开始之前,我们需要确保环境准备就绪。JoyPy的安装非常简单,只需要一行pip命令:
bash复制pip install joypy==0.2.6
我推荐使用0.2.6版本,因为这是目前最稳定的发布版。安装时可能会遇到依赖冲突的问题,特别是如果你已经安装了较新版本的matplotlib。这时可以创建一个干净的虚拟环境专门用于数据可视化项目:
bash复制python -m venv vis_env
source vis_env/bin/activate # Linux/Mac
vis_env\Scripts\activate # Windows
pip install joypy==0.2.6 pandas matplotlib
为了演示山脊线图的基本用法,我们使用经典的鸢尾花数据集。这个数据集包含了三种鸢尾花(Setosa、Versicolor和Virginica)的花萼和花瓣测量数据,非常适合用来练习数据可视化。
python复制import pandas as pd
from sklearn.datasets import load_iris
import matplotlib.pyplot as plt
from joypy import joyplot
# 加载鸢尾花数据集
iris = load_iris()
df = pd.DataFrame(iris.data, columns=iris.feature_names)
df['Name'] = iris.target_names[iris.target]
print("特征名称:", iris.feature_names)
print("标签种类:", iris.target_names)
print("\n各类样本数量:")
print(df['Name'].value_counts())
运行这段代码,你会看到数据集包含四个特征(花萼长度、花萼宽度、花瓣长度、花瓣宽度)和三个类别,每个类别正好50个样本,非常均衡。
现在让我们绘制第一个山脊线图,展示三种鸢尾花在四个特征上的分布差异:
python复制plt.figure(figsize=(10, 6), dpi=150)
fig, axes = joyplot(
data=df,
by='Name',
column=['sepal length (cm)', 'sepal width (cm)',
'petal length (cm)', 'petal width (cm)'],
xlabelsize=12,
ylabelsize=12,
grid=True,
hist=False,
color=['#FF0066', '#9400D3', '#002FA7', '#FFB900'],
legend=True,
title='鸢尾花特征分布山脊图',
alpha=0.8
)
plt.tight_layout()
plt.savefig('iris_ridge.png', dpi=300, bbox_inches='tight')
plt.show()
这段代码会产生一个漂亮的山脊线图,展示了三种鸢尾花在四个特征上的分布情况。每个"山脊"代表一个特征在某个类别下的分布密度,不同颜色代表不同类别。
让我们仔细看看joyplot函数的主要参数:
data:接受DataFrame格式的数据by:分组依据的列名,这里是'Name'列column:要绘制分布的数值列列表xlabelsize/ylabelsize:坐标轴标签大小grid:是否显示网格线hist:是否显示直方图(我们使用密度曲线,所以设为False)color:颜色列表,控制不同曲线的颜色legend:是否显示图例title:图表标题alpha:透明度,影响曲线的视觉重叠效果在实际项目中,我经常调整alpha值来优化重叠区域的显示效果。0.8左右的透明度通常能取得不错的平衡,既能看到重叠部分,又能区分不同曲线。
默认生成的山脊线图可能不够美观,我们可以通过多种方式进行定制。比如修改字体大小、调整间距、自定义颜色等:
python复制plt.rcParams.update({
'font.size': 10,
'axes.titlesize': 14,
'axes.labelsize': 12,
'xtick.labelsize': 10,
'ytick.labelsize': 10
})
fig, axes = joyplot(
data=df,
by='Name',
column=['sepal length (cm)', 'sepal width (cm)'],
figsize=(10, 5),
colormap=plt.cm.plasma, # 使用matplotlib的colormap
overlap=2, # 控制重叠程度
linecolor='white', # 曲线边缘颜色
linewidth=0.5, # 曲线边缘宽度
background='#333333', # 背景色
title='定制化山脊线图示例'
)
plt.savefig('custom_ridge.png', dpi=300, facecolor='#333333')
这个例子展示了几个有用的定制选项:
colormap:使用matplotlib内置的颜色映射overlap:控制曲线重叠程度,值越大重叠越多linecolor和linewidth:设置曲线边缘样式background:改变背景颜色在实际项目中,数据往往不像鸢尾花数据集这么干净。让我们看一个处理真实数据的例子。假设我们有一个电商网站的用户购买数据,包含不同用户组的购买金额分布:
python复制import numpy as np
# 生成模拟数据
np.random.seed(42)
groups = ['新用户', '普通用户', 'VIP用户', 'SVIP用户']
data = {
'用户组': np.repeat(groups, 500),
'购买金额': np.concatenate([
np.random.exponential(50, 500),
np.random.normal(200, 50, 500),
np.random.normal(500, 100, 500),
np.random.lognormal(6, 0.5, 500)
])
}
df_purchase = pd.DataFrame(data)
# 过滤异常值
df_purchase = df_purchase[df_purchase['购买金额'] < 2000]
# 绘制山脊图
plt.figure(figsize=(10, 6))
fig, axes = joyplot(
data=df_purchase,
by='用户组',
column='购买金额',
bins=30,
x_range=(0, 2000),
title='不同用户组购买金额分布',
fade=True # 曲线末端渐隐效果
)
plt.xlabel('购买金额(元)')
plt.grid(True, linestyle='--', alpha=0.6)
plt.show()
这个例子展示了几个处理真实数据的技巧:
bins参数:控制密度估计的精细程度x_range:统一x轴范围,便于比较fade:添加渐隐效果,提升视觉体验绘制出漂亮的山脊线图只是第一步,更重要的是能够从中提取有价值的信息。以我们的鸢尾花示例为例,通过观察山脊线图,可以发现:
在实际业务场景中,我曾用山脊线图分析不同营销渠道带来的用户质量差异。通过观察各渠道用户关键行为指标的分布,我们能够识别出哪些渠道带来了更多高质量用户,从而优化营销预算分配。
在使用山脊线图时,有几个常见错误需要注意:
组别过多:当组别超过10个时,图表会变得难以阅读。建议在这种情况下考虑其他可视化方式,或者对数据进行分组。
不恰当的y轴缩放:山脊线图的y轴是人为堆叠的,不代表实际数值大小。比较时应该关注分布形状而非绝对高度。
忽略数据预处理:山脊线图对异常值敏感,绘制前应该检查并处理异常值。
过度依赖默认参数:joyplot的默认参数可能不适合你的数据,应该根据实际情况调整bins、range等参数。
记得有一次我分析用户活跃时长分布时,没有设置合理的x_range,导致图表被少数极端活跃用户扭曲,得出了完全错误的结论。后来通过限制x轴范围,才发现了真实的分布模式。