南丁格尔玫瑰图(Nightingale Rose Chart)是数据可视化史上的经典图表之一,由现代护理学奠基人弗洛伦斯·南丁格尔(Florence Nightingale)于1858年首创。南丁格尔玫瑰图作为数据可视化史上的里程碑,不仅改变了19世纪的医疗政策,至今仍启发着数据叙事(Data Storytelling)的艺术。在当今精准分析需求下,其价值更多体现在:如何用数据讲好一个改变决策的故事。现代数据工作者可在保留其视觉冲击力的同时,通过交互设计和多维补充信息,赋予这一经典形式新的生命力。
下面是用python绘制南丁格尔图
# -*- coding: utf-8 -*-
"""
南丁格尔玫瑰图可视化案例
使用Matplotlib实现极坐标系下的堆叠玫瑰图
"""
# 导入必要库
import matplotlib.pyplot as plt # 可视化库
import numpy as np # 数值计算库
import pandas as pd # 数据处理库
# ================== 中文字体配置 ==================
# 设置中文字体显示(根据操作系统选择)
plt.rcParams['font.sans-serif'] = ['SimHei'] # Windows系统中文显示
# plt.rcParams['font.sans-serif'] = ['Arial Unicode MS'] # Mac系统中文显示
plt.rcParams['axes.unicode_minus'] = False # 解决负号显示为方块的问题
# ================== 数据准备 ==================
# 创建模拟数据(仿照克里米亚战争医疗数据格式)
months = ['2023-04', '2023-05', '2023-06', '2023-07', '2023-08', '2023-09'] # 月份数据
categories = {
'战斗负伤': [25, 42, 35, 28, 50, 38], # 战斗相关伤亡数据
'疾病感染': [120, 135, 162, 148, 200, 175], # 疾病导致的伤亡数据
'其他原因': [15, 22, 18, 25, 30, 28] # 其他因素导致的伤亡数据
}
# 转换为Pandas DataFrame格式
df = pd.DataFrame(categories, index=months)
df['月份'] = df.index # 添加月份列
# ================== 极坐标参数设置 ==================
N = len(df) # 数据点数量(月份数量)
theta = np.linspace(0.0, 2 * np.pi, N, endpoint=False) # 将圆周分为N等分,生成角度值
width = 2 * np.pi / N # 计算每个扇形的宽度(弧度)
# ================== 可视化配置 ==================
# 颜色设置(模仿南丁格尔原始配色风格)
colors = ['#FFB3BA', '#BAFFC9', '#BAE1FF'] # 粉红/浅绿/浅蓝配色方案
# 创建画布和极坐标系
fig = plt.figure(figsize=(10, 10)) # 创建10x10英寸的画布
ax = fig.add_subplot(111, polar=True) # 添加极坐标系子图
# ================== 绘制堆叠玫瑰图 ==================
bottom = np.zeros(N) # 初始化堆叠条形图的底部位置
for i, (category, color) in enumerate(zip(categories.keys(), colors)):
# 获取当前分类的数据
values = df[category].values
# 绘制极坐标堆叠条形图
bars = ax.bar(
x=theta, # 角度位置
height=values, # 条形高度(数据值)
width=width, # 条形宽度
bottom=bottom, # 堆叠起始高度
color=color, # 填充颜色
edgecolor='white', # 边框颜色
linewidth=0.5, # 边框粗细
label=category # 图例标签
)
bottom += values # 更新堆叠基线位置
# ================== 添加月份标签 ==================
for month, angle, value in zip(months, theta, bottom):
rotation = np.degrees(angle + width / 2) # 计算标签旋转角度(转换为角度制)
ax.text(
angle + width / 2, # 角度位置(中心对齐)
bottom.max() * 1.1, # 半径位置(超出最大半径10%)
month, # 显示的月份文本
ha='center', # 水平居中
va='center', # 垂直居中
# 自动调整标签旋转方向(保证文字可读)
rotation=rotation - 90 if rotation < 180 else rotation + 90,
fontsize=10 # 字体大小
)
# ================== 极坐标系参数调整 ==================
ax.set_theta_offset(np.pi / 2) # 设置起始角度(从正上方开始)
ax.set_theta_direction(-1) # 设置方向为顺时针
ax.set_rlabel_position(0) # 设置半径标签位置(0度方向)
plt.ylim(0, bottom.max() * 1.2) # 设置半径轴显示范围(留出20%空白)
# ================== 添加图例和标题 ==================
plt.legend(
loc='upper right', # 图例主位置
bbox_to_anchor=(1.15, 1.15), # 图例框偏移量(右侧外对齐)
frameon=False # 取消图例框
)
plt.title('医疗情况分析 - 南丁格尔玫瑰图\n(模拟克里米亚战争医疗数据)',
pad=40, # 标题与图的间距
fontsize=14) # 标题字号
# ================== 美化设置 ==================
ax.spines['polar'].set_visible(False) # 隐藏极坐标的边界线
ax.grid(False) # 隐藏极坐标网格线
# ================== 显示图表 ==================
plt.tight_layout() # 自动调整子图参数
plt.show() # 显示最终图表
结果如下: