Python数据分析高薪实战第九天 数据绘图基础

25 篇文章 5 订阅

21 绘图基础:如何将表中的数据特征画成图?

现在,数据分析的三座大山我们已经翻过了两座,学完了 NumPy 和 Pandas 之后,我们基本具备了如何以表格的形式进行常规的数据分析,以及对于数据部分进行一些统计分析、回归分析等。但数据分析还有另一个重要的任务,就是把数据分析的内容与结果以图表的形式呈现出来。

毕竟数据分析的结果是要给别人看的,丰富多彩的图表在表达能力上肯定超过干巴巴的表格与数字。

Python 提供了非常强大的数据可视化的工具,本部分我们就来逐步学习如何在 Python 中实现数据的可视化。

课前准备:安装 Matplotlib 与 seaborn 工具包

安装的方式和之前一样,我们打开开始菜单 → Anaconda3 → Anaconda Prompt。 输入 conda install matplotlib 并回车。界面如下所示:
image.png

输入 y 确认,完成安装。

接下来,我们安装 seaborn 工具包,继续输入 conda install seaborn 并回车,并在如下界面中输入 y 回车,确认安装。
image (1).png

安装完毕后,关闭命令行窗口。

基本概念

要能够熟练地使用 Matplotlib 来画图,首先要理解 Matplotlib 图像的三层结构。

Matplotlib 图表结构如下图所示:

image.png

从外向内的方向来看,依次如下。

  • 画板(Canvas),Matplotlib 所有图像都显示在一个 Canvas 之上,但 Canvas 一般都是系统放置好的,我们在代码中并不需要显式地创建 Canvas。

  • 画布(Figure),Canvas 上方的一层是画布,画布一般就需要我们手动创建了,一般在画图的代码编写中,第一件事就是创建画布,并指定画布的大小,材质等信息。

  • 子图(Axes),Axes 的意思是坐标轴。原本的意思为一个画布上可以有多个坐标轴,但我们习惯性地认为一个坐标轴就是一个子图,所以我们也把 Axes 叫作子图。一个画布上面可以添加多个子图。

绘图流程

介绍完了 Matplotlib 的基本概念,现在我们来介绍一下绘图的基本流程。可以用如下的图表示:
image (2).png

接下来我们通过绘制 y=2x + 1 的函数图,来逐一讲解每个步骤的实现方法。首先在工作目录创建本讲的目录,chapter21, 然后在 VS code 中打开,并新建 Notebook,保存为 chapter21.ipynb.

(1)导入 Matplotlib 和 numpy

为什么需要 numpy? 原因是基本上所有绘图的数据源要么是 pandas 的 Series,要么是 numpy 的 ndarray。这里为了方便讲解,我们使用更轻量级的 ndarray 来作为主数据源。

import matplotlib.pyplot as plt
import numpy as np

这里把 Matplotlib 简写为 plt 和 numpy 简写为 np,虽然这个简写可以任意指定,不过基本上用 plt 和 np 已经成为行业标准,网上大量的示例代码都采用了这种风格,所以一般情况下建议保持一致。

(2)准备图的数据源

我们要画图,首先就要思考我们的数据源是什么。因为图本身只是一个数据源更好的呈现方式。准备数据源,本质就是我们要思考我们想把什么东西画出来。在这个例子中,我们希望画出 y = 2x + 1 的函数曲线,那我们就要决定定义域(x 的范围)是多少。因为只要 x 确定了, y 也就可以直接算出来。

另外,在一般情况下,画图传入的只能是离散的值,不能是连续的值。比如我们想画 x 取值在 0 到 5 之间的函数曲线,那我们无法直接告诉 Matplotlib 我们想要这个范围的曲线,而是需要以一定的步长从这个范围中采样,生成一个离散的数据点列表告诉 Matplotlib。

比如我们可以按 1 采样,就是 [0,1,2,3,4] 也可以是按 0.5 采样。也就是 [0. , 0.5, 1. , 1.5, 2. , 2.5, 3. , 3.5, 4. , 4.5]。

这里我们画 0 到 10 范围内的曲线,按 1 采样,根据 NumPy 部分学习到的内容,使用 arange 函数可以轻松搞定。

x_ranges = np.arange(010)
x_ranges

输出:

array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])

现在,我们要画图的基本数据点就准备好了。

(3)创建画布

通过 plt.figure 函数可以创建一块画布。并且可以通过 figsize 参数指定画布的大小,figsize 接受一个两个元素的列表,第一个元素为画布的宽,第二个元素为画布的高,单位是英寸。

在接下来的代码中,我们创建了一个 18x6 大小的画布,并将其保存在变量 figure1 中。

figure1 = plt.figure(figsize=(186),facecolor=[0,0,1])

上述代码可以先不执行,不然会生成一个空的图。一般来说 Matplotlib 的画图代码中,创建画布、创建子图,设置子图标题、坐标轴属性以及绘制步骤往往都放在一个 Cell 中,避免中间放在不同 cell 中间会画出不完整的图像。

(4)创建子图

创建完画布之后,下一个步骤就是创建子图。在步骤 2 的基础上,添加如下的代码,创建一个充满整块画布的子图。

ax1 = figure1.add_subplot(1,1,1)

add_subplot 函数参数里面的三个 1 是什么意思呢? 我们在后续的子图布局会展开讲,这里就不再赘述。

(5)设置子图标题

在创建子图之后,直接使用 plt.title 函数即可指定当前子图的标题。将上述代码添加到步骤 2 的 cell 中。

 plt.title("y = 2x +1")

这里你可能会有疑问,我们没有传入子图对象 ax1,他是怎么知道设置的是哪个子图的?原因是 Matplotlib 内部有一个状态机的机制,在你每次执行 add_subplot 的时候,系统都会记录当前你操作的子图,这样你执行 plt 的这些方法都是针对当前子图生效。

(6)设置坐标轴的属性。

坐标轴的常见属性有标题、范围和刻度。一般来说指定的范围之后可以直接默认推导出刻度,所以主要还是以设置标题和范围为主。在步骤 2 的 cell 中,添加如下的代码。

plt.xlabel("X")
plt.xlim([020])
plt.ylabel("Y")
plt.ylim([020])

这里我们设置的范围,x 轴和 y 轴都是 0 到 20。这里的范围和数据源范围的关系是什么呢?这里设置的是坐标轴的范围,而数据源指定的是绘制曲线的范围。比如刚才我们设置的曲线范围是 0 到 10,而这里的轴范围指定了 0 到 20,所以待会儿我们画出的曲线理论上只会在坐标系的左边部分出现。

继续不用执行,看下面的步骤。

(7)绘图并显示

接下来就到了最关键的步骤:绘图。绘图的本质就是在我们准备好画布和轴之后将数据源告诉 Matplotlib 的过程,这里要用到 plt.plot 函数。

一般情况下 plot 函数接受两个参数,一个是 x 轴的数据,一个是对应的 y 轴的数据。x 轴的数据我们在步骤 2 已经创建好了,而我们要画的是 y = 2x + 1 的曲线,所以 y 轴的数据直接用 x轴的数据乘以 2 + 1 即可。因为 x_range 是一个 NumPy 的ndarray,所以我们可以直接运算。

在调用完 plot 函数后,我们再调用 plt.show 函数,来将我们刚才做的全部工作展示出来。

整个绘图过程,完成的代码如下:

figure1 = plt.figure(figsize=(186))
ax1 = figure1.add_subplot(1,1,1)
plt.title("y = 2x +1")
plt.xlabel("X")
plt.xlim([020])
plt.ylabel("Y")
plt.ylim([020])
# 本步骤新增
plt.plot(x_ranges, x_ranges * 2 + 1)
plt.show()

执行之后输出如下:

image (3).png

可以看到,我们的曲线已经被画出来了,并且和我们预期的一致,曲线只在左边部分出现,因为我们的数据源只包含了 0 到 10 的数据。画布大小也和我们设置的一样,宽是高的三倍,对于图的标题,坐标轴的标题设置也都生效。

但细心的你可能发现了问题,虽然我们的图确实展示了我们的数据在坐标轴的位置关系。但是 x 轴的刻度宽度却远比 y 轴的宽,本质原因是我们设定了坐标的 x 轴 比 y 轴长,但是两个轴的范围都是一样的,都是 0 到 20,这样就使得函数曲线看上去有点变形。

在对 Matplotlib 的绘图经验不足的时候,我们很难一次性把参数设置对,经常都需要先画出图来,然后再调整参数。

(8)根据绘图结果调整参数

要绘制出比例一致的图形,有多种方式,本质只要坐标系的大小和 x 轴、y 轴的范围成比例即可。这里我们简单地将画布大小调整为 10x10, 这样两边比例就一致了。

figure1 = plt.figure(figsize=(1010)) # 改动点
ax1 = figure1.add_subplot(1,1,1)
plt.title("y = 2x +1")
plt.xlabel("X")
plt.xlim([020])
plt.ylabel("Y")
plt.ylim([020])
plt.plot(x_ranges, x_ranges * 2 + 1)
plt.show()

输出如下:

image (4).png

现在的图能够比较真实地反应函数 y=2x+1 的形状了。

子图的布局

本节我们介绍子图的布局方式。Matplotlib 的子图默认是按照行列的方式来布局的。核心就是通过画布对象的 add_subplot 函数。

add_subplot 函数的形式如下:

add_subplot(行数,列数,当前子图索引)

行数和列数代表子图的位置在画布中如何划分。
比如如果是行数和列数都是 2 的话,代表把画纸拆分成 2x2,一共四个格子,每个格子可以放置一个子图。当前子图索引参数指的是告诉 Matplotlib 接下来要设置的是这个 2x2 的划分里的第几幅子图。

这里要注意的是,当前子图索引和绝大多数 Python 索引不同,这里的索引是从 1 开始的,顺序对应网格的顺序是从左到右,从上到下。

我们还是通过例子来学习一下 add_subplot 的用法,我们接下来创建一个 2x2 的划分,并分别逆序添加四幅子图,第一幅添加在最后,以此类推。

figure2 = plt.figure(figsize=[18,9])
# 第一幅,摆在右下角,也就是第四个位置
ax1 = figure2.add_subplot(2,24)
plt.title("fig1")
# 第二幅,摆在第三个位置,也就是第二行第一个
ax2 = figure2.add_subplot(2,2,3)
plt.title("fig2")
# 第三幅,摆在第二个位置,也就是第一行第二个
ax3 = figure2.add_subplot(2,2,2)
plt.title("fig3")
# 第四幅,摆在第一个位置,也就是第一行第一个
ax4 = figure2.add_subplot(2,2,1)
plt.title("fig4")
plt.show()

输出如下:

image (5).png

因为这次添加的子图我们并没有做属性的设置和数据的填充,所以画的都是默认的空白图。但我们为每个子图都设置了标题来区分,可以看到第一个子图在右下角,第二个在左下角,这与我们添加时设置的子图索引是匹配的。

颜色与线条样式

Matplotlib 支持丰富的样式,其中最基本的就是控制画出的图形颜色和线条的粗细。

一般来说有以下几个原则:

  • 画布的样式,通过设置 figure 函数的参数决定;

  • 子图的样式,通过设置 add_subplot 函数的参数决定;

  • 线条的样式,通过设置 plot 函数的参数决定。

另外,在 Matplotlib 中,颜色往往由三个元素的列表来表示,分别代表颜色 R、G、B 三个通道的分量,范围都是 0-1.。

学习过计算机图像处理的同学应该并不陌生,比如纯红色就对应 [1, 0 , 0], 绿色就是 [0, 1, 0],蓝色就是 [0,0,1]。比如还有一些颜色是三种颜色混合运算的,比如青色就是 [0,1,1]。 感兴趣的同学可以参考RGB 颜色表 (360doc.com)里面的颜色值除以 255,就可以得到 Matpliblot 中可以使用的色值。

接下来,我们还是通过一个例子来学习如何改变图像的各种颜色,任务是将刚才 y=2x + 1 的图像,做以下修改:

  • 画布颜色修改为红色;

  • 子图颜色修改为蓝色;

  • 线条颜色修改为绿色;

  • 线条粗细变为 3。

实现的代码如下:

# 通过 figure 函数的 facecolor 参数设置画布颜色
figure1 = plt.figure(figsize=(1010), facecolor=[100]) # 改动点
# 通过 add_subplot 函数的参数设置子图的颜色
ax1 = figure1.add_subplot(1,1,1, facecolor = [001])
plt.title("y = 2x +1")
plt.xlabel("X")
plt.xlim([020])
plt.ylabel("Y")
plt.ylim([020])
# 通过 plot 函数的 linewidth 参数设定线条的粗细
# 通过 color 参数来设定线条的颜色
plt.plot(x_ranges, x_ranges * 2 + 1, linewidth = 3, color=[0,1,0])
plt.show()

输出为:

image (6).png

从上图中可以看到,各个部分都和我们刚才的设置保持了一致。

实战:显示评分曲线

接下来我们通过一个小实战来演练一下今天学习的内容。

任务说明:将国产电视剧评分的前 100 条记录的评分情况用折线图表示出来。

准备数据源

把前 100 条记录的评分用折线图画出来,那, x 轴就是电视剧的序号,0 到 100,y 轴就是具体序号对应的电视剧的评分。

首先我们从 chapter12 目录中,把 tv_rating.csv 拷贝到 chapter21 目录中。然后新建 Cell,加载数据集。如下所示:

import pandas as pd
df_rating = pd.read_csv("tv_rating.csv")
df_rating

输出:

image (7).png

现在评分一列是字符串表示的,而且还有一个“分”字,而我们要送到 Matplotlib 中的数据肯定需要是数字,所以需要对这一列进行处理,去掉“分”字,并把数据转换为浮点数。我们把处理后的数据存为一个新的列:rating_num。

df_rating["rating_num"] = df_rating.rating.apply(lambda x:float(x.replace("分","")))
df_rating

这里继续使用了 lambda 表达式来处理列,针对 rating 列的每一项,我们首先删除“分”字,然后转换为 float。转换后的 Series 存储为 rating_num。
输出如下:

image (8).png

可以看到,我们的新列已经添加成功。由于只处理前 100 条,所以我们将前 100 条保存为一个新的 DataFrame:df_head_100。

df_head_100 = df_rating[:100]
df_head_100

输出如下:

image (9).png

至此,我们的数据源就准备完毕了。

绘制评分图

在数据源准备好之后,按照我们之前介绍的步骤直接绘图即可。

代码如下:

# 创建图表
figure = plt.figure(figsize=[18,9])
# 创建1x1分割的网格,并选中第一个区域创建子图
ax1 = figure.add_subplot(1,1,1)
# 要显示中文需要指定字体
plt.rcParams["font.sans-serif"] = "SimHei"
# 设置标题和轴属性
plt.title("电视剧评分折线图")
plt.xlabel("序号")
plt.xlim([0100])
plt.ylabel("评分")
plt.ylim([0,5])
# 画图,x 轴是序号,所以可以直接用 index 属性去索引即可
# y 轴的评分的值,则直接用我们准备的 rating_num 列
plt.plot(df_head_100.index, df_head_100.rating_num)
plt.show()

输出:

image (10).png

小结

到现在,我们关于 Matplotlib 第一课的内容就学习完毕了。简单复习一下今天的内容。

  • 我们学习了 Matplotlib 的基本概念:画板、画布、子图。

  • 学习了 Matplotlib 绘图的基本步骤:

    • 准备要用来可视化展现的数据源;

    • 创建画布,指定大小和画布颜色,默认为白色;

    • 创建子图,可以指定划分和位置,以及颜色;

    • 设置子图的标题;

    • 设置子图的轴标题,范围信息;

    • 设置数据源,执行绘图;

    • 显示图形。

  • 学习了 Matplotlib 的颜色体系:通过三个元素的列表分别代表颜色的 R G B 分量,每个分量的取值范围都是 0 到 1。

  • 学习了子图的布局方式,通过 add_subplot 函数的参数决定了对画布的划分方式,以及选择当前创建子图的位置。

Matplotlib 默认绘制的是折线图,也就是本讲我们打交道的类型,下一讲我们会介绍更多更有意义图形绘制方式。

课后习题:

将实战案例的画布拆成上下两部分,上面依旧显示 0 到 100 的评分,下面部分则显示序号 100 到 200 之间的评分。


习题答案:

figure = plt.figure(figsize=[18,9])
ax1 = figure.add_subplot(2,1,1)
plt.rcParams["font.sans-serif"] = "SimHei"
plt.title("电视剧评分折线图 (0-100)")
plt.xlabel("序号")
plt.xlim([0100])
plt.ylabel("评分")
plt.ylim([0,5])
plt.plot(df_head_100.index, df_head_100.rating_num)
ax1 = figure.add_subplot(2,1,2)
plt.rcParams["font.sans-serif"] = "SimHei"
plt.title("电视剧评分折线图 (100-200)")
plt.xlabel("序号")
plt.xlim([100200])
plt.ylabel("评分")
plt.ylim([0,5])
plt.plot(df_head_100_200.index, df_head_100_200.rating_num)
plt.show()

输出如下:

2021611-154431.png


22 散点图与线图:如何展示不同特征之间的相关性?

上一讲我们学习了 Matplotlib 画图的基本流程和一些常见图像属性的设置。本讲我们来学习在特征相关性分析中最常用的两种图形:折线图和散点图的用法,以及如何用折线图与散点图来分析特征之间相关性的方法。

折线图

折线图我们应该已经不陌生了,在上一讲我们绘制的图像都是折线图。Matplotlib 中,绘制折线图主要通过 plot 函数实现。

今天我们来学习折线图一些更深入的使用技巧和方法。

绘制平滑的曲线

虽然我们都说折线图,但是并不代表 plot 函数只能画直线,我们以画 sin 函数为例。在工作目录下创建文件夹,名字为 chapter22,并在 VS code 中打开该文件夹。打开之后新建 Notebook,保存为 chapter22.ipynb。

sin 函数的范围是 -1 到 1,所以这次我们设定 y 轴的范围为 -5 到 5。x 轴的范围取 0 到 20。我们画出 x 取值 0 到 20 的正弦曲线。我们上一讲已经学习过画图的做法,稍加改动就能够实现这次的需求。代码如下:

# 准备数据源
x_ranges = np.arange(020)
# 准备画布和轴
figure1 = plt.figure(figsize=(1010)) # 改动点
ax1 = figure1.add_subplot(1,1,1)
plt.title("y = sin(x)")
plt.xlabel("X")
plt.xlim([020])
plt.ylabel("Y")
plt.ylim([-2.52.5])
# 用 x_ranges,和对应的 sin 值画线
plt.plot(x_ranges,np.sin(x_ranges) )
plt.show()

输出如下:

Drawing 1.png

可以看到,曲线虽然画出来了,但是却不够平滑,比较生硬。本质的原因是我们的数据源,plot 函数本质就是把数据源每个点用线连起来,所以我们只需要提供更“密集”的数据源,也就是提高采样的频率,就能够获得更平滑的曲线。

用这个例子来说,y 的数据是由 x 决定的,所以我们只需要提供一个间隔更小的 x_ranges 即可。之前的课程学过,numpy 的 arange 函数的第三个参数就是步长,默认是 1,这里我们只需要在同样的范围情况下,缩短步长就可以获得更密集的数据。我们可以测试一下:

print("步长为1:", np.arange(0,20))
print("步长为0.2:", np.arange(0200.2)) 

输出如下:

步长为1: [ 0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19]
步长为0.2: [ 0.   0.2  0.4  0.6  0.8  1.   1.2  1.4  1.6  1.8  2.   2.2  2.4  2.6
  2.8  3.   3.2  3.4  3.6  3.8  4.   4.2  4.4  4.6  4.8  5.   5.2  5.4
  5.6  5.8  6.   6.2  6.4  6.6  6.8  7.   7.2  7.4  7.6  7.8  8.   8.2
  8.4  8.6  8.8  9.   9.2  9.4  9.6  9.8 10.  10.2 10.4 10.6 10.8 11.
 11.2 11.4 11.6 11.8 12.  12.2 12.4 12.6 12.8 13.  13.2 13.4 13.6 13.8
 14.  14.2 14.4 14.6 14.8 15.  15.2 15.4 15.6 15.8 16.  16.2 16.4 16.6
 16.8 17.  17.2 17.4 17.6 17.8 18.  18.2 18.4 18.6 18.8 19.  19.2 19.4
 19.6 19.8]

可以看到,当步长变为0.2 之后,生成的数组就变得很密集。
将 x_ranges 的生成增加步长,代码如下:

# 准备数据源
x_ranges = np.arange(0200.2) # 改动点
# 准备画布和轴
figure1 = plt.figure(figsize=(105)) 
ax1 = figure1.add_subplot(1,1,1)
plt.title("y = sin(x)")
plt.xlabel("X")
plt.xlim([020])
plt.ylabel("Y")
plt.ylim([-2.52.5])
# 用 x_ranges,和对应的 sin 值画线
plt.plot(x_ranges,np.sin(x_ranges) )
plt.show()

输出如下:
Drawing 3.png

可以看到,数据曲线变得平滑了很多。

连线规则

既然 plot 函数是把传入的点用线连接起来,那连接的顺序是什么呢?plot 函数的连接顺序并不是按照 x 从左到右画,而是严格按照传入的数据源的顺序,首先将 x[0], y[0] 连接到 x[1], y[1], 然后再将 x[1] y[1] 连接到 x[2], y[2],以此类推。所以理论上,我们在坐标轴中并不仅限于画函数图形,而是可以画任意图形。

接下来,我们通过四个点来在坐标轴上画一个三角形,来演示 plot 函数的连线规则。

 figure2 = plt.figure(figsize=(105)) 
ax1 = figure2.add_subplot(1,1,1)
plt.title("Test Line")
plt.xlabel("X")
plt.xlim([020])
plt.ylabel("Y")
plt.ylim([010])
# 传入四个点,画三角形
plt.plot([2105,2],[225,2])
plt.show()

输出如下:
Drawing 5.png

根据我们传入的点,上述图形的绘制顺序如下,1 → 2 → 3。

Drawing 7.png

fmt 语法

折线图的最后一个话题,我们来介绍一下它格式设置的语法。折线图常见的格式设置主要包含三种:

  • marker,也就是点的样式,这里的点指的就是数据源中传入的点;

  • line,线的样式,比如有实线、虚线;

  • color,也就是线条的颜色,这个我们之前通过参数 c 设置过。

我们先简单来感受一下样式的设置,在刚才的 cell 中,将 plot 函数增加一个字符串的参数。如下所示:

plt.plot([2105,2],[225,2], "o--r")

三角形将会变成如下所示:
Drawing 9.png

主要有三个变化:

  • 线的连接处变成了圆点,这个就是 marker;

  • 线变成了虚线;

  • 颜色变成了红色。

这些都是通过 "o--r" 这个字符串实现的,这个字符串,就称之为 fmt 参数。

fmt 参数本质就是通过一个字符串来一次性设置三种样式。它的格式如下:

fmt = "[marker][line][color]"

中括号表示可以只写其中的一项或者两项。
(1)marker 的语法

三个部分我们逐一来看,首先是 marker,可以有如下几种取值:

Drawing 10.png

设置 marker 的样式除了在 fmt 字符串中设定其样式外,plot 函数还支持 markersize 参数用于指定 marker 的大小。

可以将上述 plot 函数中的 fmt 字符串的第一个字符替换为其他样式,测试样式的效果。比如我们替换为六边形,代码如下:

plt.plot([2105,2],[225,2], "H--r", markersize=10)

输出如下:
Drawing 12.png

可以看到,定点的样式已经变成了六边形。

(2)line 的语法

Drawing 13.png

在之前的例子中,line 的类型我们使用的是线段虚线(--),可以修改之前的 plot 函数调用,来感受不同线条的样式,比如这里我们改成线段-点混合虚线(-.)。

plt.plot([2105,2],[225,2], "H-.r", markersize=10)

输出如下:
Drawing 15.png

(3)颜色的语法

设置线条和 marker 的颜色,之前我们介绍过可以通过传入 RGB 三个数字的形式来指定。在 fmt 语法中也可以通过颜色的简写来指定,对应关系如下。

Drawing 16.png

这次我们来改个颜色试试,还是改一下 fmt 的参数,将颜色标记改为 g,即绿色,如下所示:

plt.plot([2105,2],[225,2], "H-.g", markersize=10)

输出如下:
Drawing 18.png

散点图

散点图在前一节我们已经画过,和折线图最核心的区别就是,散点图是直接将点画出来,而不会用线去连接。在很多离散数据的场景,用折线图往往不能很好地反应特征之间的相关性,尤其是在数据点比较多的情况下,用折线图往往就会非常凌乱。这个时候直接将数据点画出来往往更加直观。

散点图作图的流程和折线图完全一样,只是在绘图的时候,散点图调用的方法是 plt.scatter, 而折线图调用的是 plt.plot。plt.scatter 函数的用法和 plt.plot 很类似,同样是传入 x 轴的数组和 y 轴的数组,而且 scatter 函数同样支持设置点的样式,但不支持 fmt 语法。设置散点图的点参数可以使用 scatter 函数的 marker 参数,取值和 fmt 中介绍的 marker 的表格一致。

现在我们直接基于之前画正弦函数(sin 函数)的例子,来看一下散点图的效果:

# 准备数据源
x_ranges = np.arange(0, 20, 0.2) # 改动点
# 准备画布和轴
figure1 = plt.figure(figsize=(10, 5)) 
ax1 = figure1.add_subplot(1,1,1)
plt.title("y = sin(x)")
plt.xlabel("X")
plt.xlim([0, 20])
plt.ylabel("Y")
plt.ylim([-2.5, 2.5])
# 用 *  画出正弦的散点图
plt.scatter(x_ranges,np.sin(x_ranges), marker="*")
plt.show()

输出如下:
Drawing 20.png

相关性分析实战

散点图在异常数据分析与特征相关性分析中非常常见。接下来我们通过一个实际的案例来学习如何使用散点图来进行异常值和相关性的分析。

数据准备

首先下载数据源(链接:https://pan.baidu.com/s/1HalPbk8BdkpmaRfcEAu21A提取码: 9ccd ),将下载到的 data.csv 保存到 chapter22 目录中。

然后,我们在 notebook 中查看一下数据。

import pandas as pd
df_goods = pd.read_csv("data.csv")
df_goods

输出如下:

Drawing 21.png

data.csv 存储了欧洲某礼品电商公司销售的订单数据。从上图中可以看到有 54w 条数据,本次我们重点关注以下三列:

2021617-175615.png

异常数据剔除

首先,我们来分析一下 Quantity 这个列是否存在异常值。异常值指的就是和绝大多数值差得很远,或者明显不符合常识的值。对于 Quantity 列来说,小于 0 的肯定是异常值,如果大多数订单都只是 10 到 20, 那如果有记录是几千、几万,那肯定也是异常值。

一般来说,异常值分析主要包含两个方面:

  • 分析是否存在异常值;

  • 分析异常值存在的比例。

为什么要分析比例呢? 如果异常值的比例太高,那说明这个列就没有分析的价值,我们就需要想其他的办法帮助我们找到利于分析的数据。

使用散点图,上述两个步骤都可以轻松完成。

我们尝试绘制 Quantity 与时间的散点图,这样就能看出随着时间的推移的 Quantity 数据的分布。

(1)清洗 Quantity 异常值

第一步,我们需要将订单时间,也就是 InvoiceDate 转换成 datetime 的格式,这样才能将其作为横轴。代码如下:

df_goods.InvoiceDate = pd.to_datetime(df_goods.InvoiceDate)

然后,我们画出 Quantity 沿着下单日期的分布图。

plt.figure(figsize = [18,6])
plt.scatter(df_goods.InvoiceDate, df_goods.Quantity)

上述代码中,我们没有创建子图,也没有设置 x/y 轴的属性,当没有这些代码的时候。Matplotlib 会自动为我们创建一个充满画布的子图,然后用我们传入数据的范围来作为轴的尺度。

上述代码输出如下:

Drawing 23.png

从图中可以清晰地看出 Quantity 数据存在异常值,一部分是小于 0 的,一部分远大于大多数值。我们首先剔除小于 0 的值,以及大于 10000 的值,再来看一下分布情况。

代码如下:

df_goods = df_goods.loc[(df_goods.Quantity > 0) & (df_goods.Quantity < 10000)]
plt.figure(figsize = [18,6])
plt.scatter(df_goods.InvoiceDate, df_goods.Quantity)

输出如下:
Drawing 25.png

第一轮异常值剔除后,我们现在在 0~10000 的范围上可以看得更加细致,从上图中,我们不难发现,完全可以以 800 作为阈值来剔除异常值。

代码如下:

df_goods = df_goods.loc[(df_goods.Quantity > 0) & (df_goods.Quantity < 800)]
df_goods

输出如下:
Drawing 26.png

异常值剔除之后还剩 53w 条记录,影响不大,也符合我们从散点图中观察得到的结论。

(2)清洗 UnitPrice 异常值

用类似的方法看 UnitPrice 的分布:

plt.figure(figsize = [18,6])
plt.scatter(df_goods.InvoiceDate, df_goods.UnitPrice)

输出如下:

Drawing 28.png

可以看到, UnitPrice 仍然存在异常值,我们首选初步排除 < 0 以及大于 1000 的。然后再看一次。

df_goods = df_goods.loc[(df_goods.UnitPrice > 0) & (df_goods.UnitPrice < 1000)]
plt.figure(figsize = [18,6])
plt.scatter(df_goods.InvoiceDate, df_goods.UnitPrice)

输出如下:

Drawing 30.png

这次的 UnitPrice 虽然分布均衡了一些,但不难发现底部那一条的数据点的密集程度还是远超过上方的区域,从点的稀疏程度来看,我们可以认为超过 50 的都算异常值。

在之前的基础上做以下调整。

df_goods = df_goods.loc[(df_goods.UnitPrice > 0) & (df_goods.UnitPrice < 50)]
df_goods

输出如下:
Drawing 31.png

可以看到,在过滤完 Quantity 和 UnitPrice 的异常值后,数据表还剩 52w条记录,整体还是不影响分析的。

特征相关性分析

在剔除完异常值之后,我们接下来的任务是分析 UnitPrice 和 Quantity 之间是否存在一定的相关性。相关性分析之前我们已经学习过使用 NumPy 的 corrcoef 函数来计算,今天我们尝试通过散点图的形式来分析。

一般来说,要分析两个样本量很高特征的相关性,我们可以直接把其中一个特征作为横轴,另一个特征作为纵轴,然后将样本数据以点的形式画出来。

代码如下所示:

plt.figure(figsize = [18,6])
plt.scatter(df_goods.UnitPrice, df_goods.Quantity)

输出如下:

Drawing 33.png

上图中,我们以 UnitPrice 为横轴,Quantity 作为纵轴。从图中可以看到,随着 UnitPrice 增加,Quantity 对应就减少,尤其是超过 20块之后更加明显,而随着 UnitPrice 降低,Quantity 增加,UnitPrice 在 0 块到 6 块这个区间,Quantity 的数量最高。说明东西越便宜,卖的数量就越多。

从上面的推论上,不难看出 UnitPrice 和 Quantity 存在一定的负相关性,但这个相关性不是线性的。通过 NumPy 的 corrcoef 函数我们只能得到一个相关性的系数,而通过散点图往往能解读出更多更有用的信息。

小结

我们来复习一下今天学习的内容。首先,我们学习了折线图的几个实用技巧:

  • 如何绘制平滑的曲线;

  • 如何利用连线规则来画出任意的图形;

  • 如何利用 fmt 语法来指定折线图的样式。

然后,我们学习了散点图的基本用法,非常简单,就是将 plot 函数替换为 scatter 函数即可。

最后,我们通过一个相关性分析的案例实战,体会了如何使用散点图来做异常值的清洗和特征相关性的分析。

课后作业:

在坐标系内绘制一个以(50,200) 作为左上角点的,宽度为 150 的正方形,点的样式为 8 边形,线的样式为虚线点,颜色为青色。


答案:

figure3 = plt.figure(figsize=(55))
ax1 = figure3.add_subplot(1,1,1)
plt.title("Test Line")
plt.xlabel("X")
plt.xlim([0400])
plt.ylabel("Y")
plt.ylim([0400])
plt.plot([50200200,5050],[20020050,50,200], "8-.c")
plt.show()

输出
Drawing 35.png


23 直方图、条形图和饼图:如何分析数据分布与占比?

上节课中,我们学习了折线图和散点图,并且学习了如何可视化地进行特征的异常数据清洗和相关性分析。今天我们来学习另一类可视化的图表类型,主要有直方图、条形图和饼图,这些图更常见的用法是用于分析数据集内部的数据分布,以及数据取值频率等。

直方图

直方图是数据分析中经常出现的概念,又称为质量分布图,由一系列高度不等的细长矩形排列在横轴上来表示数据分布的情况(如下图所示)。一般来说,横轴表示数据的类别,纵轴表示数据的频率。直方图本质上是对于连续变量的概率分布图。

Drawing 1.png

(直方图示例)

要画直方图,我们第一步首先是要将所有数据分组,组的数量就是直方图中竖长方形的数量。直方图的分组要求是不重叠且相邻的,并且一般是等距的。

直方图在很多领域都有广泛的应用,除了数据分析。直方图也常常被用来分析图像的特征,甚至可以用来做一些基本的图像分类。因为图像是由不同的像素点组成的,每个像素点的取值就是颜色值 0~255, 通过将图像的所有像素点都以直方图的形式展现,比如按照像素值分成 255 个组,就能根据直方图的形状来推测图像大概是什么。

画直方图

在 matplotlib 中,可以通过 plt.hist 函数画直方图。接下来我们生成一个符合正态分布的数组,然后将其用直方图的形式表现出来。通过这个例子来学习直方图的使用方法。

首先是大家已经非常熟悉的准备工作:在工作目录新建 chapter23, 并在 VScode 中打开,然后新建 notebook,保存为 chapter23.ipynb。

首先导入必要的三大件:

import matplotlib.pyplot as plt
import pandas as pd 
import numpy as np

然后初始化 NumPy 的随机数,并生成以 5 为中心,宽度为正负 10 的 1000 个符合正态分布的随机数,作为我们画图的数据源。

np.random.seed(100)
hints = np.random.normal(5101000)

这里由于数字太多,就不打印了,直接逐一执行上述两个 Cell 即可。

我们在 NumPy 的部分学习了 ndarray 支持常见的统计方法,所以我们可以通过查看数组的均值和标准差来看这些数据是否符合我们设定的正态分布。

print(hints.mean(), hints.std())

输出如下:

4.832278426560908 10.458427194167

可以看到,均值在我们设定的中心(5)附近,标准差约等于我们设定的宽度(10,所以数据基本没有问题。
接下来就是关键部分,画直方图,代码如下:

# 创建画布
figure1 = plt.figure(figsize = (105))
figure1.add_subplot(1,1,1)
# 设置轴的属性,轴属性相关的函数都支持fontsize 参数来设置字号
# 这里我们都设置为 14
plt.xlabel("值",fontsize = 14)
plt.ylabel("频率", fontsize = 14)
plt.xticks(fontsize=14)
plt.yticks(fontsize=14)
plt.title("正态分布的直方图", fontsize=14)
# 设置画布基本属性:1.显示中文  2.显示负号
plt.rcParams["font.sans-serif"] = "SimHei"
plt.rcParams["axes.unicode_minus"] = False
# 画直方图,分10组,用蓝色来画
plt.hist(hints, bins=10, color=[0,0,1])
plt.show()

输出如下:

Drawing 3.png

从上图中不难发现,整个数据看起来就是正态分布的形状,随机的极值点到了-30 到 40。从直方图的台阶上看,相对于中心对称区域的高度都差不多(频率基本一致)。所以从直方图的几何特征上能够真实地反映出数据的概率分布特性。

看到这里,想必你也已经发现,plt.hist 函数的 bins 参数非常关键,bins 的值代表了要把数据分成几组,也就代表了图上一种会有多少个长方形。bins 越大,体现得就越精确,但相应的分布特征可能就越不明显。比如我们可以把上述代码的 bins 改成 50 。

...
plt.hist(hints, bins=50, color=[0,0,1])
...

输出如下:
Drawing 5.png

可以看到,分组变多后,直方图展示了更多关于原始数据的信息,但是也出现了较多的锯齿,但整体仍然是正态分布的形状。

直方图另外一个非常有用的参数就是 edgecolor,即每个长方形的边框颜色。上面的直方图中间区域是一片蓝,有时候并不利于做进一步的数据分析。这个时候我们可以设置 plt.hist 函数的 edgecolor 属性,来让每个长方形都有一个边框颜色。

修改上述代码如下:

...
# 设置长方形的边框颜色是黑色 
plt.hist(hints, bins=50, color=[0,0,1], edgecolor=[0,0,0])
...

输出如下:
Drawing 7.png

可以看到,设置边框颜色之后,整个直方图看着更清晰了。

展示多个直方图

在很多分析场景,我们希望同时分析 2~3 个数据源的频率分布,这背后的技术就是把 2~3 个直方图在同一个坐标系中展示。

为了模拟多个直方图,我们首先生成另外两个数据源。

hints_1 = np.random.normal(08500)
hints_2 = np.random.normal(1510800)

接下来我们三次调用 plt.hist 即可,为了区分,我们使用三种不同的颜色来画。

# 创建画布
figure1 = plt.figure(figsize = (105))
figure1.add_subplot(1,1,1)
# 设置轴的属性,轴属性相关的函数都支持fontsize 参数来设置字号
# 这里我们都设置为 14
plt.xlabel("值",fontsize = 14)
plt.ylabel("频率", fontsize = 14)
plt.xticks(fontsize=14)
plt.yticks(fontsize=14)
plt.title("正态分布的直方图", fontsize=14)
# 设置画布基本属性:1.显示中文  2.显示负号
plt.rcParams["font.sans-serif"] = "SimHei"
plt.rcParams["axes.unicode_minus"] = False
# 第一个用蓝色
plt.hist(hints, bins=50, color=[0,0,1], edgecolor=[0,0,0])
# 第二个用绿色
plt.hist(hints_1, bins=50, color=[0,1,0], edgecolor=[0,0,0])
# 第三个用红色
plt.hist(hints_2, bins=50, color=[1,0,0], edgecolor=[0,0,0])
plt.show()

输出如下:
Drawing 9.png

图像虽然画出来了,但是有重叠的地方因为遮挡的关系,绿色和蓝色的分布看不见了,这个时候我们可以使用 plt.hist 函数的 alpha 属性,来让三个图都有一定的透明度,解决这个问题。

修改相关代码如下:

...
# 第一个用蓝色
plt.hist(hints, bins=50, color=[0,0,1], edgecolor=[0,0,0], alpha = 0.5)
# 第二个用绿色
plt.hist(hints_1, bins=50, color=[0,1,0], edgecolor=[0,0,0], alpha = 0.5)
# 第三个用红色
plt.hist(hints_2, bins=50, color=[1,0,0], edgecolor=[0,0,0], alpha = 0.5)
...

输出如下:
Drawing 11.png

增加透明度之后,即便有所遮挡也可以看到了。不过你会发现好像最终的颜色比我们设置的蓝、绿、红要多一些,这背后的原因就是因为我们设置的透明度,不同的颜色区域叠在一块的时候不会完全遮挡,而是会用重叠区域的颜色混合成一种新的颜色。所以看上去好像颜色变多了。

条形图

区别条形图和直方图

条形图,又称为柱状图,有的地方也把横版的称为条形图,竖版的称为柱状图,这里我们统称条形图。

条形图和直方图类似,也是通过一个个细长的长方形来表示数据的频率,所以很容易搞混这两个概念。

我们可以从下面这个表格来理解条形图和直方图的区别。

Drawing 12.png

很多情况下,条形图往往能表示比直方图更多维度的数据。下面是一个常见的条形图的例子。

Drawing 14.png

在上图的例子中,可以看到有两个维度的类别,首先是男和女,然后是这两个类别分别在四个年龄段的分布情况。条形图一定程度上可以表示类似这样离散的三维数据,所以在部分场景,尤其是类别不多的场景下,用条形图来表示就显得非常的直观。像这样的类别我们称之为二维的类别。

绘制条形图

通过 plt.bar 函数可以实现条形图的绘制。接下来我们通过一个实际的案例来学习条形图的绘制,某学校的初中二年级举行了期中考试。五个班的平均分如下:

Drawing 15.png

现在我们通过条形图来比对各个班的平均分的分布情况。

第一步,我们首先将数据导入到 notebook 中,以列的维度。

math_scores = np.array([71,65,70,96,64])
chinese_scores = np.array([84,75,68,83,57])
english_scores = np.array([55,78,76,91,64])

然后就用 plt 的常规模板进行画图。这里的 x 轴的数据源我们直接使用五个类别的值即可,即是一班、二班等。代码如下:

# 创建画布
figure2 = plt.figure(figsize = (105))
figure2.add_subplot(1,1,1)
# 设置轴的属性
plt.xlabel("",fontsize = 14)
plt.ylabel("平均分", fontsize = 14)
plt.xticks(fontsize=14)
plt.yticks(fontsize=14)
plt.title("期中成绩条形图", fontsize=14)
# 设置画布基本属性:1.显示中文  2.显示负号
plt.rcParams["font.sans-serif"] = "SimHei"
plt.rcParams["axes.unicode_minus"] = False
category = ["一班""二班""三班""四班""五班"]
# 语文成绩用蓝色
plt.bar(category, chinese_scores, color=[0,0,1])
# 数学成绩,用绿色
plt.bar(category, math_scores, color=[0,1,0])
# 英语成绩,用红色
plt.bar(category, english_scores,  color=[1,0,0])
plt.show()

输出如下:

Drawing 17.png

条形图已经画出来了,但是我们三门课的形状都画到一起了,图像的堆叠次序是根据画图的顺序来决定的。所以当英语的成绩比前面高的时候,红色的条形就遮住了前面的。

这种情况该怎么办呢?根据之前对条形图的定义,这里我们应该需要将三个图形并列排放。具体的实现方式分为两步:

  1. x 轴的数据源切换为数字,这样才能有宽度的概念;

  2. 在绘制三个 bar 的时候,分别指定它们的相对位置与宽度。

修改后完成的代码如下:

figure2 = plt.figure(figsize = (105))
figure2.add_subplot(1,1,1)
plt.xlabel("",fontsize = 14)
plt.ylabel("平均分", fontsize = 14)
plt.xticks(fontsize=14)
plt.yticks(fontsize=14)
plt.title("期中成绩条形图", fontsize=14)
plt.rcParams["font.sans-serif"] = "SimHei"
plt.rcParams["axes.unicode_minus"] = False
category = ["一班""二班""三班""四班""五班"]
# 新增代码,使用 index 数组来代替类别
index_category = np.arange(len(category))
# 新增代码,指定每条条形的宽度
bar_width = 0.25
# 指定宽度为 bar_width, 并且绘制在 index - bar_width 的区域,也就是每组图的左边
plt.bar(index_category - bar_width, chinese_scores, width=bar_width, color=[0,0,1])
# 指定宽度为 bar_width, 并且绘制在 index 的区域,也就是每组图的中间
plt.bar(index_category, math_scores, width=bar_width, color=[0,1,0])
# 指定宽度为 bar_width, 并且绘制在 index + bar_width 的区域,也就是每组图的右边
plt.bar(index_category + bar_width, english_scores, width=bar_width, color=[1,0,0])
plt.show()

输出如下:

Drawing 19.png

这样,就能够比较明确地反映出学生期中考试的考试情况了,明显四班(序号 3)的成绩优于其他班,并且英语的考试成绩整体优于另外两科。

水平条形图

水平条形图和普通的条形图逻辑都是类似的,只是绘制的方向变成了自左向右,绘制的函数名称为 plt.barh。其实就相当于把普通条形图的坐标轴的 x 轴和 y 轴调换一下。不过有区别的是,水平条形图一般不用于比较二维的类别,因为没有普通的条形图清晰。水平条形图多用于一维的类别。

比如下面的例子中,我们单独将各个班的数学成绩用水平条形图来展示:

figure3 = plt.figure(figsize = (105))
figure3.add_subplot(1,1,1)
plt.xlabel("",fontsize = 14)
plt.ylabel("平均分", fontsize = 14)
plt.xticks(fontsize=14)
plt.yticks(fontsize=14)
plt.title("数学成绩条形图", fontsize=14)
plt.rcParams["font.sans-serif"] = "SimHei"
plt.rcParams["axes.unicode_minus"] = False
category = ["一班""二班""三班""四班""五班"]
# 绘制横版条形图
plt.barh(category, math_scores, color=[1,0,1])
plt.show()

输出如下:
Drawing 21.png

堆叠条形图

堆叠条形图和之前我们画的条形图,多个条叠在一起的图不同。堆叠条形图也是解决二维类别的数据,和普通条形图核心的区别是不同子类的图形是叠在一起的(上下叠在一起,不是重叠)。堆叠条形图的好处是可以清晰地反应每个子类数据的占比。

绘制堆叠条形图的方式和普通条形图一样,也是通过 plt.bar 函数,区别只是堆叠条形图在绘制上叠的数据时,需要额外指定 bottom 参数。我们通过一个例子来学习一下。

某公司销售部门统计了最近四周的签单数据,目前在职的销售一共两名,销售 A 的签单数为 [10,23, 5, 11] ,销售 B 的签单数为 [3,12,6, 5]。 我们通过堆叠条形图来展示两个销售对于部门总销量的占比,代码如下所示:

figure4 = plt.figure(figsize = (105))
figure4.add_subplot(1,1,1)
plt.xlabel("",fontsize = 14)
plt.ylabel("签单量", fontsize = 14)
plt.xticks(fontsize=14)
plt.yticks(fontsize=14)
plt.title("签单堆叠条形图", fontsize=14)
plt.rcParams["font.sans-serif"] = "SimHei"
plt.rcParams["axes.unicode_minus"] = False
# 横轴数据
category = ["第一周""第二周""第三周""第四周"]
# 两个销售各周的销量
sales_a = [10,23511]
sales_b = [3,12,65]
# 首先画销售A, 用紫色
plt.bar(category, sales_a, color=[1,0,1])
# 然后用蓝色画销售B,并指定 bottom为销售a的数据
plt.bar(category, sales_b, color=[0,0,1], bottom=sales_a)
plt.show()

输出如下:
Drawing 23.png

可以看到销售 A(紫色)和销售 B(蓝色)的数据在同一个条形上展示了,事实上撇开两个销售的占比不谈,单看条形图的高度反映的就是该部门的总签单量,然后不同的颜色代表两个销售各自对总签单量的共享。所以从上图中不难看出,销售 A 的贡献明显更大一些。

饼图

本讲最后一节是饼图,饼图虽然在表示内容的维度与丰富度上相比直方图和条形图会差一些。但是在反映数据占比的可理解性上是最好的,这也是为什么在很多数据分析的报告中做占比分析最常见的就是饼图。

通过 plt.pie 函数可以绘制饼图。pie 函数由以下几个关键参数实现功能。

  • x:饼图每一块的占比列表,列表有几个元素就代表饼图分几块。

  • explode:凸出显示,也是一个列表,和 x 一一对应,代表具体某一块是否要凸出显示。

  • colors:列表,和 x 一一对应,代表每一块的颜色。

  • labels:标签文本列表,和 x 一一对应,代表每一块的标题。

  • autopct:代表百分比文本的格式。

  • startangle: 默认饼图是从角度为 0 的位置逆时针开始画,这里可以指定初始角度。

  • labeldistance:文本标签距离饼图的距离。

  • pctdistance:百分比标签距离饼图中心的距离。

接下来我们通过一个案例来学习饼图的绘制方式。

假设我们对一个年级的学生,统计历史上得过三好学生称号的情况,完全没得过的占比为 40%, 得过一次的占比为 30%, 二次的为 25%, 三次以上的为 5%。现在我们用饼图表示这个数据,并将得过三次以上的凸出显示。

代码如下:

# 创建画布
figure5 = plt.figure(figsize = (105))
figure5.add_subplot(1,1,1)
plt.rcParams["font.sans-serif"] = "SimHei"
plt.rcParams["axes.unicode_minus"] = False
# 设置饼图数据源
category = ["没有得过""得过一次""得过二次""得过三次以上"]
size = [4030255# 用四种不同的颜色绘制不同的部分
color = ["r""g""b""c"]
explode = [0,0,0,0.1]
# 绘制饼图
plt.pie(size,explode=explode,colors=color,labels=category,labeldistance= 1.1, autopct="%1.1f%%", startangle=90,pctdistance=0.6)
plt.show()

执行之后输出如下:
Drawing 25.png

小结

至此,今天我们的课程就学习完毕了。我们一起来回顾一下今天学习的内容。

首先我们学习了直方图的概念以及绘制的方式。直方图的关键就是对连续数据进行分组统计。主要的技术点如下:

  • 通过 plt.hist 函数可以实现绘制直方图;

  • 通过 edgecolor 属性可以设置直方图的边框颜色;

  • 通过 alpha 属性可以再绘制多个直方图的时候半透明显示,互不干扰。

然后我们学习了条形图,首先对比了条形图和直方图的区别。然后展示了条形图的绘制技巧。主要包括:

  • 通过 plt.bar 绘制条形图;

  • 通过 width 参数,以及对 x 轴的数据加偏移的形式,来实现多个子类的条形图并排显示;

  • 通过 plt.barh 绘制水平条形图;

  • 通过 plt.bar 配合 bottom 属性来绘制堆叠条形图。

最后,我们学习了饼图的绘制方式,plt.pie 方法的参数比较多。核心的就是占比列表(x 轴数据源),以及对应的文字标题信息、颜色信息、凸出显示信息等。

课后习题:

将“堆叠条形图”小节的销售的案例,用饼图绘制。因为有 4 周的数据,所以需要绘制 4 个饼图。


答案:

# 数据源
week_category = ["第一周""第二周""第三周""第四周"]
sales_a = [10,23511]
sales_b = [3,12,65]
# 创建画布
figure6 = plt.figure(figsize = (1010))
plt.rcParams["font.sans-serif"] = "SimHei"
plt.rcParams["axes.unicode_minus"] = False
# 循环画子图
for i in range(0,4):
    figure6.add_subplot(2,2,i + 1)
    plt.title(week_category[i])
    # 设置饼图数据源
    category = ["销售A""销售B"]
    size = [sales_a[i], sales_b[i]] 
    color = ["b""g"]
    explode = [0,0]
    # 绘制饼图
    plt.pie(size,explode=explode,colors=color,labels=category,labeldistance= 1.1, autopct="%1.1f%%", startangle=90,pctdistance=0.6)
# 展示
plt.show()

输出如下:
Drawing 27.png


  • 0
    点赞
  • 1
    收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:编程工作室 设计师:CSDN官方博客 返回首页
评论

打赏作者

办公模板库 素材蛙

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值