基础设置
首先进行基础的全局设置,无论是绘制什么图形,这些代码都是必不可少的
import matplotlib.pyplot as plt
from cycler import cycler
import numpy as np
# 进行必要的全局设置,让matplotlilb支持中文
plt.rcParams.update(
{
"font.sans-serif": "SimSun",
"axes.unicode_minus": False,
}
)
绘制平行柱状图
def _grouped_bar_loc(bar_width, legends_count, data_len):
"""bar_width:1月的苹果,橘子,香蕉三个柱子的总宽度,范围0~1
series是苹果,橘子,香蕉,legends_count=3
data_len:data是苹果的1,2,3,4,5月的销售数据,data_len=5
返回:np.array[所有苹果柱子的位置,所有橘子柱子的位置,所有香蕉柱子的位置]"""
# 即使本函数的思想要用于其他地方,start_num也不要随意修改
start_num = (
-(legends_count / 2 - 0.5) * (bar_width / legends_count)
if legends_count % 2 == 0
else -(legends_count // 2) * (bar_width / legends_count)
)
# first_bar_loc是苹果,橘子,香蕉的1月柱子的位置
first_bar_loc = np.linspace(start_num, -start_num, legends_count)
# https://numpy.org/doc/stable/reference/generated/numpy.broadcast_to.html
return np.broadcast_to(
first_bar_loc, (data_len, legends_count)
).transpose() + np.broadcast_to(np.arange(data_len), (legends_count, data_len))
def grouped_bar(axe, data, xticklabels, bar_width):
"data是dict时本函数会对legends重新排序,若需保持顺序请将data转为dataframe并reindex"
if isinstance(data, dict):
grouped = sorted(data.items(), key=lambda i: i[0])
legends = [i[0] for i in grouped]
arrays = [i[1] for i in grouped]
elif isinstance(data, pd.DataFrame):
legends = data.columns
arrays = data.to_numpy().transpose()
# ========
legends_count = len(legends)
data_len = len(xticklabels)
single_bar_width = bar_width / legends_count
bar_loc = _grouped_bar_loc(bar_width, legends_count, data_len)
for i in range(legends_count):
axe.bar(bar_loc[i], arrays[i], width=single_bar_width, label=legends[i])
axe.set(xlim=(-0.5, data_len - 0.5))
axe.set_xticks(np.arange(data_len), labels=xticklabels)
axe.xaxis.set_major_locator(plt.MultipleLocator(1))
绘制堆叠柱状图
def _stacked_bar_loc(data: np.ndarray):
"""data:np.array[[苹果的1,2,3,4,5月的销售数据],[橘子的1,2,3,4,5月的销售数据],……]
返回:[[苹果的1,2,3,4,5月的bottom],[橘子的1,2,3,4,5月的bottom],……]"""
# https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.bar.html
# https://numpy.org/doc/stable/reference/generated/numpy.ufunc.accumulate.html
return np.add.accumulate(data, axis=0) - data
def stacked_bar(axe, data, xticklabels, bar_width):
"data是dict时本函数会对legends重新排序,若需保持顺序请将data转为dataframe并reindex"
if isinstance(data, dict):
grouped = sorted(data.items(), key=lambda i: i[0])
legends = [i[0] for i in grouped]
npdata = np.vstack([i[1] for i in grouped])
elif isinstance(data, pd.DataFrame):
npdata = data.to_numpy().transpose()
legends = data.columns
# ========
bar_loc = _stacked_bar_loc(npdata)
data_len = len(xticklabels)
for i in range(len(legends)):
axe.bar(np.arange(data_len), npdata[i], bar_width, bar_loc[i], label=legends[i])
axe.set(xlim=(-0.5, data_len - 0.5))
axe.set_xticks(np.arange(data_len), labels=xticklabels)
axe.xaxis.set_major_locator(plt.MultipleLocator(1))
结果展示
# ========首先配置测试数据========
dict数据 = {
"苹果": np.array([4, 7, 3, 6, 2]),
"橘子": np.array([8, 4, 2, 6, 1]),
"香蕉": [3, 4, 5, 2, 5],
}
df数据 = pd.DataFrame(
{"苹果": [4, 7, 3, 6, 2], "橘子": [8, 4, 2, 6, 1], "香蕉": [3, 4, 5, 2, 5]}
)
月份 = ["1月", "2月", "3月", "4月", "5月"]
# ========开始绘图========
fig, axes = plt.subplots(2, 2, figsize=(12, 8))
#用四种方式绘制复杂柱状图
stacked_bar(axes[0, 0], df数据, 月份, 0.4)
grouped_bar(axes[0, 1], dict数据, 月份, 0.75)
stacked_bar(axes[1, 0], dict数据, 月份, 0.6)
grouped_bar(axes[1, 1], df数据, 月份, 0.6)
# 设置图例,把图例放在外面
# https://matplotlib.org/stable/api/_as_gen/matplotlib.axes.Axes.legend.html
axes[0, 0].legend(bbox_to_anchor=(1, 1), loc="lower right", title="df数据")
axes[0, 1].legend(bbox_to_anchor=(1, 1), loc="lower right", title="dict数据")
axes[1, 0].legend(bbox_to_anchor=(1, -0.07), loc="upper right", title="dict数据")
axes[1, 1].legend(bbox_to_anchor=(1, -0.07), loc="upper right", title="df数据")
# 展示并保存图片
fig.tight_layout()
plt.show()
生成的图片如下