在实际数据分析与汇报中,常常需要在一张图表内同时展示数量与趋势——例如工单数量(柱形图)与解决率(折线图)。若分两张图,读者需来回对照;若强行共用同一纵轴,量级差异大的数据会使折线被压扁。Python 的 matplotlib 库配合 pandas 读取 Excel 数据,可以快速生成可复用的组合图表(Combo Chart),并直接输出为 PNG 图片用于报告。本文从基本同轴组合图入手,重点讲解双 y 轴组合图的实现,并提供一个从 Excel 一键读取、自动出图的脚本模板,最后总结常见坑与验证标准。
- # 1. 同坐标轴组合图:适合量级相近的数据
- import matplotlib.pyplot as plt
- import numpy as np
- plt.rcParams["font.sans-serif"] = ["SimHei"]
- plt.rcParams["axes.unicode_minus"] = False
- months = ["1月", "2月", "3月", "4月", "5月", "6月"]
- tickets = [120, 98, 135, 110, 150, 160] # 柱形:工单数量
- avg_time = [80, 75, 90, 82, 88, 95] # 折线:处理效率评分
- x = np.arange(len(months))
- plt.figure(figsize=(9, 6))
- plt.bar(x, tickets, width=0.6, label="工单数量", alpha=0.75)
- plt.plot(x, avg_time, marker="o", linewidth=2, label="处理效率评分")
- plt.xticks(x, months)
- plt.title("组合图:工单数量 + 处理效率评分")
- plt.xlabel("月份")
- plt.ylabel("数值")
- plt.grid(axis="y", linestyle="--", alpha=0.4)
- plt.legend()
- plt.tight_layout()
- plt.show()
复制代码
上述代码中,柱形图与折线图共享同一个 y 轴。只有当两组数据的数量级相近(如 80~160 与 80~95)时,这种图表才能清晰传达信息。若一组数据为 0~10000,另一组为 0~100,折线会被压成一条几乎无起伏的线,此时应改用双 y 轴。
- # 2. 双 y 轴组合图:解决单位与量级差异
- import matplotlib.pyplot as plt
- import numpy as np
- plt.rcParams["font.sans-serif"] = ["SimHei"]
- plt.rcParams["axes.unicode_minus"] = False
- months = ["1月", "2月", "3月", "4月", "5月", "6月"]
- tickets = [120, 98, 135, 110, 150, 160] # 柱形:工单数量
- solve_rate = [92, 88, 95, 90, 96, 97] # 折线:解决率(%)
- x = np.arange(len(months))
- fig, ax1 = plt.subplots(figsize=(9, 6))
- # 左轴:柱形图
- ax1.bar(x, tickets, width=0.6, alpha=0.75, label="工单数量")
- ax1.set_xlabel("月份")
- ax1.set_ylabel("工单数量(单)")
- ax1.set_xticks(x)
- ax1.set_xticklabels(months)
- ax1.grid(axis="y", linestyle="--", alpha=0.4)
- # 右轴:折线图
- ax2 = ax1.twinx()
- ax2.plot(x, solve_rate, marker="o", linewidth=2, linestyle="--", label="解决率(%)")
- ax2.set_ylabel("解决率(%)")
- ax2.set_ylim(0, 100) # 百分比指标锁定范围
- # 合并图例(重要:双轴需从两个对象分别取 handles 和 labels)
- h1, l1 = ax1.get_legend_handles_labels()
- h2, l2 = ax2.get_legend_handles_labels()
- ax1.legend(h1 + h2, l1 + l2, loc="upper left")
- plt.title("组合图:工单数量(柱) + 解决率(折线)")
- plt.tight_layout()
- plt.show()
复制代码
使用 ax1.twinx() 创建共享 x 轴的第二个 y 轴。左轴承载数量,右轴承载百分比。关键点:右轴必须显式设定 y 轴范围(set_ylim(0, 100)),否则 matplotlib 自动缩放会夸大微小波动。另外,图例必须合并两个 axes 的 handles 和 labels,否则只显示柱形图的图例。
- # 3. 企业可交付版:从 Excel 读取数据,一键生成组合图并保存 PNG
- import pandas as pd
- import numpy as np
- import matplotlib.pyplot as plt
- from pathlib import Path
- plt.rcParams["font.sans-serif"] = ["SimHei"]
- plt.rcParams["axes.unicode_minus"] = False
- def excel_to_combo_chart(
- xlsx_path: str,
- sheet: str,
- x_col: str,
- bar_col: str,
- line_col: str,
- out_png: str = "out/combo.jpg",
- line_is_percent: bool = True
- ) -> str:
- """
- 从 Excel 读取数据生成组合图(柱形+折线,双y轴)并保存为PNG
- :param xlsx_path: Excel文件路径
- :param sheet: 工作表名
- :param x_col: 用作x轴标签的列名(如"月份")
- :param bar_col: 柱形图数据列名
- :param line_col: 折线图数据列名
- :param out_png: 输出图片路径
- :param line_is_percent: 折线数据是否为百分比(如果是则锁定右轴0-100)
- :return: 输出图片路径
- """
- df = pd.read_excel(xlsx_path, sheet_name=sheet)
- x_labels = df[x_col].astype(str).tolist()
- bar = pd.to_numeric(df[bar_col], errors="coerce").fillna(0).tolist()
- line = pd.to_numeric(df[line_col], errors="coerce").fillna(0).tolist()
- x = np.arange(len(x_labels))
- fig, ax1 = plt.subplots(figsize=(10, 6))
- ax1.bar(x, bar, width=0.6, alpha=0.75, label=bar_col)
- ax1.set_xlabel(x_col)
- ax1.set_ylabel(bar_col)
- ax1.set_xticks(x)
- ax1.set_xticklabels(x_labels)
- ax1.grid(axis="y", linestyle="--", alpha=0.4)
- # 柱形图数据标签
- for i, v in enumerate(bar):
- ax1.text(i, v, str(int(v)), ha="center", va="bottom")
- ax2 = ax1.twinx()
- ax2.plot(x, line, marker="o", linewidth=2, linestyle="--", label=line_col)
- ax2.set_ylabel(line_col)
- if line_is_percent:
- ax2.set_ylim(0, 100)
- h1, l1 = ax1.get_legend_handles_labels()
- h2, l2 = ax2.get_legend_handles_labels()
- ax1.legend(h1 + h2, l1 + l2, loc="upper left")
- plt.title(f"组合图:{bar_col}(柱) + {line_col}(折线)")
- plt.tight_layout()
- out_png = Path(out_png)
- out_png.parent.mkdir(parents=True, exist_ok=True)
- plt.savefig(out_png, dpi=200)
- plt.close()
- return str(out_png)
- if __name__ == "__main__":
- png = excel_to_combo_chart(
- xlsx_path="report.xlsx",
- sheet="数据",
- x_col="月份",
- bar_col="工单数量",
- line_col="解决率",
- out_png="out/组合图_工单数量_解决率.jpg",
- line_is_percent=True
- )
- print("已输出:", png)
复制代码
此函数为完整自动化流程:pandas 读取 Excel 指定列,清洗数值(非数值填充为0),建立双y轴,生成图片并保存。使用时只需维护 report.xlsx 内的数据,每次运行即可获得最新组合图。
常见坑与验证要点:
1. 右轴范围:百分比指标务必锁定 0~100,避免自动缩放造成视觉误导。
2. 单位标注:左右轴必须分别写明单位(如“单”、“%”),否则图表失去可读性。
3. 图例合并:双轴图表需从 ax1 和 ax2 分别获取 handles 和 labels 后合并,再调用一次 legend(),否则折线图例可能缺失。
4. 数据标签:柱形图顶部的数据标签不宜过多,避免遮挡。
5. 验收标准:生成 PNG 后检查标题是否说明主题、左右轴单位是否明确、图例是否完整、坐标范围是否合理、数据标签是否影响阅读。真正的验证不是“图有没有生成”,而是“图能否支持你想表达的判断”。
组合图不是为了让图表更复杂,而是在一张图内同时呈现“量 + 趋势 + 质量”。理解了业务关联性(如工单量与解决率是否共同增长),才能正确选择使用同轴还是双轴。上述代码已可直接复制到 Python 环境中运行,适用于桌面支持月报、销售数据分析、KPI 看板等场景。 |