棒棒糖图:当条形图遇上极简美学

棒棒糖图(Lollipop Chart)可以看作是条形图的一种"轻盈版"变体:它用一根从基准线延伸出来的"棒",并在末端以一个"糖"(圆点)来表示数值,取代了传统的矩形条。

这种图表传达的信息与条形图是一样的,但它更注重突出数据点的位置,整体看起来更加清新、现代。

今天,我们一起探索棒棒糖图的优势,并通过代码实现,亲手打造一个属于自己的棒棒糖图!

设计原理

设计棒棒糖图的初衷有两个方面:

首先,通过减少图形中的"墨水"使用量,在面对大量类别或数值普遍较高的情况下,避免了条形图过于密集可能带来的视觉压迫感杂乱无章的感觉;

其次,这样的设计能够更好地引导观察者的注意力集中在各个数据点的具体位置及其之间的差异上,非常适合用来进行排名或者对比分析。

棒棒糖图条形图在功能上是等价的,也并不总是优于条形图。

当处理大量类别、条柱非常高且相互之间距离较近的数据时,棒棒糖图会显得更加易于阅读理解;

然而,在需要强调绝对数量或是希望获得最为直观长度比较的情况下,条形图依然是更好的选择。

实现原理

棒棒糖图matplotlib库中没有直接对应的类。

不过,它的实现原理非常简单,通过组合使用 matplotlib 中的两个基本绘图功能就可以实现:

  1. 绘制线条 (plt.vlines):这是构成棒棒糖"棍子"部分的关键。

plt.vlines 函数用于在图表上绘制垂直线段。通过指定每个数据点的 x 坐标、线条的起始点(通常是 0)和结束点(即对应数据的 y 值),就可以画出从 x 轴延伸到数据值的线条。

  1. 绘制圆点 (plt.scatter):这是构成棒棒糖"糖"部分的关键。

plt.scatter 函数用于绘制散点图。通过将每个数据点的 x 坐标和 y 坐标(即数据值)作为参数传入,就可以在每条线的顶端绘制一个圆点。

总的来说,实现原理就是:用线条表示数值的大小,用圆点强调数值的终点位置,两者结合就形成了视觉上类似棒棒糖的图表。

这种组合方式使得图表比实心的条形图更简洁,同时又能清晰地传达数据信息。

下一节的示例中,将会演示如何使用matplotlib来绘制棒棒糖图

应用示例

接下来,让我们通过实际的对比示例,直观地感受棒棒糖图与传统条形图的不同表现。

适合棒棒糖图的场景

python 复制代码
# 创建更多类别的测试数据
# 模拟不同月份中每天的某个指标(例如:每日平均步数,单位:千步)
days = [f"Day {i}" for i in range(1, 21)]  # 生成 20 天的数据
np.random.seed(42)  # 设置随机种子,确保每次运行结果一致
# 生成 1 到 5 之间的随机数值作为示例数据
values = np.round(np.random.uniform(1.0, 5.0, size=len(days)), 1)

# --- 创建子图 ---
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(12, 10))  # 调整为垂直布局,方便比较

# --- 绘制条形图 (上图) ---
bars = ax1.bar(days, values, color="skyblue", edgecolor="navy", linewidth=0.7)
ax1.set_title("条形图 (Bar Chart) - 每日步数", fontsize=14)
ax1.set_ylabel("数值 (千步)")
# ax1.set_xlabel('日期') # x轴标签已在下方图中
ax1.grid(axis="y", linestyle="--", alpha=0.7)
# 在条形图上添加数值标签
for bar, val in zip(bars, values):
    height = bar.get_height()
    ax1.text(
        bar.get_x() + bar.get_width() / 2.0,
        height + 0.05,
        f"{val}",
        ha="center",
        va="bottom",
        fontsize=8,
    )  # 标签旋转90度节省空间

# --- 绘制棒棒糖图 (下图) ---
# 1. 绘制线条
ax2.vlines(x=range(len(days)), ymin=0, ymax=values, color="navy", linewidth=2)
# 2. 在线条顶部绘制圆点
ax2.scatter(x=range(len(days)), y=values, color="red", s=50, zorder=3)
# 3. 添加数值标签
for i, val in enumerate(values):
    ax2.text(i, val + 0.1, f"{val}", ha="center", va="bottom", fontsize=8)

ax2.set_title("棒棒糖图 (Lollipop Chart) - 每日步数", fontsize=14)
ax2.set_ylabel("数值 (千步)")
ax2.set_xlabel("日期")
ax2.set_xticks(range(len(days)))
ax2.set_xticklabels(days, rotation=45, ha="right")  # 旋转x轴标签以便阅读
ax2.grid(axis="y", linestyle="--", alpha=0.7)
ax2.set_ylim(0, max(values) * 1.1)

# --- 显示图形 ---
plt.tight_layout()  # 调整子图间距
plt.show()

这个示例使用了20个数据类别(Day 1 到 Day 20)。

你可以看到,在条形图中,许多蓝色的条形紧密地排列在一起,视觉上显得有些拥挤。

而在棒棒糖图中,线条和圆点使得数据点之间的关系更加清晰,整体视觉效果更轻盈,更容易比较各个数值的大小和识别模式。

适合传统条形图的场景

python 复制代码
# 创建更适合条形图的测试数据
# 模拟某公司连续12个月的销售额(单位:万元)
months = [
    "1月",
    "2月",
    "3月",
    "4月",
    "5月",
    "6月",
    "7月",
    "8月",
    "9月",
    "10月",
    "11月",
    "12月",
]
# 生成有一定趋势和波动的销售额数据,例如有季节性高峰
sales = [120, 110, 135, 140, 155, 170, 185, 180, 160, 150, 145, 165]

# --- 创建子图 ---
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(16, 6))

# --- 绘制条形图 (左图) ---
bars = ax1.bar(months, sales, color="lightsteelblue", edgecolor="black", linewidth=0.7)
ax1.set_title("条形图 (Bar Chart - 月份销售额)", fontsize=14)
ax1.set_ylabel("销售额 (万元)")
ax1.set_xlabel("月份")
ax1.grid(axis="y", linestyle="--", alpha=0.7)
# 在条形图上添加数值标签
for bar, s in zip(bars, sales):
    height = bar.get_height()
    ax1.text(
        bar.get_x() + bar.get_width() / 2.0,
        height + 2,
        f"{s}",
        ha="center",
        va="bottom",
        fontsize=9,
    )

# --- 绘制棒棒糖图 (右图) ---
ax2.vlines(x=range(len(months)), ymin=0, ymax=sales, color="gray", linewidth=2)
ax2.scatter(x=range(len(months)), y=sales, color="coral", s=50, zorder=3)
for i, s in enumerate(sales):
    ax2.text(i, s + 3, f"{s}", ha="center", va="bottom", fontsize=9)

ax2.set_title("棒棒糖图 (Lollipop Chart - 月份销售额)", fontsize=14)
ax2.set_ylabel("销售额 (万元)")
ax2.set_xlabel("月份")
ax2.set_xticks(range(len(months)))
ax2.set_xticklabels(months, rotation=45)  # 旋转x轴标签以防重叠
ax2.grid(axis="y", linestyle="--", alpha=0.7)
ax2.set_ylim(0, max(sales) * 1.1)

# --- 显示图形 ---
plt.tight_layout()
plt.show()

在这个示例中,数据代表的是连续的月份,数值本身代表的是销售额,这是一个"量"的概念。

条形图的实心块能让人立刻感受到哪个月份的销售额更高,整体的分布和对比关系一目了然。

而棒棒糖图虽然也展示了数据,但线条和圆点的组合在视觉上不如实心条形那样能直接传达"量"的感觉,尤其是在数值差异不是特别巨大时,对比效果会稍逊于条形图。

总结

棒棒糖图就像数据可视化世界中的"少即是多"哲学体现。

它不是要取代传统条形图,而是为数据可视化工具箱增加了一个有价值的选项。

就像不同的画笔适合不同的绘画风格,不同的图表类型也适合不同的数据故事。

棒棒糖图的真正优势在于它改变了数据的**"讲述方式"**。

它不说:"这是所有信息,你自己找重点",而是说:"看这里,这些是关键点"。

这种焦点导向的特性,使得棒棒糖图在现代快节奏的数据沟通中越来越受欢迎。

设计可视化时,我们不妨问问自己:我想要观众首先看到什么?如果是精确的数值点和清晰的排名,那么棒棒糖图可能是你的理想选择。

相关推荐
Dxy123931021636 分钟前
Python基于BERT的上下文纠错详解
开发语言·python·bert
SiYuanFeng2 小时前
Colab复现 NanoChat:从 Tokenizer(CPU)、Base Train(CPU) 到 SFT(GPU) 的完整踩坑实录
python·colab
炸炸鱼.3 小时前
Python 操作 MySQL 数据库
android·数据库·python·adb
_深海凉_3 小时前
LeetCode热题100-颜色分类
python·算法·leetcode
AC赳赳老秦4 小时前
OpenClaw email技能:批量发送邮件、自动回复,高效处理工作邮件
运维·人工智能·python·django·自动化·deepseek·openclaw
zhaoshuzhaoshu4 小时前
Python 语法之数据结构详细解析
python
AI问答工程师4 小时前
Meta Muse Spark 的"思维压缩"到底是什么?我用 Python 复现了核心思路(附代码)
人工智能·python
zfan5205 小时前
python对Excel数据处理(1)
python·excel·pandas
小饕5 小时前
我从零搭建 RAG 学到的 10 件事
python
老歌老听老掉牙5 小时前
PyQt5+Qt Designer实战:可视化设计智能参数配置界面,告别手动布局时代!
python·qt