Python绘制饼状图/甜甜圈

Python Matplotlib.pyplot

Matplotlib库是一个面向对象的绘图库。绘图界面由pyplot模块提供。该模块提供了许多绘图函数,以下记录的是饼状图/甜甜圈图的相关参数和绘图过程,官方资料详见文末链接。

# 导入相关模块
import numpy as np
import matplotlib.pyplot as plt
# 使中文正常显示的参数设置
plt.rcParams['font.sans_serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False

plt.subplot()plt.subplots()创建绘图环境

这两个函数都是用于创建绘图环境,但有所差异。由于参数较多,这里只对比主要的区别。

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()等命令,无需索引
"""

plt.pie()绘制饼状图

饼状图的应用场景通常是比较不同类别的占比,图中每一块扇形对应着一个类别的百分比。pie() 函数的输入数据 x 可能有以下几种形式:

  1. 已经计算好的、各类别对应的百分比(其和等于1),例如:
类别1类别2类别3
20%50%30%
  1. 未经百分比计算的、直接的统计数据(其和通常大于1),例如:
类别1类别2类别3
205030

比较常见的是第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. 一种特殊的情况是,当 s u m ( x ) &lt; 1 sum(x) &lt; 1 sum(x)<1时,例如:
类别1类别2类别3
0.10.50.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()的参数】
# 饼状图函数
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, optionalNone
labels各个数值对应的类别标签list, optionalNone
colors饼图的填充色array-like, optionalNone
autopct自动添加百分比显示,可以采用格式化的方法显示string, or function, optionalNone
pctdistance百分比标签与圆心的距离float, optional0.6
shadow是否添加饼图的阴影效果bool, optionalFalse
labeldistance各扇形标签(图例)与圆心的距离float, optional1.1
startangle饼图的初始摆放角度float, optionalNone
radius饼图的半径大小float, optionalNone
counterclock是否让饼图按逆时针顺序呈现bool, optionalTrue
wedgeprops饼图内外边界的属性,如边界线的粗细、颜色等dict, optionalNone
textprops饼图中文本的属性,如字体大小、颜色等dict, optionalNone
center指定饼图的中心点位置,默认为原点list of float, optional(0, 0)
frame是否要显示饼图背后的图框,如果设置为True的话,需要同时控制图框x轴、y轴的范围和饼图的中心位置bool, optionalFalse

由于饼状图是圆形的,所以绘图尺寸一般设置为正方形或圆形最佳,可以通过参数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实例:
[<matplotlib.patches.Wedge at 0x2ec2b835860>, 
<matplotlib.patches.Wedge at 0x2ec2b835d68>, 
<matplotlib.patches.Wedge at 0x2ec2b60f240>, 
<matplotlib.patches.Wedge at 0x2ec2b60f9b0>]

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()

plt.pie()绘制甜甜圈

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–饼图的绘制

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值