分享一个自己写的柱状图绘制函数,可用来绘制横向的多柱状图、堆积柱状图,纵向的多柱状图、堆积柱状图。便于我们方便快捷的绘制相应的柱状图。该函数参数不多,只用于观察数据形式是足够的,若要绘制更加精美的柱状图,大家可自行调整。
欢迎关注本人同名公众号--交通数据探索师
import random
import matplotlib.pyplot as plt
import numpy as np
def generate_colors(num):
"""随机生成num个不同的颜色"""
random.seed(123)
colors = []
while len(colors) <= num:
color = '#{:06x}'.format(random.randint(0, 0xFFFFFF))
if color not in colors:
colors.append(color)
return colors
def bars(data, cols, labels, kind='s', title='', x_label='', y_label='', width=0.4, grid=False, x_rotation=0,
y_rotation=0, legend=False, filename=''):
"""绘制柱状图 包括双柱状图等多柱状图 横竖累积柱状图
demo: bars(data, cols=['a', 'b', 'c', 'd', 'e'], labels=['b', 'c', 'd', 'e'], width=0.2, filename='l1')
cols中第一个元素对应x轴, 其余元素对应y轴
kind: 绘制柱状图的种类 {h: 横柱状图, s: 竖柱状图, l1: 竖累积柱状图, l2: 横累积柱状图}
title: 标题
x_label: x轴标签
y_label: y轴标签
width: 柱子宽度
grid: 是否显示网格
x_rotation: x轴标签旋转角度
legend: 是否显示图例
filename: 保存的文件名
"""
# 解决中文乱码问题,并设置字体
plt.rcParams['axes.unicode_minus'] = False
plt.rcParams['font.family'] = ['SimHei']
plt.rcParams['font.sans-serif'] = ['SimHei']
fig, ax = plt.subplots(figsize=(6, 2.5), dpi=300)
x_ticks = np.arange(data[cols[0]].nunique())
if len(cols[1:]) > 4:
bar_colors = generate_colors(len(cols) - 1)
else:
# 设置好的四个颜色 如果柱子个数<=4就用这四个颜色中的几个
bar_colors = ['#63b5ef', '#73de94', '#ffce7b', '#ff948c']
if (len(cols) - 1) % 2 == 0: # 偶数根柱子
center = (len(cols) - 1) / 2
for r, col in enumerate(cols[1:]):
if kind == 's':
ax.bar([x + (r - center) * width + width / 2 for x in x_ticks], data[col], lw=0.4,
color=bar_colors[r], edgecolor="k", label=labels[r], width=width)
plt.xticks(x_ticks, labels=data[cols[0]].unique())
plt.xlabel(x_label, fontsize=5)
plt.ylabel(y_label, fontsize=5)
elif kind == 'h':
ax.barh([x + (r - center) * width + width / 2 for x in x_ticks], data[col], lw=0.4,
color=bar_colors[r], edgecolor="k", label=labels[r], height=width)
plt.yticks(x_ticks, labels=data[cols[0]].unique())
plt.xlabel(y_label, fontsize=5)
plt.ylabel(x_label, fontsize=5)
elif kind == 'l1':
# r==0表示是最下面那根柱子, 则bottom=0 否则==前面几列的和
bottom = 0 if r == 0 else data[cols[1:][:r]].sum(axis=1)
ax.bar(x_ticks, data[col], bottom=bottom, lw=0.4,
color=bar_colors[r], edgecolor="k", label=labels[r], width=width)
plt.xticks(x_ticks, labels=data[cols[0]].unique())
plt.xlabel(x_label, fontsize=5)
plt.ylabel(y_label, fontsize=5)
elif kind == 'l2':
bottom = 0 if r == 0 else data[cols[1:][:r]].sum(axis=1)
ax.barh(x_ticks, data[col], left=bottom, lw=0.4,
color=bar_colors[r], edgecolor="k", label=labels[r], height=width)
plt.yticks(x_ticks, labels=data[cols[0]].unique())
plt.xlabel(y_label, fontsize=5)
plt.ylabel(x_label, fontsize=5)
else:
print('kind参数输入错误: {h: 横柱状图, s: 竖柱状图, l1: 竖累积柱状图, l2: 横累积柱状图}')
else:
center = (len(cols) - 1) // 2
for r, col in enumerate(cols[1:]):
if kind == 's':
ax.bar([x + (r - center) * width for x in x_ticks], data[col], lw=0.4,
color=bar_colors[r], edgecolor="k", label=labels[r], width=width)
plt.xticks(x_ticks, labels=data[cols[0]].unique())
plt.xlabel(x_label, fontsize=5)
plt.ylabel(y_label, fontsize=5)
elif kind == 'h':
ax.barh([x + (r - center) * width for x in x_ticks], data[col], lw=0.4,
color=bar_colors[r], edgecolor="k", label=labels[r], height=width)
plt.yticks(x_ticks, labels=data[cols[0]].unique())
plt.xlabel(y_label, fontsize=5)
plt.ylabel(x_label, fontsize=5)
elif kind == 'l1':
# r==0表示是最下面那根柱子, 则bottom=0 否则==前面几列的和
bottom = 0 if r == 0 else data[cols[1:][:r]].sum(axis=1)
ax.bar(x_ticks, data[col], bottom=bottom, lw=0.4,
color=bar_colors[r], edgecolor="k", label=labels[r], width=width)
plt.xticks(x_ticks, labels=data[cols[0]].unique())
plt.xlabel(x_label, fontsize=5)
plt.ylabel(y_label, fontsize=5)
elif kind == 'l2':
bottom = 0 if r == 0 else data[cols[1:][:r]].sum(axis=1)
ax.barh(x_ticks, data[col], left=bottom, lw=0.4,
color=bar_colors[r], edgecolor="k", label=labels[r], height=width)
plt.yticks(x_ticks, labels=data[cols[0]].unique())
plt.xlabel(y_label, fontsize=5)
plt.ylabel(x_label, fontsize=5)
else:
print('kind参数输入错误: {h: 横柱状图, s: 竖柱状图, l1: 竖累积柱状图, l2: 横累积柱状图}')
# 修饰参数
plt.title(title, fontsize=7)
if grid:
plt.grid(axis='both', linestyle=":")
if legend:
plt.legend(fontsize=5, ncols=4, loc='lower center', bbox_to_anchor=(0.5, -0.27))
else:
plt.legend(fontsize=5)
# 轴刻度参数
ax.tick_params(axis='x', labelsize=5, rotation=x_rotation)
ax.tick_params(axis='y', labelsize=5, rotation=y_rotation)
plt.tight_layout()
if filename == '':
plt.savefig('柱状图.png')
else:
plt.savefig('{filename}.png')
return fig
使用如下示例数据进行演示
import pandas as pd
data = pd.DataFrame(
{
'1': list('abcdefg'),
'2':range(1, 8),
'3': range(2, 9),
'4': range(3, 10),
'5': range(4, 11),
'6': range(5, 12)
}
)
竖柱状图
bars(data, ['1', '2', '3', '4', '5', '6'], ['2', '3', '4', '5', '6'], width=0.1, kind='s')
bars(data, ['1', '2', '3', '4'], ['2', '3', '4'], width=0.1, kind='s')
横柱状图
bars(data, ['1', '2', '3', '4'], ['2', '3', '4'], width=0.2, kind='h')
bars(data, ['1', '2', '3', '4'], ['2', '3', '4'], width=0.2, kind='l1')
横累积柱状图
bars(data, ['1', '2', '3', '4'], ['2', '3', '4'], width=0.4, kind='l2')