Python用Pandas直接绘制饼图的方法
本文详细讲解在Python中使用Pandas直接绘制饼图的实用方法,覆盖Series与DataFrame调用方式、关键参数配置、代码示例与常见问题排查。
在日常数据清洗与分析中,展示各分类在总体中的占比分布是高频需求。假设你刚刚用 Pandas 统计出季度各销售渠道的业绩汇总,需要直接输出一份清晰的渠道占比分布图。很多初学者会先提取数值再调用 Matplotlib 的底层函数,实际上 Pandas 自身内置了高度集成的图表接口,通过对 Series 或 DataFrame 对象调用 .plot.pie(),无需显式构建冗长的绘图循环,即可快速生成结构规范的饼图。
一、环境准备与最简调用:从 Series 开始
在 Pandas 中,pd.Series 是一维带标签数组,天然契合饼图“一组标签对应一组数值”的数据结构。调用 series.plot.pie() 时,Pandas 默认以索引(Index)作为每个扇区的文本标签,以对应位置的数值(Values)计算所占角度比例。
执行前请确保安装了 pandas 和 matplotlib。以下是在本地 Python 脚本或 Jupyter Notebook 中的完整可执行代码:
import pandas as pd
import matplotlib.pyplot as plt
# 配置中文字体,避免标签出现方块乱码
plt.rcParams['font.sans-serif'] = ['PingFang SC', 'Microsoft YaHei', 'SimHei', 'DejaVu Sans']
plt.rcParams['axes.unicode_minus'] = False
# 1. 构造带有分类索引的 Series 数据
channel_sales = pd.Series(
[4200, 2600, 1800, 950, 450],
index=['线上商城', '直营实体店', '电商平台加盟', '企业内购', '其他渠道']
)
# 2. 直接调用 plot.pie() 绘制饼图
ax = channel_sales.plot.pie(
figsize=(6, 6), # 保持正方形比例,防止压成椭圆
autopct='%1.1f%%', # 显示一位小数的百分比
startangle=140 # 旋转起始角度
)
# 3. 清理默认的 y 轴标签(Pandas 默认会把 Series 的名称显示在左侧)
plt.ylabel('')
plt.title('季度各销售渠道业绩分布', fontsize=14)
# 4. 展示图形
plt.show()
运行这段代码后,可以看到一个比例协调的圆形图表。Pandas 自动完成了求和、百分比换算与扇区划分。注意到代码中显式调用了 plt.ylabel(''),这是因为 Pandas 内部将饼图绘制在标准笛卡尔坐标系中,默认会将 Series 的名称作为纵轴标签输出,手动将其置空即可保持页面清爽。
二、DataFrame 绘制饼图:必须掌握的 y 参数与多子图
实际业务中,数据多以多列的 DataFrame 形式存储。如果直接执行 df.plot.pie(),程序会立刻抛出异常:ValueError: pie requires either y column or subplots=True。
这是因为 DataFrame 包含多个数值列,Pandas 无法猜测应当将哪一列作为扇区的切割依据。因此,必须通过两种明确的方式之一来执行绘图:
- 指定 y 参数:显式告知绘制哪一列数值,例如
df.plot.pie(y='销售额')。 - 开启 subplots=True:如果需要横向对比多个指标(例如第一季度与第二季度对比),Pandas 会为每个数值列分别生成一个独立的子饼图。
下面展示 DataFrame 中两种调用方式的具体代码实现:
import pandas as pd
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['PingFang SC', 'Microsoft YaHei', 'SimHei']
plt.rcParams['axes.unicode_minus'] = False
# 构建区域多季度数据表
df_sales = pd.DataFrame({
'Q1': [1200, 850, 600, 400],
'Q2': [1400, 920, 580, 510]
}, index=['华东大区', '华北大区', '华南大区', '西部大区'])
# 方式一:单图展示,必须显式传递 y 参数
df_sales.plot.pie(
y='Q1',
figsize=(6, 6),
autopct='%1.1f%%',
legend=False,
title='第一季度区域销售占比'
)
plt.ylabel('')
plt.show()
# 方式二:双季度对比,设置 subplots=True
df_sales.plot.pie(
subplots=True,
figsize=(12, 6),
autopct='%1.1f%%',
legend=False
)
plt.show()
三、核心定制参数与视觉微调
默认配置下的饼图往往信息密度低,且颜色可能过于刺眼或难以区分。通过合理运用以下几个关键参数,可以显著提升图表的信息传达效率:
在实际报表制作中,将上述参数综合应用,可以生成具备出版级质感的可视化图表:
import pandas as pd
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['PingFang SC', 'Microsoft YaHei', 'SimHei']
plt.rcParams['axes.unicode_minus'] = False
data = pd.Series([48, 24, 16, 12], index=['手机端', '桌面端', '平板端', '车机端'])
# 自定义柔和配色方案与突出特定切片
custom_colors = ['#83ADD0', '#77B9AE', '#99C2E2', '#B5D8CD']
explode_slices = [0.08, 0, 0, 0] # 仅突出占比最高的手机端
ax = data.plot.pie(
figsize=(7, 7),
colors=custom_colors,
explode=explode_slices,
autopct='%1.1f%%',
pctdistance=0.75, # 百分比数字与圆心的距离
startangle=90, # 12点钟方向作为起始点
counterclock=False, # 顺时针顺序展开
shadow=True, # 启用轻微阴影
wedgeprops={'edgecolor': 'white', 'linewidth': 1.5} # 切片白边分割
)
plt.ylabel('')
plt.title('访问终端流量占比结构', fontsize=14, pad=20)
plt.show()
四、实际应用中的高频问题与排查方法
1. 类别过多导致的标签重叠
当分类超过 7 个,或者存在大量占比不足 3% 的微小项目时,扇区过于狭窄,文本标签会重叠成一团。解决该问题的最佳实践是在绘图前进行数据清洗与归并:
# 数据清洗:将占比小于 5% 的类别合并为“其他”
threshold = 0.05
total_sum = data.sum()
is_small = (data / total_sum) < threshold
# 重新组装数据
clean_data = data[~is_small].copy()
clean_data['其他'] = data[is_small].sum()
# 再进行绘图
clean_data.plot.pie(autopct='%1.1f%%', figsize=(6, 6))
plt.ylabel('')
plt.show()
2. 数据包含负数或空值
饼图的本质是将总和映射到 360 度圆周上。如果数据集中包含负数,Matplotlib 会直接抛出 ValueError;若包含 NaN,切片占比计算将出现异常。在绘图前应始终执行过滤:
clean_series = raw_series.dropna()
clean_series = clean_series[clean_series >= 0]
3. 椭圆变形与长宽比失衡
如果在 Notebook 中不加参数绘制,由于默认画布比例多为长方形(如 8×6),饼图可能被横向拉伸变形成椭圆。确保调用 figsize=(6, 6),或者在绘图后补充 plt.axis('equal'),强制锁定坐标系为等比例正圆。
五、总结
用 Pandas 直接绘制饼图的核心要领可以概括为三个要点:
- 数据形态决定语法:单维 Series 直接
s.plot.pie();二维 DataFrame 必须显式传参df.plot.pie(y='列名')或配置subplots=True。 - 图表美观依赖比例与标签:始终配置
figsize=(6, 6)或等比画布,通过autopct='%1.1f%%'标注数值,并用plt.ylabel('')清理自动生成的纵轴名。 - 注重数据适读性:在制图前过滤负数与空值,当分类过多时主动合并长尾数据为“其他”,避免扇区过窄造成视觉噪点。
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









