Matplotlib库是一个面向对象的绘图库。绘图界面由pyplot模块提供。该模块提供了许多绘图函数,以下记录的是饼状图/甜甜圈图的相关参数和绘图过程,官方资料详见文末链接。
# 导入相关模块
import numpy as np
import matplotlib.pyplot as plt
# 使中文正常显示的参数设置
plt.rcParams['font.sans_serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
这两个函数都是用于创建绘图环境,但有所差异。由于参数较多,这里只对比主要的区别。
type(plt.subplot()) # matplotlib.axes._subplots.AxesSubplot
type(plt.subplots()) # tuple
type(plt.subplots()[0]) # matplotlib.figure.Figure
type(plt.subplots()[1]) # matplotlib.axes._subplots.AxesSubplot
"""
默认情况下(nrows=1,ncols=1),两个函数均准备了如下的绘图窗口。但二者返回的类型不同:
1. plt.subplot()直接返回AxesSubplot对象
2. plt.subplots()返回一个元组,且元组的第1个元素是Figure对象,第2个元素是AxesSubplot对象
"""
plt.subplot(3, 1, 1) # 缩写为plt.subplot(311),表示绘图子窗口被设定为3行1列,当前在第1个窗口
plt.plot([1, 2, 3], [2, 4, 6]) # 在第1个子窗口绘图
plt.subplot(3, 1, 2) # 缩写为plt.subplot(312),表示绘图子窗口被设定为3行1列,当前在第2个窗口
plt.plot([1, 2, 3], [2, 4, 6]) # 在第2个子窗口绘图
plt.subplot(3, 1, 3) # 缩写为plt.subplot(313),表示绘图子窗口被设定为3行1列,当前在第3个窗口
plt.plot([1, 2, 3], [2, 4, 6]) # 在第3个子窗口绘图
"""
绘图过程见如下动图
plt.subplot()设定了子窗口的行、列后,每次绘图才生成一个子窗口
随后可用plt.plot()、plt.scatter()、plt.pie()等命令在各个子窗口中绘图
"""
fig, ax = plt.subplots(figsize=(12, 8), nrows=3, ncols=1) # figsize可设定子图的长宽
ax[0].plot([1,2,3], [2,4,6]) # 在第1个子窗口绘图
ax[1].plot([1,2,3], [2,4,6]) # 在第2个子窗口绘图
ax[2].plot([1,2,3], [2,4,6]) # 在第3个子窗口绘图
"""
绘图过程见如下动图
plt.subplot()s设定了子窗口的行、列后,一次性生成所有子窗口
随后可用ax[i].plot()、ax[i].scatter()、ax[i].pie()等命令在第i+1个子窗口中绘图
当只创建了一个窗口时,则直接用ax.plot()、ax.scatter()、ax.pie()等命令,无需索引
"""
饼状图的应用场景通常是比较不同类别的占比,图中每一块扇形对应着一个类别的百分比。pie() 函数的输入数据 x 可能有以下几种形式:
类别1 | 类别2 | 类别3 |
---|---|---|
20% | 50% | 30% |
类别1 | 类别2 | 类别3 |
---|---|---|
20 | 50 | 30 |
比较常见的是第2种类型的统计数据,即当 s u m ( x ) > 1 sum(x) > 1 sum(x)>1时,pie() 函数会进行归一化,以 x s u m ( x ) \frac{x}{sum(x)} sum(x)x形成饼状图的各个扇形部分。
类别1 | 类别2 | 类别3 |
---|---|---|
0.1 | 0.5 | 0.2 |
此时,pie() 不会进行归一化,而是直接绘制出缺失一个类别的饼状图(除了 0.1 0.1 0.1、 0.5 0.5 0.5、 0.2 0.2 0.2三个类别外,应剩余一个 0.2 0.2 0.2的类别,以空白区绘制):
此外,各个类别的扇形默认从x轴开始,以逆时针的顺序绘制。
# 饼状图函数
plt.pie(x, explode=None, labels=None, colors=None,
autopct=None, pctdistance=0.6, shadow=False,
labeldistance=1.1, startangle=None,
radius=None, counterclock=True, wedgeprops=None,
textprops=None, center=(0, 0), frame=False)
参数 | 含义 | 类型 | 默认 |
---|---|---|---|
x | 用于绘图的数值,决定扇形大小 | array-like | |
explode | 指定饼图某些类别(远离圆心)突出显示 | array-like, optional | None |
labels | 各个数值对应的类别标签 | list, optional | None |
colors | 饼图的填充色 | array-like, optional | None |
autopct | 自动添加百分比显示,可以采用格式化的方法显示 | string, or function, optional | None |
pctdistance | 百分比标签与圆心的距离 | float, optional | 0.6 |
shadow | 是否添加饼图的阴影效果 | bool, optional | False |
labeldistance | 各扇形标签(图例)与圆心的距离 | float, optional | 1.1 |
startangle | 饼图的初始摆放角度 | float, optional | None |
radius | 饼图的半径大小 | float, optional | None |
counterclock | 是否让饼图按逆时针顺序呈现 | bool, optional | True |
wedgeprops | 饼图内外边界的属性,如边界线的粗细、颜色等 | dict, optional | None |
textprops | 饼图中文本的属性,如字体大小、颜色等 | dict, optional | None |
center | 指定饼图的中心点位置,默认为原点 | list of float, optional | (0, 0) |
frame | 是否要显示饼图背后的图框,如果设置为True的话,需要同时控制图框x轴、y轴的范围和饼图的中心位置 | bool, optional | False |
由于饼状图是圆形的,所以绘图尺寸一般设置为正方形或圆形最佳,可以通过参数aspect="equal"
实现。
# plt.subplots定义画布和图型;figsize设置画布尺寸;aspect="equal"设置坐标轴的方正
fig, ax = plt.subplots(figsize=(6, 3), subplot_kw=dict(aspect="equal"))
recipe = ["375 g flour",
"75 g sugar",
"250 g butter",
"300 g berries"]
data = [float(x.split()[0]) for x in recipe] # 输入数值:375、75、250、300
ingredients = [x.split()[-1] for x in recipe] # 对应标签:flour、sugar、butter、berries
"""
pct是各类别百分比的整数部分(不含百分号):[37.5, 7.5, 25, 30]
allvals是统计绝对值列表:[375, 75, 250, 300]
fuc函数自定义百分比标签的摆放形式:百分数占一行,统计绝对值占一行
"""
def func(pct, allvals):
absolute = int(pct/100.*np.sum(allvals))
return "{:.1f}%\n({:d} g)".format(pct, absolute)
"""
参数autopct以函数形式传递,指定百分比标签(以及绝对值标签)的摆放形式
参数textprops以字典形式传递,设置字体颜色、字体大小等
ax.pie(data)返回一个元组
wedges是一个列表,其中包含4个matplotlib.patches.Wedge实例:
[,
,
,
]
texts是一个列表,其中包含4个matplotlib.text.Text实例:
[Text(0.420952,1.01627,''),
Text(-0.937904,0.574748,''),
Text(-0.980107,-0.49939,''),
Text(0.646564,-0.889919,'')]
autotexts是一个列表,其中包含4个matplotlib.text.Text实例:
[Text(0.22961,0.554328,'37.5%\n(375 g)'),
Text(-0.511584,0.313499,'7.5%\n(75 g)'),
Text(-0.534604,-0.272394,'25.0%\n(250 g)'),
Text(0.352671,-0.48541,'30.0%\n(300 g)')]
"""
wedges, texts, autotexts = ax.pie(data, autopct=lambda pct: func(pct, data),
textprops=dict(color="w"))
# ax.legend()指定各扇形(wedges)的图例标签(ingredients)、图例标题(title)、图例位置(loc)、图例坐标(bbox_to_anchor)
ax.legend(wedges, ingredients,
title="Ingredients",
loc="center left",
bbox_to_anchor=(1, 0, 0.5, 1))
# 设置文本标签的样式
plt.setp(autotexts, size=8, weight="bold")
ax.set_title("Matplotlib bakery: A pie")
plt.show()
fig, ax = plt.subplots(figsize=(6, 3), subplot_kw=dict(aspect="equal"))
recipe = ["225 g flour",
"90 g sugar",
"1 egg",
"60 g butter",
"100 ml milk",
"1/2 package of yeast"]
data = [225, 90, 50, 60, 100, 5]
"""
参数wedgeprops以字典形式传递,设置饼图边界的相关属性,例如圆环宽度0.5
饼状图默认从x轴正向沿逆时针绘图,参数startangle可指定新的角(例如负40度)度起画
"""
wedges, texts = ax.pie(data, wedgeprops=dict(width=0.5), startangle=-40)
# 创建字典bbox_props,设置文本框的边框样式(boxstyle:方框,pad设置方框尺寸)、前景色(fc)为白色(w)、边框颜色(ec)为黑色(k)、线粗(lw)为0.72
bbox_props = dict(boxstyle="square,pad=0.3", fc="w", ec="k", lw=0.72)
"""
参数集kw以字典形式传递,包含一系列用于绘图标注的指定参数
xycoords用于指定点xy的坐标环境,xycoords='data'表示沿用被注释的对象所采用的坐标系(默认设置)
textcoords用于指定点xytext的坐标环境,textcoords='data'表示沿用被注释的对象所采用的坐标系(默认设置)
参数arrowprops以字典形式传递,用于控制箭头的诸多属性,如箭头类型(arrowstyle)、箭头连接时的弯曲程度(connectionstyle)
"""
kw = dict(xycoords='data', textcoords='data', arrowprops=dict(arrowstyle="-"),
bbox=bbox_props, zorder=0, va="center")
for i, p in enumerate(wedges): # 遍历每一个扇形
ang = (p.theta2 - p.theta1)/2. + p.theta1 # 锁定扇形夹角的中间位置,对应的度数为ang
# np.deg2rad(x)将度数x转为弧度(x*pi)/180
y = np.sin(np.deg2rad(ang)) # np.sin()求正弦
x = np.cos(np.deg2rad(ang)) # np.cos()求余弦
"""
np.sign()符号函数:大于0返回1.0,小于0返回-1.0,等于0返回0.0
参数horizontalalignment用于设置垂直对齐方式,可选参数:left、right、center
当余弦值x大于0(即标签在饼图右侧时,按框左侧对齐)时,horizontalalignment="left"
当余弦值x小于0(即标签在饼图左侧时,按框右侧对齐)时,horizontalalignment="right"
"""
horizontalalignment = {-1: "right", 1: "left"}[int(np.sign(x))]
connectionstyle = "angle,angleA=0,angleB={}".format(ang) # 参数connectionstyle用于控制箭头连接时的弯曲程度
kw["arrowprops"].update({"connectionstyle": connectionstyle}) # 将connectionstyle更新至参数集kw的参数arrowprops中
"""
用一个箭头/横线指向要注释的地方,再写上一段话的行为,叫做annotate
ax.annotate()用于对已绘制的图形做标注
recipe[i]是第i个注释文本
size设置字体大小
xy=(x1, y1)表示在给定的xycoords中,被注释对象的坐标点
xytext=(x2, y2)表示在给定的textcoords中,注释文本的坐标点
"""
ax.annotate(recipe[i], size=10, xy=(x, y), xytext=(1.35*np.sign(x), 1.4*y),
horizontalalignment=horizontalalignment, **kw)
ax.set_title("Matplotlib bakery: A donut")
plt.show()
创建bbox_props字典参数时,有许多类型的选项,可在Annotating Axes查看。
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
df = pd.read_csv('all.csv', encoding='utf-8')
# 【直方图+折线】
year = df['year'].value_counts().sort_index()
plt.figure(figsize=(11, 11*0.618))
plt.bar(range(len(year)), list(year), width=0.8, align='center', color='steelblue')
# plt.title('title', fontsize=20)
plt.ylabel('x', fontsize=18)
plt.xlabel('y', fontsize=18)
plt.xticks(range(len(year)), list(year.index), fontsize=15, rotation=90)
plt.yticks(fontsize=15)
plt.plot(range(len(year)), list(year), color='black', linewidth=0.8)
plt.tight_layout()
for x, y in enumerate(list(year)):
plt.text(x, y+10, '%s' % y, ha='center', fontsize=12)
plt.savefig('直方图+折线.png')
# 甜甜圈
area = df['area'].value_counts()
pct = [format(i/sum(list(area)), '.1%') for i in list(area)]
text_label = ['{}({})'.format(i[0], i[1]) for i in zip(list(area.index), pct)]
fig, ax = plt.subplots(figsize=(11, 11*0.618), subplot_kw=dict(aspect="equal"))
# ax.set_title("title", fontsize=20)
explode = [0.02] * len(area)
wedges, texts = ax.pie(list(area), wedgeprops=dict(width=0.6),
startangle=0, explode=explode)
for i, p in enumerate(wedges):
ang = (p.theta2 - p.theta1)/2. + p.theta1
y = np.sin(np.deg2rad(ang))
x = np.cos(np.deg2rad(ang))
if i < 7:
connectionstyle = "angle,angleA=0,angleB={}".format(ang)
bbox_props = dict(boxstyle="square,pad=0.65", fc="w", ec="k", lw=1)
kw = dict(xycoords='data', textcoords='data',
arrowprops=dict(arrowstyle="-", connectionstyle=connectionstyle),
bbox=bbox_props, zorder=0, va="center")
horizontalalignment = {-1: "right", 1: "left"}[int(np.sign(x))]
ax.annotate(text_label[i], size=14, xy=(x, y), xytext=(1.2*np.sign(x), 1.35*y),
horizontalalignment=horizontalalignment, **kw)
else:
bbox_props = dict(boxstyle="square,pad=0.18", fc="w", ec="k", lw=1)
kw = dict(xycoords='data', textcoords='data',
arrowprops=dict(arrowstyle="-"),
bbox=bbox_props, zorder=0, va="center")
dynamic_y = [1.5*y, 1.3*y, -1.5*y, -25*y]
ax.annotate(text_label[i], size=10, xy=(x, y), xytext=(1.2*np.sign(x)+0.03*i, dynamic_y[i-7]),
horizontalalignment=horizontalalignment, **kw)
plt.savefig('圆环图.png')
# 词云图
import jieba
from collections import Counter
from pyecharts import WordCloud
str = ''.join(list(df['经营范围']))
seg_list = jieba.lcut(str)
stop = [] # 自备停用词表
cloud_data = [word for word in seg_list if word not in stop]
count = Counter(cloud_data)
wordcloud = WordCloud(width=1300, height=620)
wordcloud.add('', count.keys(), count.values(), word_size_range=[10, 100], shape='circle', rotate_step=30)
wordcloud.render('wordcloud.html')
参考资料:
官方文档:matplotlib.pyplot.subplot
官方文档:matplotlib.pyplot.subplots
官方文档:matplotlib.pyplot.pie
官方文档:Labeling a pie and a donut
官方文档:Annotating Axes
从0开始学Python系列11–饼图的绘制