常见图形绘制
Matplotlib 能够绘制折线图、散点图、柱状图、直方图、饼图。
我们需要知道不同的统计图的意义,以此来决定选择哪种统计图来呈现我们的数据。
折线图
以折线的上升或下降来表示统计数量的增减变化的统计图
特点:能够显示数据的变化趋势,反映事物的变化情况。(变化)
api:plt.plot(x, y)
柱状图(条形图)
排列在工作表的列或行中的数据可以绘制到柱状图中。
特点:绘制连离散的数据,能够一眼看出各个数据的大小,比较数据之间的差别。(统计/对比)
api:plt.bar(x, width, align='center', **kwargs)
应用场景:数量统计,频率统计。
条形图的绘制方式跟折线图非常的类似,只不过是换成了plt.bar
方法。plt.bar
方法有以下常用参数:
x
:一个数组或者列表,代表需要绘制的条形图的 x 轴的坐标点。height
:一个数组或者列表,代表需要绘制的条形图 y 轴的坐标点。width
:每一个条形图的宽度,默认是 0.8 的宽度。bottom
:y
轴的基线,默认是 0,也就是距离底部为 0.align
:对齐方式,默认是center
,也就是跟指定的x
坐标居中对齐,还有为edge
,靠边对齐,具体靠右边还是靠左边,看width
的正负。color
:条形图的颜色。
返回值为 BarContainer
,是一个存储了条形图的容器,而条形图实际上的类型是 matplotlib.patches.Rectangle
对象。
更多参考:https://matplotlib.org/api/_as_gen/matplotlib.pyplot.bar.html
条形图的绘制
比如现在有2019
年贺岁片票房的数据
数据来源:https://piaofang.maoyan.com/dashboard
# 票房单位亿元
movies = {
"流浪地球": 40.78,
"飞驰人生": 15.77,
"疯狂的外星人": 20.83,
"新喜剧之王": 6.10,
"廉政风云": 1.10,
"神探蒲松龄": 1.49,
"小猪佩奇过大年": 1.22,
"熊出没·原始时代": 6.71
}
用条形图绘制每部电影及其票房的代码如下:
import matplotlib.pyplot as plt
import random
# 设置显示中文字体
plt.rcParams['font.sans-serif'] = ['SimHei'] # 指定默认字体
# 设置正常显示符号
plt.rcParams['axes.unicode_minus'] = False
plt.figure(figsize=(20, 8), dpi=100)
movies = {
"流浪地球": 40.78,
"飞驰人生": 15.77,
"疯狂的外星人": 20.83,
"新喜剧之王": 6.10,
"廉政风云": 1.10,
"神探蒲松龄": 1.49,
"小猪佩奇过大年": 1.22,
"熊出没·原始时代": 6.71
}
plt.bar(range(len(movies)), list(movies.values()))
plt.xticks(range(len(movies)), list(movies.keys()))
plt.grid()
效果图如下:
其中 xticks
和 yticks
的用法跟之前的折线图一样。这里新出现的方法是 bar
,bar
常用的有 3 个参数,分别是 x
(x 轴的坐标点), y
(y 轴的坐标点)以及 width
(条形的宽度)。
横向条形图
横向条形图需要使用plt.barh
这个方法跟bar
非常的类似,只不过把方向进行旋转。参数跟bar
类似,但也有区别。如下:
y
:数组或列表,代表需要绘制的条形图在y
轴上的坐标点。width
:数组或列表,代表需要绘制的条形图在x
轴上的值(也就是长度)。height
:条形图的高度,默认是 0.8。left
:条形图的基线,也就是距离 y 轴的距离。- 其他参数跟
bar
一样。
返回值也是BarContainer
容器对象。
还是以以上数据为例,将电影名和票房反转一下。示例代码如下:
import matplotlib.pyplot as plt
plt.figure(figsize=(20, 8), dpi=100)
movies = {
"流浪地球": 40.78,
"飞驰人生": 15.77,
"疯狂的外星人": 20.83,
"新喜剧之王": 6.10,
"廉政风云": 1.10,
"神探蒲松龄": 1.49,
"小猪佩奇过大年": 1.22,
"熊出没·原始时代": 6.71
}
plt.barh(range(len(movies)), list(movies.values()))
plt.yticks(range(len(movies)), list(movies.keys())) # 修改轴坐标
plt.grid()
效果图如下:
分组条形图
现在有一组数据,是 2019 年春节贺岁片前五天的电影票房记录。 示例代码如下:
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] # 指定默认字体
plt.rcParams['axes.unicode_minus'] = False
movies = {
"流浪地球": [2.01, 4.59, 7.99, 11.83, 16],
"飞驰人生": [3.19, 5.08, 6.73, 8.10, 9.35],
"疯狂的外星人": [4.07, 6.92, 9.30, 11.29, 13.03],
"新喜剧之王": [2.72, 3.79, 4.45, 4.83, 5.11],
"廉政风云": [0.56, 0.74, 0.83, 0.88, 0.92],
"神探蒲松龄": [0.66, 0.95, 1.10, 1.17, 1.23],
"小猪佩奇过大年": [0.58, 0.81, 0.94, 1.01, 1.07],
"熊出没·原始时代": [1.13, 1.96, 2.73, 3.42, 4.05]
}
plt.figure(figsize=(20, 8))
width = 0.75
bin_width = width / 5
ind = range(0, len(movies))
movie_data = list(movies.values())
print(movie_data)
every_day = []
for i in range(len(movie_data[0])):
every_day.append([
movie_data[0][i],
movie_data[1][i],
movie_data[2][i],
movie_data[3][i],
movie_data[4][i],
movie_data[5][i],
movie_data[6][i],
movie_data[7][i],
])
print(every_day)
# 第一种方案
# plt.bar([i - bin_width * 2 for i in ind], every_day[0], width=bin_width, label='第一天')
# plt.bar([i - bin_width for i in ind], every_day[1], width=bin_width, label='第二天')
# plt.bar(ind, every_day[2], width=bin_width, label='第三天')
# plt.bar([i + bin_width for i in ind], every_day[3], width=bin_width, label='第四天')
# plt.bar([i + bin_width * 2 for i in ind], every_day[4], width=bin_width, label='第五天')
# 第二种方案
for index in range(len(every_day)):
day_tickets = every_day[index]
xs = [i - (bin_width * (2 - index)) for i in ind]
plt.bar(xs, day_tickets, width=bin_width, label="第%d 天" % (index + 1))
# 添加坐标上的数字
for ticket, x in zip(day_tickets, xs):
plt.annotate(ticket, xy=(x, ticket), xytext=(x - 0.1, ticket + 0.1))
# 设置图例
plt.legend()
plt.ylabel("单位:亿")
plt.title("春节前 5 天电影票房记录")
# 设置 x 轴的坐标
plt.xticks(ind, movies.keys())
plt.grid(True)
plt.show()
示例图如下:
堆叠条形图
堆叠条形图,是将一组相关的条形图堆叠在一起进行比较的条形图。比如以下案例:
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] # 指定默认字体
plt.rcParams['axes.unicode_minus'] = False
# 准备数据
menMeans = (20, 35, 30, 35, 27)
womenMeans = (25, 32, 34, 20, 25)
groupNames = ('G1', 'G2', 'G3', 'G4', 'G5')
# 生成序号
xs = range(len(menMeans))
plt.bar(xs, menMeans)
plt.bar(xs, womenMeans, bottom=menMeans)
plt.xticks(xs, groupNames)
plt.show()
效果图如下:
在绘制女性得分的条形图的时候,因为要堆叠在男性得分的条形图上,所以使用到了一个 bottom
参数,就是距离x
轴的距离。通过对贴条形图,我们就可以清楚的知道,哪一个队伍的综合排名是最高的,并且在每个队伍中男女的得分情况。
案例-对比每部电影的票房收入
电影数据如下图所示:
准备数据
movie_name = ['雷神 3:诸神黄昏', '正义联盟', '东方快车谋杀案', '寻梦环游记',
'全球风暴', '降魔传', '追捕', '七十七天', '密战', '狂兽', '其它']
y = [73853, 57767, 22354, 15969, 14839, 8725, 8716, 8318, 7916, 6764, 52222]
绘制柱状图
import matplotlib.pyplot as plt
# 0. 准备数据
# 电影名字
movie_name = [
'雷神 3:诸神黄昏', '正义联盟', '东方快车谋杀案', '寻梦环游记', '全球风暴',
'降魔传', '追捕', '七十七天', '密战', '狂兽', '其它']
# 横坐标
x = range(len(movie_name))
# 票房数据
y = [73853, 57767, 22354, 15969, 14839, 8725, 8716, 8318, 7916, 6764, 52222]
# 1. 创建画布
plt.figure(figsize=(20, 8), dpi=100)
# 2. 绘制柱状图
plt.bar(x, y, width=0.5,
color=['b', 'r', 'g', 'y', 'c', 'm', 'y', 'k', 'c', 'g', 'b'])
# 2.1b 修改 x 轴的刻度显示
plt.xticks(x, movie_name)
# 2.2 添加网格显示
plt.grid(linestyle="--", alpha=0.5)
# 2.3 添加标题
plt.title("电影票房收入对比")
# 3. 显示图像
plt.show()
散点图
用两组数据构成多个坐标点,考察坐标点的分布,判断两变量之间是否存在某种关联或总结坐标点的分布模式。
特点:判断变量之间是否存在数量关联趋势,展示离群点(分布规律)
api:plt.scatter(x, y)
散点图也叫 X-Y 图,它将所有的数据以点的形式展现在直角坐标系上,以显示变量之间的相互影响程度,点的位置由变量的数值决定。
通过观察散点图上数据点的分布情况,我们可以推断出变量间的相关性。如果变量之间不存在相互关系,那么在散点图上就会表现为随机分布的离散的点,如果存在某种相关性,那么大部分的数据点就会相对密集并以某种趋势呈现。数据的相关关系主要分为:正相关(两个变量值同时增长)、负相关(一个变量值增加另一个变量值下降)、不相关、线性相关、指数相关等,表现在散点图上的大致分布如下图所示。那些离点集群较远的点我们称为离群点或者异常点。
示例图如下:
绘制散点图
散点图的绘制,使用的是plt.scatter
方法,这个方法有以下参数:
x,y
:分别是 x 轴和 y 轴的数据集。两者的数据长度必须一致。s
:点的尺寸。如果是一个具体的数字,那么散点图的所有点都是一样大小,如果是一个序列,那么这个序列的长度应该和 x 轴数据量一致,序列中的每个元素代表每个点的尺寸。c
:点的颜色。可以为具体的颜色,也可以为一个序列或者是一个cmap
对象。marker
:标记点,默认是圆点,也可以换成其他的。- 其他参数:https://matplotlib.org/api/_as_gen/matplotlib.pyplot.scatter.html
散点图示例
'''1.简单示例'''
import matplotlib.pyplot as plt
import numpy as np
N = 10
x = np.random.rand(N)
y = np.random.rand(N)
plt.scatter(x, y)
plt.show()
'''2.随机改变点的大小'''
N = 10
x = np.random.rand(N)
y = np.random.rand(N)
size = (30 * np.random.rand(N)) ** 2
plt.scatter(x, y, s=size)
plt.show()
'''3.随机更改颜色,透明度为0.5'''
N = 10
x = np.random.rand(N)
y = np.random.rand(N)
size = (30 * np.random.rand(N)) ** 2
color = np.random.rand(N)
plt.scatter(x, y, s=size, c=color, alpha=0.5)
plt.show()
'''4.更改散点形状'''
N = 10
x = np.random.rand(N)
y = np.random.rand(N)
size = (30 * np.random.rand(N)) ** 2
color = np.random.rand(N)
plt.scatter(x, y, s=size, c=color, alpha=0.5, marker='^')
plt.show()
'''5.绘制两组数据'''
N = 10
x1 = np.random.rand(N)
y1 = np.random.rand(N)
x2 = np.random.rand(N)
y2 = np.random.rand(N)
plt.scatter(x1, y1, alpha=0.5, marker='^')
plt.scatter(x2, y2, alpha=0.5, marker='o')
plt.show()
'''6.增加图例'''
N = 10
x1 = np.random.rand(N)
y1 = np.random.rand(N)
x2 = np.random.rand(N)
y2 = np.random.rand(N)
plt.scatter(x1, y1, alpha=0.5, marker='^', label='triangle')
plt.scatter(x2, y2, alpha=0.5, marker='o', label='circle')
plt.legend(loc='best')
plt.show()
案例:探究房屋面积和房屋价格的关系
房屋面积数据:
x = [225.98, 247.07, 253.14, 457.85, 241.58, 301.01, 20.67, 288.64,
163.56, 120.06, 207.83, 342.75, 147.9, 53.06,
224.72, 29.51, 21.61, 483.21, 245.25, 399.25, 343.35]
房屋价格数据:
y = [196.63, 203.88, 210.75, 372.74, 202.41, 247.61, 24.9, 239.34,
140.32, 104.15, 176.84, 288.23, 128.79, 49.64, 191.74, 33.1,
30.74, 400.02, 205.35, 330.64, 283.45]
import matplotlib.pyplot as plt
# 0. 准备数据
x = [225.98, 247.07, 253.14, 457.85, 241.58, 301.01, 20.67, 288.64, 163.56,
120.06, 207.83, 342.75, 147.9, 53.06, 224.72, 29.51, 21.61, 483.21,
245.25, 399.25, 343.35]
y = [196.63, 203.88, 210.75, 372.74, 202.41, 247.61, 24.9, 239.34, 140.32,
104.15, 176.84, 288.23, 128.79, 49.64, 191.74, 33.1, 30.74, 400.02,
205.35, 330.64, 283.45]
# 1. 创建画布
plt.figure(figsize=(20, 8), dpi=100)
# 2. 绘制散点图
plt.scatter(x, y)
# 3. 显示图像
plt.show()
饼图
用于表示不同分类的占比情况,通过弧度大小来对比各种分类。饼图可以看成数据的合计后的占比,适合突出表现份额。
特点:分类数据的占比情况(占比)
api:plt.pie(x, labels=,autopct=,colors)
饼图是一个划分为几个扇形的圆形统计图表,用于描述量、频率或百分比之间的相对关系的。 在matplotlib
中,可以通过plt.pie
来实现,其中的参数如下:
x
:饼图的比例序列。labels
:饼图上每个分块的名称文字。explode
:设置某几个分块是否要分离饼图。autopct
:设置比例文字的展示方式。比如保留几个小数等。shadow
:是否显示阴影。textprops
:文本的属性(颜色,大小等)。- 其他参数:https://matplotlib.org/api/_as_gen/matplotlib.pyplot.pie.html
返回值:
patches
:饼图上每个分块的对象。texts
:分块的名字文本对象。autotexts
:分块的比例文字对象。
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] # 指定默认字体
plt.rcParams['axes.unicode_minus'] = False
# 设置绘画的主题风格
plt.figure(figsize=(10, 8))
# 服务行业单位个数
edu = [59104, 1467937, 3579974]
labels = ['第一产业', '第二产业', '第三产业']
explode = [0, 0, 0.1, ] # 用于突出第三产业
colors = ['#FEB748', '#EDD25D', '#FE4F54'] # 自定义颜色
plt.axes(aspect='equal') # 保证饼图是圆 不是默认的椭圆
plt.pie(x=edu, # 数据
labels=labels, # 标签名称
autopct='%.2f%%', # 设置百分比格式 保留几位小数
colors=colors, # 使用自定义颜色
labeldistance=1.1, # 设置教育标签与圆心的距离
# startangle=30, # 设置饼图的初始角度 逆时针
textprops={'fontsize': 12, 'color': 'k'}, # 设置文本标签的属性值
explode=explode, # 突出显示第三产业
pctdistance=0.5, # 占比和图距离
)
plt.title('服务行业单位个数')
plt.show()
案例
假如现在我们有一组数据,用来记录各个操作系统的市场份额的。那么用饼状图表示如下:
import matplotlib.pyplot as plt
# 设置绘画的主题风格
plt.figure(figsize=(10, 8))
# 构造数据
edu = [0.2515, 0.3724, 0.3336, 0.0368, 0.0057]
labels = ['中专', '大专', '本科', '硕士', '其他']
explode = [0, 0.1, 0, 0, 0] # 用于突出大专
colors = ['#FEB748', '#EDD25D', '#FE4F54', '#51B4FF', '#dd5555'] # 自定义颜色
plt.axes(aspect='equal') # 保证饼图是圆 不是默认的椭圆
plt.pie(x=edu, # 数据
labels=labels, # 标签名称
autopct='%.2f%%', # 设置百分比格式 保留几位小数
colors=colors, # 使用自定义颜色
# radius = 1, # 设置饼图半径
# center = (80,80), # 设置圆点
labeldistance=1.1, # 设置教育水平标签与圆心的距离
startangle=30, # 设置饼图的初始角度 逆时针
textprops={'fontsize': 12, 'color': 'k'}, # 设置文本标签的属性值
explode=explode, # 突出显示大专人群
pctdistance=0.5, # 占比和图距离
# shadow=True, # 阴影
# frame=True # frame 显示
)
plt.title('失信人员组成')
雷达图
雷达图(Radar Chart)又被叫做蜘蛛网图,适用于显示三个或更多的维度的变量的强弱情况。比如英雄联盟中某个影响的属性(法术伤害,物理防御等),或者是某个企业在哪些业务方面的投入等,都可以用雷达图方便的表示。
在matplotlib.pyplot
中,可以通过plt.polar
来绘制雷达图,这个方法的参数跟plt.plot
非常的类似,只不过是x
轴的坐标点应该为弧度(2*PI=360°)。示例代码如下:
import numpy as np
import matplotlib.pyplot as plt
properties = ['输出', 'KDA', '发育', '团战', '生存']
values = [40, 91, 44, 90, 95, 40]
theta = np.linspace(0, np.pi * 2, 6)
plt.polar(theta, values)
plt.xticks(theta, properties)
plt.fill(theta, values)
plt.show()
效果图如下:
其中有几点需要注意:
- 因为
polar
并不会完成线条的闭合绘制,所以我们在绘制的时候需要在theta
中和values
中在最后多重复添加第 0 个位置的值,然后在绘制的时候就可以和第 1 个点进行闭合了。 polar
只是绘制线条,所以如果想要把里面进行颜色填充,那么需要调用fill
函数来实现。polar
默认的圆圈的坐标是角度,如果我们想要改成文字显示,那么可以通过xticks
来设置。
小结
- 折线图【知道】
- 能够显示数据的变化趋势,反映事物的变化情况。(变化)
- plt.plot()
- 散点图【知道】
- 判断变量之间是否存在数量关联趋势,展示离群点(分布规律)
- plt.scatter()
- 柱状图【知道】
- 绘制连离散的数据,能够一眼看出各个数据的大小,比较数据之间的差别。(统计/对比)
- plt.bar(x, width, align="center")
- 直方图【知道】
- 绘制连续性的数据展示一组或者多组数据的分布状况(统计)
- plt.hist(x, bins)
- 饼图【知道】
- 用于表示不同分类的占比情况,通过弧度大小来对比各种分类
- plt.pie(x, labels, autopct, colors)
附录:中文显示问题解决
解决方案一:
下载中文字体(黑体,看准系统版本)
步骤一:下载 SimHei 字体(或者其他的支持中文显示的字体也行)
步骤二:安装字体
linux 下:拷贝字体到 usr/share/fonts 下:
sudo cp ~/SimHei.ttf /usr/share/fonts/SimHei.ttf
windows 和 mac 下:双击安装
步骤三:删除~/.matplotlib 中的缓存文件
shellcd ~/.matplotlib rm -r *
步骤四:修改配置文件 matplotlibrc
shellvi ~/.matplotlib/matplotlibrc
将文件内容修改为:
font.family : sans-serif font.sans-serif : SimHei axes.unicode_minus : False
解决方案二:
在 Python 脚本中动态设置 matplotlibrc,这样也可以避免由于更改配置文件而造成的麻烦,具体代码如下:
import matplotlib.pyplot as plt
# 设置显示中文字体
plt.rcParams['font.sans-serif'] = ['SimHei'] # 指定默认字体
有时候,字体更改后,会导致坐标轴中的部分字符无法正常显示,此时需要更改 axes.unicode_minus 参数:
# 设置正常显示符号
plt.rcParams['axes.unicode_minus'] = False