在数据分析与机器学习中,我们经常要用到大量的可视化操作。一张制作精美的数据图片,可以展示大量的信息,一图顶千言。
而在可视化中,Matplotlib算得上是最常用的工具。Matplotlib 是 python 最著名的绘图库,它提供了一整套 API,十分适合绘制图表,或修改图表的一些属性,如字体、标签、范围等。
Matplotlib 是一个 Python 的 2D 绘图库,它交互式环境生成出版质量级别的图形。通过 Matplotlib这个标准类库,开发者只需要几行代码就可以实现生成绘图,折线图、散点图、柱状图、饼图、直方图、组合图等数据分析可视化图表。
学习本文之前,需要先自修:NumPy从入门到进阶,pandas从入门到进阶本文中很多的操作在 NumPy从入门到进阶 ,pandas从入门到进阶二文中有详细的介绍,包含一些软件以及扩展库,图片的安装和下载流程,本文会直接进行使用。
下载 M a t p l o t l i b Matplotlib Matplotlib 见博客:matplotlib的安装教程以及简单调用,这里不再赘述
数据可视化其实就是把抽象不容易看出规律的数据转换为人眼更能接受的图片,下面我们来简单的绘制一个图形
import numpy as np
import matplotlib.pyplot as plt
# 横坐标
# 等差数列,把[0,2π]等分成100份
x = np.linspace(0, 2 * np.pi, 100)
# 纵坐标:正弦波; x:Numpy数组
y = np.sin(x)
# 画线形图
plt.plot(x, y)
# 设置横坐标的范围是 -1 ~ 10
plt.xlim(-1, 10)
# 设置纵坐标的范围是 -1.5 ~ 1.5
plt.ylim(-1.5, 1.5)
# 设置网格线,网格线颜色设为绿色,透明度为0.5,线的样式为虚线:--,宽度为2
plt.grid(color = 'green', alpha = 0.5, linestyle = '--', linewidth = 2)
plt.plot(x, y)
标题其实就是图片的名字,即这个图片是什么,表达的是什么样的含义
# 数据还是刚刚绘制的正弦图
plt.plot(x, y)
# 图的标题设置为 Sin(x),字体大小设为18,颜色设为红色,标题离图片的间距为20
plt.title('Sin(x)', fontsize = 18, color = 'red', pad = 20)
# 数据还是刚刚绘制的正弦图
plt.plot(x, y)
# 图的标题设置为正弦波,字体大小设为18,颜色设为红色,标题离图片的间距为20
plt.title('正弦波', fontsize = 18, color = 'red', pad = 20)
首先先来查看一下我们电脑都有哪些字体:
# 找到电脑的字体
from matplotlib import font_manager
fm = font_manager.FontManager()
# 用列表生成式获取字体
[font.name for font in fm.ttflist]
我们的电脑有很多的字体,这个截图只展示了一部分字体,其中有英文字体,中文字体…
我们可以找找有没有 K a i T i KaiTi KaiTi(楷体)
浏览器页面搜索(Google浏览器),按下 Ctrl + F,输入 KaiTi:
或者你搜宋体什么的都是可以的,一般电脑都会有这些字体,接下来回到我们的报错代码上,我们来设置我们的字体:
# 数据还是刚刚绘制的正弦图
plt.plot(x, y)
# 设置字体为楷体
plt.rcParams['font.family'] = 'KaiTi'
# 图的标题设置为正弦波,字体大小设为18,颜色设为红色,标题离图片的间距为20
plt.title('正弦波', fontsize = 18, color = 'red', pad = 20)
这下 正弦波 被显示出来了,可是还是有报错,这波啊~,这波叫做对了但没完全对,我们通过观察图像可以发现是负号有出了问题:
害,多大点事儿嘛,有 bug 咋们就接着改就完了:
# 数据还是刚刚绘制的正弦图
plt.plot(x, y)
# 设置字体为楷体
plt.rcParams['font.family'] = 'KaiTi'
# 设置数字的负号
plt.rcParams['axes.unicode_minus'] = False
# 图的标题设置为正弦波,字体大小设为18,颜色设为红色,标题离图片的间距为20
plt.title('正弦波', fontsize = 18, color = 'red', pad = 20)
ok,这下所有的 bug 就完美的解决了,但是总看的不得劲,字体有点小了,看起来费眼睛,我们来把所有的字体都变得大一点:
# 数据还是刚刚绘制的正弦图
plt.plot(x, y)
# 设置字体为楷体
plt.rcParams['font.family'] = 'KaiTi'
# 设置数字的负号
plt.rcParams['axes.unicode_minus'] = False
# 把所有的字体都设置成 28 大小
plt.rcParams['font.size'] = 28
# 图的标题设置为正弦波,字体大小设为18,颜色设为红色,标题离图片的间距为20
plt.title('正弦波', fontsize = 18, color = 'red', pad = 20)
好家伙,又出问题了,我们的字体虽然都变大了(图片显示的不明显,读者自己执行一下代码会有明显的变化),但是相对应的我们的图变小了,我们来设置一下图片的尺寸:
# 设置图片的大小
plt.figure(figsize = (12, 9))
# 数据还是刚刚绘制的正弦图
plt.plot(x, y)
# 设置字体为楷体
plt.rcParams['font.family'] = 'KaiTi'
# 设置数字的负号
plt.rcParams['axes.unicode_minus'] = False
# 把所有的字体都设置成 28 大小
plt.rcParams['font.size'] = 28
# 图的标题设置为正弦波,字体大小设为18,颜色设为红色,标题离图片的间距为20
plt.title('正弦波', fontsize = 18, color = 'red', pad = 20)
标签对应到我们的上图之中其实就是横纵坐标
plt.figure(figsize = (9, 6))
plt.plot(x, y)
plt.rcParams['font.family'] = 'KaiTi'
plt.rcParams['axes.unicode_minus'] = False
plt.rcParams['font.size'] = 28
plt.title('正弦波', fontsize = 18, color = 'red', pad = 20)
# 设置横坐标为 X
plt.xlabel('X')
# 设置纵坐标为 f(x) = sin(x)
plt.ylabel('f(x) = sin(x)')
plt.figure(figsize = (9, 6))
plt.plot(x, y)
plt.rcParams['font.family'] = 'KaiTi'
plt.rcParams['axes.unicode_minus'] = False
plt.rcParams['font.size'] = 28
plt.title('正弦波', fontsize = 18, color = 'red', pad = 20)
# 设置横坐标为 X
plt.xlabel('X')
# 设置纵坐标为 f(x) = sin(x)
plt.ylabel('f(x) = sin(x)', rotation = 0)
plt.figure(figsize = (9, 6))
plt.plot(x, y)
plt.rcParams['font.family'] = 'KaiTi'
plt.rcParams['axes.unicode_minus'] = False
plt.rcParams['font.size'] = 28
plt.title('正弦波', fontsize = 18, color = 'red', pad = 20)
# 设置横坐标为 X
plt.xlabel('X')
# 设置纵坐标为 f(x) = sin(x)
plt.ylabel('f(x) = sin(x)', rotation = 0, horizontalalignment = 'right')
接下来我们设置刻度,上图中的 0 2 4 6 -1.0 -0.5 0.0 0.5 1.0
其实就是刻度,因为我们现在描绘的是正弦波,对于正弦波而言,我们 y y y轴上的刻度其实给-1 0 1
就可以了:
我们的横坐标刻度对于正弦波而言一般是 0 , π 2 , π , 3 π 2 , 2 π 0, \frac{\pi}{2}, \pi,\frac{3\pi}{2},2π 0,2π,π,23π,2π:
plt.figure(figsize = (9, 6))
plt.plot(x, y)
plt.rcParams['font.family'] = 'KaiTi'
plt.rcParams['axes.unicode_minus'] = False
plt.rcParams['font.size'] = 28
plt.title('正弦波', fontsize = 18, color = 'red', pad = 20)
plt.xlabel('X')
plt.ylabel('f(x) = sin(x)', rotation = 0, horizontalalignment = 'right')
# 设置y轴上的刻度为:-1 0 1
plt.yticks([-1, 0, 1])
# 设置x轴的刻度
plt.xticks([0, np.pi / 2, np.pi, 1.5 * np.pi, 2 * np.pi])
这好像和我们希望的不太一样,这编译器太实诚了,直接把 π \pi π 带入计算了,我们希望的显然不是这样,我们希望 π \pi π 用希腊字母进行显示:
plt.figure(figsize = (9, 6))
plt.plot(x, y)
plt.rcParams['font.family'] = 'KaiTi'
plt.rcParams['axes.unicode_minus'] = False
plt.rcParams['font.size'] = 28
plt.title('正弦波', fontsize = 18, color = 'red', pad = 20)
plt.xlabel('X')
plt.ylabel('f(x) = sin(x)', rotation = 0, horizontalalignment = 'right')
# 设置y轴上的刻度为:-1 0 1
plt.yticks([-1, 0, 1])
# 设置x轴的刻度
plt.xticks([0, np.pi / 2, np.pi, 1.5 * np.pi, 2 * np.pi],
[0, r'$\frac{\pi}{2}$', r'$\pi$', r'$\frac{3\pi}{2}$', r'$2\pi$'])
plt.figure(figsize = (9, 6))
plt.plot(x, y)
plt.rcParams['font.family'] = 'KaiTi'
plt.rcParams['axes.unicode_minus'] = False
plt.rcParams['font.size'] = 28
plt.title('正弦波', fontsize = 18, color = 'red', pad = 20)
plt.xlabel('X')
plt.ylabel('f(x) = sin(x)', rotation = 0, horizontalalignment = 'right')
# 设置y轴上的刻度为:-1 0 1
plt.yticks([-1, 0, 1])
# 设置x轴的刻度
plt.xticks([0, np.pi / 2, np.pi, 1.5 * np.pi, 2 * np.pi],
[0, r'$\frac{\pi}{2}$', r'$\pi$', r'$\frac{3\pi}{2}$', r'$2\pi$'],
color = 'red')
图例就是在同一个表中显示多个图,我们要区分它们所对应的小框框:
import numpy as np
import matplotlib.pyplot as plt
# x轴
x = np.linspace(0, 2 * np.pi)
# y轴
y = np.sin(x) # 正弦
# 绘制线形图
# 调整尺寸
plt.figure(figsize=(9,6))
# 绘制正弦波
plt.plot(x,y)
# 绘制余弦波
plt.plot(x,np.cos(x))
plt.legend(['Sin', 'Cos'],fontsize = 18, # 字体大小
loc = 'center', # 图例居中
ncol = 2, # 图例展示时2个为一行
# 设置图例的位置 [x,y,width,height]
bbox_to_anchor = [0, 1.05, 1, 0.2])
# 我们规定图形左下角为(0,0),右上角为(1,1)
# 表示图例的位置在(0,1.05)的位置,且我们设置了居中
脊柱移动翻译成大白话就是黑色边框的移动
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-np.pi, np.pi, 50)
plt.rcParams['axes.unicode_minus'] = False
plt.figure(figsize = (9 ,6))
# plot绘制了两个图形,x-y成对出现就可以!!!
plt.plot(x,np.sin(x),x,np.cos(x))
# 获取当前视图
ax = plt.gca()
# 右边和上面脊柱消失,白色就是消失
ax.spines['right'].set_color('white') # 白色
ax.spines['top'].set_color('#FFFFFF') # 白色
# 设置下面左边脊柱位置,data表示数据,axes表示相对位置0~1
ax.spines['bottom'].set_position(('data', 0)) # 中间(竖直)
ax.spines['left'].set_position(('data' , 0)) # 中间(水平)
plt.yticks([-1,0,1],labels = ['-1', '0', '1'],fontsize = 18)
plt.xticks([-np.pi, -np.pi / 2, np.pi / 2, np.pi],
labels = [r'$-\pi$', r'$-\frac{\pi}{2}$', r'$\frac{\pi}{2}$', r'$\pi$'],
fontsize = 18)
我们可以把我们绘制好的图形进行保存:
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-np.pi, np.pi, 50)
plt.rcParams['axes.unicode_minus'] = False
plt.figure(figsize = (9 ,6))
# plot绘制了两个图形,x-y成对出现就可以!!!
plt.plot(x,np.sin(x),x,np.cos(x))
# 获取当前视图
ax = plt.gca()
# 右边和上面脊柱消失,白色就是消失
ax.spines['right'].set_color('white') # 白色
ax.spines['top'].set_color('#FFFFFF') # 白色
# 设置下面左边脊柱位置,data表示数据,axes表示相对位置0~1
ax.spines['bottom'].set_position(('data', 0)) # 中间(竖直)
ax.spines['left'].set_position(('data' , 0)) # 中间(水平)
plt.yticks([-1,0,1],labels = ['-1', '0', '1'],fontsize = 18)
plt.xticks([-np.pi, -np.pi / 2, np.pi / 2, np.pi],
labels = [r'$-\pi$', r'$-\frac{\pi}{2}$', r'$\frac{\pi}{2}$', r'$\pi$'],
fontsize = 18)
# 图片保存到当前路径下,命名为 1.png
plt.savefig('./1.png')
我们在保存的时候,可以修改屏幕像素密度: d p i dpi dpi(默认为100)
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-np.pi, np.pi, 50)
plt.rcParams['axes.unicode_minus'] = False
plt.figure(figsize = (9 ,6))
# plot绘制了两个图形,x-y成对出现就可以!!!
plt.plot(x,np.sin(x),x,np.cos(x))
# 获取当前视图
ax = plt.gca()
# 右边和上面脊柱消失,白色就是消失
ax.spines['right'].set_color('white') # 白色
ax.spines['top'].set_color('#FFFFFF') # 白色
# 设置下面左边脊柱位置,data表示数据,axes表示相对位置0~1
ax.spines['bottom'].set_position(('data', 0)) # 中间(竖直)
ax.spines['left'].set_position(('data' , 0)) # 中间(水平)
plt.yticks([-1,0,1],labels = ['-1', '0', '1'],fontsize = 18)
plt.xticks([-np.pi, -np.pi / 2, np.pi / 2, np.pi],
labels = [r'$-\pi$', r'$-\frac{\pi}{2}$', r'$\frac{\pi}{2}$', r'$\pi$'],
fontsize = 18)
# 图片保存到当前路径下,命名为 2.png,像素密度设为 300
plt.savefig('./2.png', dpi = 300)
从两张图片的大小上其实就能看出清晰度的高低,因为保存的是同一张图
当然,我们也可以保存成 p d f pdf pdf
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-np.pi, np.pi, 50)
plt.rcParams['axes.unicode_minus'] = False
plt.figure(figsize = (9 ,6))
# plot绘制了两个图形,x-y成对出现就可以!!!
plt.plot(x,np.sin(x),x,np.cos(x))
# 获取当前视图
ax = plt.gca()
# 右边和上面脊柱消失,白色就是消失
ax.spines['right'].set_color('white') # 白色
ax.spines['top'].set_color('#FFFFFF') # 白色
# 设置下面左边脊柱位置,data表示数据,axes表示相对位置0~1
ax.spines['bottom'].set_position(('data', 0)) # 中间(竖直)
ax.spines['left'].set_position(('data' , 0)) # 中间(水平)
plt.yticks([-1,0,1],labels = ['-1', '0', '1'],fontsize = 18)
plt.xticks([-np.pi, -np.pi / 2, np.pi / 2, np.pi],
labels = [r'$-\pi$', r'$-\frac{\pi}{2}$', r'$\frac{\pi}{2}$', r'$\pi$'],
fontsize = 18)
# 图片保存到当前路径下,格式为 pdf,命名为 1.pdf
plt.savefig('./1.pdf')
如果你觉得白底太过于单调,我们在设置字体大小的时候可以设置颜色(改变边框的颜色):
# 设置一个绿边框
plt.figure(figsize = (9 ,6), facecolor = 'green')
# 获取当前视图
ax = plt.gca()
# 底色改为绿色
ax.set_facecolor('green')
# 查看所有颜色
plt.colormaps()
下图代表我们可以使用的颜色:
接下来我们结合代码去进行讲解:
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0, 2 * np.pi, 20)
y1 = np.sin(x)
y2 = np.cos(x)
# 设置颜色,线型,点型
# 绘制x,y1的图形,颜色是 indigo,线形为'-.',图像上点的类型是五边形'p
plt.plot(x, y1, color = 'indigo', ls = '-.', marker = 'p')
# 绘制x,y2的图形,颜色是 #FF00EE,线形为'-',图像上点的类型是圆圈'o'
plt.plot(x, y2, color = '#FF00EE', ls = '--', marker = 'o')
# 绘制x,y1+y2的图形,颜色是(0.2, 0.7, 0.2),线形为':',图像上点的类型是五角星'*'
plt.plot(x, y1 + y2, color = (0.2, 0.7, 0.2), marker = '*',ls = ':')
# 绘制x,y1+2*y2的图形,线宽为3,透明度为0.7,颜色为 orange
plt.plot(x, y1 + 2 * y2, linewidth = 3, alpha = 0.7, color = 'orange') # 线宽、透明度
# 参数连用
# 绘制x,2*y1-y2的图形,b代表color:blue;o代表marker:'o';--代表ls:'--'
plt.plot(x, 2 * y1 - y2, 'bo--')
import numpy as np
import pandas as pd
def f(x):
return np.exp(-x) * np.cos(2 * np.pi * x)
x = np.linspace(0, 5, 50)
plt.figure(figsize = (9, 6))
plt.plot(x, f(x), color = 'purple', # 线颜色
marker = 'o', # 图像中的点的形状
ls = '--', # 线形
lw = 2, # 线宽
alpha = 0.6, # 透明度
markerfacecolor = 'red', # 点颜色
markersize = 10, # 图像中点的大小
markeredgecolor = 'green', # 点边缘颜色
markeredgewidth = 3) # 点边缘宽度
plt.xticks(size = 18) # 设置刻度大小
plt.yticks(size = 18)
import numpy as np
import matplotlib.pyplot as plt
plt.figure(figsize = (9, 6), facecolor = 'gray')
def fun(x):
for i in range(1, 7):
y = np.sin(x - 0.5 * i) * (7 - i)
plt.plot(x, y)
# 画图范围从 0~14,分成200份
x= np.linspace(0, 14, 200)
fun(x)
# 获取当前视图
ax = plt.gca()
ax.set_facecolor('gray')
# 添加网格线
plt.grid(color = 'white', linestyle = '--')
要求:
首先我们需要下载一个 Excel 文件:
链接: https://pan.baidu.com/s/1ns8p3xD_EVS2GNNKApDtLg?pwd=eu4u
提取码: eu4u
下载完成之后,把该文件和我们的代码放到同一个文件夹下,这一操作我们在之前的博客中已经反复说到,这里就不再进行演示
注:代码处于运行中将显示:
下列代码运行几十秒甚至几分钟都是正常的,耐心等待运行结果即可。
我们先来加载我们的数据
%%time
import pandas as pd
import matplotlib.pyplot as plt
df = pd.read_excel('./PM2.5.xlsx')
display(df.head(10))
df.shape
# 根据 城市,季节进行分组,获取PM2.5的数据并求平均
df.groupby(by = ['城市', '季节'])['PM2.5'].mean()
数据看起来不太舒服,把它变成 D a t a F r a m e DataFrame DataFrame 格式,并保留两位小数
df2 = df.groupby(by = ['城市', '季节'])[['PM2.5']].mean().round(2)
df2
df2 = df2.unstack(level = -1)
df2
df2.columns = df2.columns.droplevel(level = 0)
df2
df2 = df2[list('春夏秋冬')]
df2 = df2.loc[['北京', '上海', '广州', '成都', '沈阳']]
df2
绘图:
plt.rcParams['font.family'] = 'KaiTi'
plt.rcParams['font.size'] = 18
df2.plot.bar(figsize = (12, 9))
plt.grid(color = 'gray', ls = '--')
创建子视图可以一个视图一个视图的创建,也可以多个视图一起创建:
单图创建:
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0, 2 * np.pi)
# 创建子视图:2行1列第1个视图
ax = plt.subplot(2, 1, 1)
ax.plot(x, np.sin(x))
# 创建子视图:2行1列第2个视图
ax = plt.subplot(2, 1, 2)
ax.plot(x, np.cos(x))
多图一起创建:
# 一次创造多个视图
fig, axes = plt.subplots(2, 2) # 2行2列:四个图
# 索引从0开始
axes[0, 0].plot(x, np.sin(x), color = 'red')
axes[0, 1].plot(x, np.sin(x), color = 'green')
axes[1, 0].plot(x, np.cos(x), color = 'purple')
axes[1, 1].plot(x, np.cos(x))
下面附上一个完整的代码供读者理解:
遇到无法理解的地方可以看后面的代码解释,还是不理解的地方可以评论区留言(有问必答)
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-np.pi, np.pi, 50)
y = np.sin(x)
# 子视图1
plt.figure(figsize = (9, 6))
ax = plt.subplot(221) # 两行两列第一个子视图
ax.plot(x, y, color = 'red')
ax.set_facecolor('green') # 调用子视图设置方法,设置子视图整体属性
# 子视图2
ax = plt.subplot(2, 2, 2) # 两行两列第二个子视图
line, = ax.plot(x, -y) # 返回绘制对象
line.set_marker('*') # 调用对象设置方法,设置属性
line.set_markerfacecolor('red')
line.set_markeredgecolor('green')
line.set_markersize(10)
# 子视图3
ax = plt.subplot(2, 1, 2) # 两行一列第二行视图
plt.sca(ax) # 设置当前视图
x = np.linspace(-np.pi, np.pi, 200)
plt.plot(x, np.sin(x * x), color = 'red')
# 第15行
# line, = ax.plot(x, -y) # 返回绘制对象
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-np.pi, np.pi, 50)
y = np.sin(x)
# 子视图1
plt.figure(figsize = (9, 6))
ax = plt.subplot(221) # 两行两列第一个子视图
ax.plot(x, y, color = 'red')
ax.set_facecolor('green') # 调用子视图设置方法,设置子视图整体属性
# 子视图2
ax = plt.subplot(2, 2, 2) # 两行两列第二个子视图
ax.plot(x, -y) # 返回绘制对象
ax.plot(x, -y)
其实就是返回了一句话:[
我们可以发现这句话使用的是列表,我们取出这句话可以用:
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-np.pi, np.pi, 50)
y = np.sin(x)
# 子视图1
plt.figure(figsize = (9, 6))
ax = plt.subplot(221) # 两行两列第一个子视图
ax.plot(x, y, color = 'red')
ax.set_facecolor('green') # 调用子视图设置方法,设置子视图整体属性
# 子视图2
ax = plt.subplot(2, 2, 2) # 两行两列第二个子视图
line = ax.plot(x, -y) # 返回绘制对象
line[0]
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-np.pi, np.pi, 50)
y = np.sin(x)
# 子视图1
plt.figure(figsize = (9, 6))
ax = plt.subplot(221) # 两行两列第一个子视图
ax.plot(x, y, color = 'red')
ax.set_facecolor('green') # 调用子视图设置方法,设置子视图整体属性
# 子视图2
ax = plt.subplot(2, 2, 2) # 两行两列第二个子视图
line, = ax.plot(x, -y) # 返回绘制对象
line
还有一个很容易晕掉的细节:就是我们在绘图的时候几行几列第几个是什么意识:
我们就拿上述代码去说明:
我们假想有一块空白的板子是供我们绘图的,下面来看上述代码:
ax = plt.subplot(221) # 两行两列第一个子视图
这行代码的意思是把我们假想的这块白板,划分称为两行两列的板子:
然后这个图片占据的是第一个板子的位置,对于板子位置我们有如下规定:
板子的编号从1开始,而非从0开始,从左至右,从上至下依次命名板子
所以对上面这个板子,板子的编号为:
所以我们最后图像所显示的其实就是左上角的位置。
接着来看我们的第二个图:
ax = plt.subplot(2, 2, 2) # 两行两列第二个子视图
这下就好理解啦,还是把之前的空白板子分成两行两列,然后编号为2,即右上角。
接着我们来看第三个图:
ax = plt.subplot(2, 1, 2) # 两行一列第二行视图
把我们的空白板子分成两行一列,那么就是下图的形式:
然后我们把第三个图片放到第二个位置,不难想到,该图的第二个位置其实就是对应的我们分成两行两列的 3,4 位置,所以三个图像最终绘制的结果就是:
所谓嵌套,其实就是在图形中继续画图:
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-np.pi, np.pi, 25)
y = np.sin(x)
fig = plt.figure(figsize = (9, 6)) # 创建视图
plt.plot(x,y)
# 嵌套方式一,axes轴域(横纵坐标范围),子视图
# x,y,width,height
ax = plt.axes([0.2, 0.55, 0.3, 0.3]) # 参数含义[left, bottom, width, height]
ax.plot(x, y, color = 'g')
# 嵌套方式二
ax = fig.add_axes([0.55, 0.2, 0.3, 0.3]) # 使用视图对象添加子视图
ax.plot(x, y, color = 'r')
每张图片都是均匀展示的
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0,2*np.pi)
# sharex:所有小图共享x轴 sharey:表示所有小图共享y轴 坐标轴以所有小图中范围最大的进行显示
fig, ((ax11,ax12,ax13), (ax21,ax22,ax23),(ax31,ax32,ax33)) = plt.subplots(3, 3)
# 也可通过plt.subplot() 一个个添加子视图
fig.set_figwidth(9)
fig.set_figheight(6)
ax11.plot(x,np.sin(x))
ax12.plot(x,np.cos(x))
ax13.plot(x,np.tanh(x))
ax21.plot(x,np.tan(x))
ax22.plot(x,np.cosh(x))
ax23.plot(x,np.sinh(x))
ax31.plot(x,np.sin(x) + np.cos(x))
ax32.plot(x,np.sin(x * x) + np.cos(x * x))
ax33.plot(x,np.sin(x) * np.cos(x))
# 紧凑显示,边框会比较小,可以注释掉该行查看效果
plt.tight_layout()
plt.show()
上图中代码运行所展示的就是均匀分布的结果,可以看出每张图片所占的空间大小都是均等的,如下面图片的展示结果,就是不均匀分布:
为显示如上图的不均匀分布,我们有三种方法
方法一:
import numpy as np
import matplotlib.pyplot as plt
# 需要导入gridspec模块
x = np.linspace(0, 2 * np.pi, 200)
fig = plt.figure(figsize = (12, 9))
# 使用切片方式设置子视图
ax1 = plt.subplot(3, 1, 1) # 视图对象添加子视图
ax1.plot(x, np.sin(10 * x))
# 设置ax1的标题,xlim、ylim、xlabel、ylabel等所有属性现在只能通过set_属性名的方法设置
ax1.set_title('ax1_title') # 设置小图的标题
ax2 = plt.subplot(3, 3, (4, 5))
ax2.set_facecolor('green')
ax2.plot(x, np.cos(x),color = 'red')
ax3 = plt.subplot(3, 3, (6, 9))
ax3.plot(x,np.sin(x) + np.cos(x))
ax4 = plt.subplot(3, 3, 7)
ax4.plot([1, 3], [2, 4])
ax5 = plt.subplot(3, 3, 8)
ax5.scatter([1, 2, 3], [0, 2, 4])
ax5.set_xlabel('ax5_x',fontsize = 12)
ax5.set_ylabel('ax5_y',fontsize = 12)
plt.show()
方法二:
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0, 2 * np.pi, 100)
plt.figure(figsize = (12, 9))
# 子视图1
ax1 = plt.subplot2grid(shape = (3, 3),# 布局形状
loc = (0, 0), # 布局绘制位置
colspan = 3) # 跨几列
ax1.plot(x, np.sin(10 * x))
# 设置ax1的标题,xlim、ylim、xlabel、ylabel等所有属性现在只能通过set_属性名的方法设置
ax1.set_title('ax1_title') # 设置小图的标题
# 子视图2
ax2 = plt.subplot2grid((3, 3), (1, 0), colspan = 2) # 跨两列
ax2.set_facecolor('green')
ax2.plot(x,np.cos(x),color = 'red')
# 子视图3
ax3 = plt.subplot2grid((3, 3), (1, 2), rowspan = 2) # 跨两行
ax3.plot(x,np.sin(x) + np.cos(x))
# 子视图4
ax4 = plt.subplot2grid((3, 3), (2, 0))
ax4.plot([1, 3], [2, 4])
# 子视图5
ax5 = plt.subplot2grid((3, 3), (2, 1))
ax5.scatter([1, 2, 3], [0, 2, 4])
ax5.set_xlabel('ax5_x',fontsize = 12)
ax5.set_ylabel('ax5_y',fontsize = 12)
import numpy as np
import matplotlib.pyplot as plt
# 需要导入gridspec模块
import matplotlib.gridspec as gridspec
x = np.linspace(0, 2 * np.pi, 200)
fig = plt.figure(figsize = (12, 9))
# 将整个视图分成3x3布局
gs = gridspec.GridSpec(3, 3)
# 使用切片方式设置子视图
ax1 = fig.add_subplot(gs[0, :]) # 视图对象添加子视图
ax1.plot(x,np.sin(10 * x))
# 设置ax1的标题,xlim、ylim、xlabel、ylabel等所有属性现在只能通过set_属性名的方法设置
ax1.set_title('ax1_title') # 设置小图的标题
ax2 = plt.subplot(gs[1, :2]) # 模块调用
ax2.set_facecolor('green')
ax2.plot(x,np.cos(x), color = 'red')
# 从第一行到最后,占1、2两行,后面的2表示只占用第二列,也就是最后的一列
ax3 = plt.subplot(gs[1:, 2])
ax3.plot(x,np.sin(x) + np.cos(x))
# 倒数第一行,只占第0列这一列
ax4 = plt.subplot(gs[-1, 0])
ax4.plot([1, 3], [2, 4])
# 倒数第一行,只占倒数第二列,由于总共三列,所以倒数第二列就是序号1的列
ax5 = plt.subplot(gs[-1, -2])
ax5.scatter([1, 2, 3], [0, 2, 4])
ax5.set_xlabel('ax5_x',fontsize = 12)
ax5.set_ylabel('ax5_y',fontsize = 12)
plt.show()
有时候,有两个轴是不够用的,我们经常会见到如下的图形,下述图形的样式就是双轴显示:
就比如我们在绘制正弦波和指数函数的时候,让他们在一个 y y y轴上显示是不合理的:
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-np.pi, np.pi, 50)
y = np.sin(x)
plt.plot(x, y, color = 'blue')
_ = plt.yticks(np.linspace(-1, 1, 11), color = 'blue')
y2 = np.exp(x)
plt.plot(x, y2, color = 'red')
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-np.pi, np.pi, 50)
y = np.sin(x)
plt.plot(x, y, color = 'blue')
_ = plt.yticks(np.linspace(-1, 1, 11), color = 'blue')
# 获取当前视图
ax = plt.gca()
# 双胞胎,相当于两个x轴,并且是合到一起的
# 这两个x轴对应的是两个y轴
ax2 = ax.twinx()
# 其中一个视图纵坐标范围是 -1~1.0,另一个视图范围是0~25
# 刻度是自适应的,当然也可以进行调整
# 默认向 ax2这个子视图中绘制
y2 = np.exp(x)
plt.plot(x, y2, color = 'red')
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-np.pi, np.pi, 50)
y = np.sin(x)
plt.plot(x, y, color = 'blue')
_ = plt.yticks(np.linspace(-1, 1, 11), color = 'blue')
# 获取当前视图
ax = plt.gca()
# 双胞胎,相当于两个x轴,并且是合到一起的
# 这两个x轴对应的是两个y轴
ax2 = ax.twinx()
# 其中一个视图纵坐标范围是 -1~1.0,另一个视图范围是0~25
# 刻度是自适应的,当然也可以进行调整
# 默认向 ax2这个子视图中绘制
y2 = np.exp(x)
plt.plot(x, y2, color = 'red')
# 规定刻度
_ = plt.yticks(np.arange(0, 26, 5), color = 'red')
常用函数如下:
Pyplot函数 | API方法 | 描述 |
---|---|---|
text() | mpl.axes.Axes.text() | 在Axes对象的任意位置添加文字 |
xlabel() | mpl.axes.Axes.set_xlabel() | 为X轴添加标签 |
ylabel() | mpl.axes.Axes.set_ylabel() | 为Y轴添加标签 |
title() | mpl.axes.Axes.set_title() | 为Axes对象添加标题 |
legend() | mpl.axes.Axes.legend() | 为Axes对象添加图例 |
annnotate() | mpl.axes.Axes.annotate() | 为Axes对象添加注释(箭头可选) |
figtext() | mpl.figure.Figure.text() | 在Figure对象的任意位置添加文字 |
suptitle() | mpl.figure.Figure.suptitle() | 为Figure对象添加中心化的标题 |
import numpy as np
import matplotlib.pyplot as plt
# 字体属性
font = {'fontsize': 20,
'family': 'KaiTi',
'color': 'red',
'weight': 'bold'} # bold表示字体加粗
x = np.linspace(0.0, 5.0, 100)
y = np.cos(2 * np.pi * x) * np.exp(-x)
plt.figure(figsize = (9, 6))
plt.plot(x, y, 'k') # k代表黑色
# 视图的标题
plt.title('exponential decay', fontdict = font, pad = 20)
# figure的大标题
plt.suptitle('指数衰减', y = 1.05, fontdict = font, fontsize = 30)
# 向图片中绘制文本
plt.text(x = 2, y = 0.65, # 横纵坐标位置
s = r'$\cos(2 \pi t) \exp(-t)$', # 文本内容
fontsize = 25) # 文本大小
plt.xlabel('time (s)')
plt.ylabel('voltage (mV)')
plt.show()
import matplotlib.pyplot as plt
import numpy
# 随机生成数字,10行2列(x,y)
loc = np.random.randint(0, 10,size = (10, 2))
plt.figure(figsize=(10, 10))
# 画图: loc[:, 0]:所有行都获取,索引0--->横坐标
# loc[:, 1]:所有行都获取,索引1--->纵坐标
# g*:g代表的是绿色,*代表的是点的形状是五角星
# ms = 20:点的大小
plt.plot(loc[:, 0], loc[:, 1], 'g*', ms = 20)
plt.grid(True)
# 路径
way = np.arange(10) # 0、1、2、...、9索引
np.random.shuffle(way) # 洗牌,打乱顺序
# 开始连点:0-->1-->2-->...-->9
# for 循环:少一个:10个点连9条线
for i in range(0, len(way) - 1):
start = loc[way[i]] # 起点
end = loc[way[i + 1]] # 终点
# 画箭头:
plt.arrow(start[0], start[1], # x = start[0], y = start[1]:起点的(x, y)
end[0] - start[0], # 水平方向上移动的距离
end[1] - start[1], # 竖直方向上移动的距离
head_width = 0.2, lw = 2,# 箭头长度,箭尾线宽
length_includes_head = True) # 长度计算包含箭头箭尾
# 绘制编号:0 1 2 3... 8
plt.text(start[0], start[1], s = i, fontsize = 18, color = 'red')
# 绘制编号:9
if i == len(way) - 2:
plt.text(end[0], end[1], s = i + 1, fontsize = 18, color = 'red')
import numpy as np
import matplotlib.pyplot as plt
# 数据创建
fig, ax = plt.subplots()
x = np.arange(0.0, 5.0, 0.01)
y = np.cos(2 * np.pi * x)
line, = ax.plot(x, y, lw = 2)
# 注释:
ax.annotate('local max', # 文本内容
xy = (2, 1), # 箭头指向位置
xytext = (3, 1.5), # 文本位置
# 箭头属性
arrowprops = dict(facecolor = 'black', # 箭头的颜色
shrink = 0.05)) # 箭头两端收缩的百分比(占总长)
ax.annotate('local min',
xy = (2.5, -1),
xytext = (4, -1.8),
arrowprops = dict(facecolor = 'black',
width = 2, # 箭头宽度
headwidth = 10, # 箭头头部宽度
headlength = 10, # 箭头头部长度
shrink = 0.1)) # 箭头两端收缩的百分比(占总长)
ax.annotate('median',
xy = (2.25, 0),
xytext = (0.5, -1.8),
arrowprops = dict(arrowstyle = '-|>'), # 箭头样式
# 按下 Shift+Tab查看提示:-|>:head_length=0.4,head_width=0.2
fontsize = 20)
# y轴范围调为(-2, 2)
ax.set_ylim(-2, 2)
import matplotlib.pyplot as plt
def annotate_con_style(ax, connectionstyle):
x1, y1 = 3,2
x2, y2 = 8,6
ax.plot([x1, x2], [y1, y2], ".")
ax.annotate(text = '',
xy = (x1, y1), # 相当于B点,arrow head
xytext = (x2, y2), # 相当于A点,arrow tail
arrowprops = dict(arrowstyle = '->', color = 'red',
shrinkA = 5,shrinkB = 5,
connectionstyle = connectionstyle))
ax.text(.05, 0.95, connectionstyle.replace(",", "\n"),
transform = ax.transAxes, # 相对坐标
ha = "left", va = "top") # 指定对齐方式
# 常用箭头连接样式
fig, axs = plt.subplots(3, 5, figsize = (9, 6))
annotate_con_style(axs[0, 0], "angle3,angleA=90,angleB=0")
annotate_con_style(axs[1, 0], "angle3,angleA=0,angleB=90")
annotate_con_style(axs[2, 0], "angle3,angleA = 0,angleB=150")
annotate_con_style(axs[0, 1], "arc3,rad=0.")
annotate_con_style(axs[1, 1], "arc3,rad=0.3")
annotate_con_style(axs[2, 1], "arc3,rad=-0.3")
annotate_con_style(axs[0, 2], "angle,angleA=-90,angleB=180,rad=0")
annotate_con_style(axs[1, 2], "angle,angleA=-90,angleB=180,rad=5")
annotate_con_style(axs[2, 2], "angle,angleA=-90,angleB=10,rad=5")
annotate_con_style(axs[0, 3], "arc,angleA=-90,angleB=0,armA=30,armB=30,rad=0")
annotate_con_style(axs[1, 3], "arc,angleA=-90,angleB=0,armA=30,armB=30,rad=5")
annotate_con_style(axs[2, 3], "arc,angleA=-90,angleB=0,armA=0,armB=40,rad=0")
annotate_con_style(axs[0, 4], "bar,fraction=0.3")
annotate_con_style(axs[1, 4], "bar,fraction=-0.3")
annotate_con_style(axs[2, 4], "bar,angle=180,fraction=-0.2")
for ax in axs.flat:
# 设置轴域刻度
ax.set(xlim = (0, 10), ylim = (0, 10), xticks = [], yticks = [], aspect = 1)
fig.tight_layout(pad = 0.2)
要求:
首先我们需要下载一个 Excel 文件:
链接: https://pan.baidu.com/s/1nSw6wbOEg8GpP19WQ7ggxw?pwd=53ph
提取码: 53ph
下载完成之后,把该文件和我们的代码放到同一个文件夹下,这一操作我们在之前的博客中已经反复说到,这里就不再进行演示
注:代码处于运行中将显示:
下列代码运行几十秒甚至几分钟都是正常的,耐心等待运行结果即可。
我们先来加载我们的数据
%%time
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
plt.rcParams['font.family'] = 'KaiTi'
plt.rcParams['font.size'] = 18
df = pd.read_excel('./PM2.5.xlsx')
接下来绘制年份的数据:
# 添加子视图
fig, axes = plt.subplots(2, 2, figsize = (16, 12))
# 按照城市年份分组,获取PM2.5,计算平均值并保留2位小数
df2 = df.groupby(by = ['城市', '年份'])[['PM2.5']].mean().round(2)
# 数据重塑
df2 = df2.unstack(level = 0) # 城市作为列索引
df2.columns = df2.columns.droplevel(0) # 删除 PM2.5 这个列索引
df2 = df2[['北京', '上海', '广州', '沈阳', '成都']] # 调整列索引的顺序
df2.plot(ax = axes[0, 0]) # 左上角
df3 = df.groupby(by = ['城市', '季节'])[['PM2.5']].mean().round(2)
# 数据重塑
df3 = df3.unstack(level = 0)
# 调整行索引顺序
df3 = df3.loc[list('春夏秋冬')]
# 删除列索引 PM2.5
df3.columns = df3.columns.droplevel(0)
# 调整列索引顺序
df3 = df3[['北京', '上海', '广州', '沈阳', '成都']]
# 第一行第二列的子视图(右上角)
df3.plot(ax = axes[0, 1])
df4 = df.groupby(by = ['城市', '月份'])[['PM2.5']].mean().round(2)
# 数据重塑
df4 = df4.unstack(level = 0)
df4.columns = df4.columns.droplevel(0)
df4 = df4[['北京', '上海', '广州', '沈阳', '成都']]
ax = df4.plot(ax = axes[1, 0])
# 设置
months = ['一月', '二月', '三月', '四月', '五月', '六月',
'七月', '八月', '九月', '十月', '十一月', '十二月']
ax.set_xticks(np.arange(1, 13)) # 设置刻度(x轴)
_ = ax.set_xticklabels(months, rotation = 60) # 旋转60°
绘制小时:
df5 = df.groupby(by = ['城市', '小时'])[['PM2.5']].mean().round(2)
# 数据重塑
df5 = df5.unstack(level = 0)
df5.columns = df5.columns.droplevel(0)
df5 = df5[['北京', '上海', '广州', '沈阳', '成都']]
ax = df5.plot(ax = axes[1, 1])
_ = ax.set_xticks(np.arange(0, 24))
最后我们来保存我们的图片:
plt.savefig('./homework7.png')
import numpy as np
import matplotlib.pyplot as plt
y = np.random.randint(0, 10, size = 15)
# 一图多线
plt.figure(figsize = (9, 6))
# 只给了y,不给x,则x有默认值:0、1、2、3、...
plt.plot(y, marker = '*', color = 'r')
plt.plot(y.cumsum(), marker = 'o')
# 多图布局
fig,axs = plt.subplots(2, 1)
# 设置宽高
fig.set_figwidth(9)
fig.set_figheight(6)
axs[0].plot(y, marker = '*', color = 'red')
axs[1].plot(y.cumsum(), marker = 'o')
import numpy as np
import matplotlib.pyplot as plt
labels = ['G1', 'G2', 'G3', 'G4', 'G5','G6'] # 级别
# 生成数据
men_means = np.random.randint(20, 35, size = 6)
women_means = np.random.randint(20, 35, size = 6)
men_std = np.random.randint(1, 7, size = 6)
women_std = np.random.randint(1, 7, size = 6)
width = 0.35 # 柱状图中柱的宽度
plt.bar(labels, # 横坐标
men_means, # 柱高
width, # 线宽
yerr = men_std, # 误差条(标准差)
label = 'Men') # 标签
plt.bar(labels,
women_means,
width,
yerr = women_std,
bottom = men_means, # 把女生画成男生的上面
# 没有上一行代码柱状图会发生重叠覆盖,读者可以自行尝试
label = 'Women')
plt.ylabel('Scores')
plt.title('Scores by group and gender')
plt.legend()
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
# 创造数据
labels = ['G1', 'G2', 'G3', 'G4', 'G5','G6'] # 级别
men_means = np.random.randint(20, 35,size = 6)
women_means = np.random.randint(20, 35,size = 6)
x = np.arange(len(men_means))
plt.figure(figsize = (9, 6))
# 把男生的柱状图整体左移 width / 2
rects1 = plt.bar(x - width / 2, men_means, width)
# 把女生的柱状图整体右移 width / 2
rects2 = plt.bar(x + width / 2, women_means, width)
# 设置标签标题,图例
plt.ylabel('Scores')
plt.title('Scores by group and gender')
plt.xticks(x, labels)
plt.legend(['Men','Women'])
# 放置文本 text
def set_label(rects):
for rect in rects:
height = rect.get_height() # 获取高度
plt.text(x = rect.get_x() + rect.get_width() / 2, # 水平坐标
y = height + 0.5, # 竖直坐标
s = height, # 文本
ha = 'center') # 水平居中
set_label(rects1)
set_label(rects2)
# 设置紧凑布局
plt.tight_layout()
对于极坐标,我们先来绘制一个普通的直角坐标系下的直线
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0, 4 * np.pi, 200)
y = np.linspace(0, 2, 200)
plt.plot(x, y)
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0, 4 * np.pi, 200)
y = np.linspace(0, 2, 200)
plt.subplot(111, projection = 'polar')
plt.plot(x, y)
接下来,为了让这个极坐标图更加的美观,我们对其属性进行一些设置:
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0, 4 * np.pi, 200)
y = np.linspace(0, 2, 200)
ax = plt.subplot(111, projection = 'polar', facecolor = 'lightgreen')
plt.plot(x, y)
# 设置
ax.set_rmax(3) # 最大半径设置为3
ax.set_rticks([0.5, 1, 1.5, 2]) # 设置刻度
ax.grid(True) # 设置网格线
import numpy as np
import matplotlib.pyplot as plt
# 分成8份 (0~360)
N = 8
# 横坐标
x = np.linspace(0.0, 2 * np.pi, N, endpoint = False)
# 纵坐标
y = np.random.randint(3, 15, size = N)
# 宽度(8个柱子沾满圆)
width = np.pi / 4
# 8个柱子随机生成颜色
colors = np.random.rand(8,3)
# polar表示极坐标
ax = plt.subplot(111, projection = 'polar')
ax.bar(x, y, width = width, color = colors)
绘制的直方图其实就是一个概率分布,直方图可以看成很多个柱子的柱状图
import numpy as np
import matplotlib.pyplot as plt
mu = 100 # 平均值
sigma = 15 # 标准差
x = np.random.normal(loc = mu, scale = 15, size = 10000)
fig, ax = plt.subplots()
# 直方图一般用于描述统计性的数据
# 数据量比较大,通过绘制直方图,看出数据内部的关系
# density = True 统计的是概率
# density = False 统计数字在某个范围内的次数
n, bins, patches = ax.hist(x, 200, density = True) # 直方图
# 概率密度函数
y = ((1 / (np.sqrt(2 * np.pi) * sigma)) *
np.exp(-0.5 * (1 / sigma * (bins - mu)) ** 2))
plt.plot(bins, y, '--')
plt.xlabel('Smarts')
plt.ylabel('Probability density')
plt.title(r'Histogram of IQ: $\mu=100$, $\sigma=15$')
# 紧凑布局
fig.tight_layout()
import numpy as np
import matplotlib.pyplot as plt
# 正态分布
data = np.random.normal(size = (500, 4))
lables = ['A', 'B', 'C', 'D']
# 用Matplotlib画箱线图
# 黄色的线就是中位数,红色的圆点是异常值
_ = plt.boxplot(data, 1, 'ro', labels = lables)
import numpy as np
import matplotlib.pyplot as plt
data = np.random.randn(100, 2)
s = np.random.randint(100, 300, size = 100)
color = np.random.randn(100)
plt.scatter(data[:, 0], # 横坐标
data[:, 1], # 纵坐标
s = s, # 尺寸
c = color, # 颜色
alpha = 0.5) # 透明度
import numpy as np
import matplotlib.pyplot as plt
# 解决中文字体乱码的问题
plt.rcParams['font.sans-serif'] = 'KaiTi'
labels = ["五星", "四星", "三星", "二星", "一星"] # 标签
percent = [95, 261, 105, 30, 9] # 某市星级酒店数量
# 设置图片大小和分辨率
fig = plt.figure(figsize = (5, 5), dpi = 120)
# 偏移中心量,突出某一部分
# 0.1 表示 10%,自身高度的10%,是一个相对值
explode = (0, 0.1, 0, 0, 0)
# 绘制饼图:autopct显示百分比,这里保留一位小数;shadow控制是否显示阴影
_ = plt.pie(x = percent, # 数据
explode = explode, # 偏移中心量
labels = labels, # 显示标签
autopct = '%0.1f%%', # 显示百分比
shadow = True) # 阴影,3D效果
import numpy as np
import matplotlib.pyplot as plt
plt.figure(figsize = (6, 6))
# 甜甜圈原料
recipe = ["225g flour",
"90g sugar",
"1 egg",
"60g butter",
"100ml milk",
"1/2package of yeast"]
# 原料比例
data = [225, 90, 50, 60, 100, 5]
wedges, texts = plt.pie(data, startangle = 40)
bbox_props = dict(boxstyle = "square,pad=0.3", fc = "w", ec = "k", lw = 0.72)
kw = dict(arrowprops = dict(arrowstyle = "-"),
bbox = bbox_props, va = "center")
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))
ha = {-1 : "right", 1 : "left"}[int(np.sign(x))] # 水平对齐方式
connectionstyle = "angle,angleA=0,angleB={}".format(ang) # 箭头连接样式
kw["arrowprops"].update({"connectionstyle" : connectionstyle}) # 更新箭头连接方式
plt.annotate(recipe[i], xy=(x, y), xytext = (1.35 * np.sign(x), 1.4 * y),
ha = ha, **kw, fontsize = 18, weight = 'bold')
plt.title("Matplotlib bakery: A donut", fontsize = 18, pad = 25)
plt.tight_layout()
import numpy as np
import matplotlib
import matplotlib.pyplot as plt
# 标签
vegetables = ["cucumber", "tomato", "lettuce", "asparagus", "potato", "wheat", "barley"]
farmers = list('ABCDEFG')
# 创建数据,随机数
harvest = np.random.randn(7, 7) * 5 # 农民丰收数据
plt.rcParams['font.size'] = 18
plt.rcParams['font.weight'] = 'heavy'
plt.figure(figsize = (9, 9))
# imshow() 显示图片,因为数值不同,所以图片颜色不同
im = plt.imshow(harvest)
plt.xticks(np.arange(len(farmers)), farmers, rotation = 45, ha = 'right')
plt.yticks(np.arange(len(vegetables)), vegetables)
# 绘制文本
for i in range(len(vegetables)):
for j in range(len(farmers)):
text = plt.text(j, i, round(harvest[i, j], 1),
ha = "center", va = "center", color = 'r')
plt.title("Harvest of local farmers (in tons/year)", pad = 20)
import matplotlib.pyplot as plt
plt.figure(figsize = (9, 6))
days = [1, 2, 3, 4, 5]
sleeping = [7, 8, 6, 11, 7]
eating = [2, 3, 4, 3, 2]
working = [7, 8, 7, 2, 2]
playing = [8, 5, 7, 8, 13]
plt.stackplot(days, sleeping, eating, working, playing)
plt.xlabel('x')
plt.ylabel('y')
plt.title('Stack Plot', fontsize = 18)
plt.legend(['Sleeping', 'Eating', 'Working', 'Playing'],
fontsize = 18)
import numpy as np
import matplotlib.pyplot as plt
# 画图数据
plt.rcParams['font.family'] = 'KaiTi'
labels = np.array(["个人能力", "IQ", "服务意识",
"团队精神", "解决问题能力", "持续学习"])
y = [83, 61, 95, 67, 76, 88]
x = np.linspace(0, 2 * np.pi, len(labels), endpoint = False)
# 首位相接,我们要把 "持续学习" 和 "个人能力相连起来"
y = np.concatenate((y, [y[0]])) # 首尾相接
x = np.concatenate((x, [x[0]])) # 首尾相接
# 用Matplotlib画蜘蛛图
fig = plt.figure(figsize = (6, 6))
ax = fig.add_subplot(111, polar = True)
# 连线
# o:表示形状,圆形
# -:表示实线
# o-:属性连用
ax.plot(x, y, 'o-', linewidth = 2)
ax.fill(x, y, alpha = 0.25) # 填充颜色
# 设置角度
ax.set_thetagrids(x[:-1] * 180 / np.pi,# 角度值
# 由于首位相接时候相当于给x增加了一个元素,现在需要切片去掉这个元素
labels,
fontsize = 18)
_ = ax.set_rgrids([20, 40, 60, 80], fontsize = 18)
创建一个三维空间有两种方法:
方法一:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.axes3d import Axes3D # 3D引擎
plt.rcParams['font.family'] = 'KaiTi'
plt.rcParams['axes.unicode_minus'] = False
x = np.linspace(0, 60, 300)
y = np.sin(x)
z = np.cos(x)
fig = plt.figure(figsize = (9, 6)) # 二维图形
ax3 = Axes3D(fig) # 二维变成了三维
ax3.plot(x, y, z) # 3维折线图
ax3.set_xlabel('X')
ax3.set_ylabel('Y')
ax3.set_zlabel('Z')
方法二:
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.family'] = 'KaiTi'
plt.rcParams['axes.unicode_minus'] = False
x = np.linspace(0, 60, 300)
y = np.sin(x)
z = np.cos(x)
fig = plt.figure(figsize = (9, 6))
ax3 = plt.subplot(111, projection = '3d')
ax3.plot(x, y, z)
ax3.set_xlabel('X')
ax3.set_ylabel('Y')
ax3.set_zlabel('Z')
如果你觉得这个视角不好看,我们还可以调整视角:
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.family'] = 'KaiTi'
plt.rcParams['axes.unicode_minus'] = False
x = np.linspace(0, 60, 300)
y = np.sin(x)
z = np.cos(x)
fig = plt.figure(figsize = (9, 6))
ax3 = plt.subplot(111, projection = '3d')
ax3.plot(x, y, z)
ax3.set_xlabel('X')
ax3.set_ylabel('Y')
ax3.set_zlabel('Z')
# 图形可以调整角度
# 第一个参数是 x,y轴的角度,第二个参数是z轴的角度
ax3.view_init(elev = 30, azim = -80)
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.family'] = 'KaiTi'
plt.rcParams['axes.unicode_minus'] = False
x = np.linspace(0, 60, 300)
y = np.sin(x)
z = np.cos(x)
fig = plt.figure(figsize = (9, 6))
ax3 = plt.subplot(111, projection = '3d')
ax3.set_xlabel('X')
ax3.set_ylabel('Y')
ax3.set_zlabel('Z')
# 散点图
x = np.random.randint(0, 60, size = 20)
y = np.random.randn(20)
z = np.random.randn(20)
ax3.scatter(x, y, z, color = 'red')
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.axes3d import Axes3D # 3D引擎
month = np.arange(1, 5)
# 每个月 4周 每周都会产生数据
# 三个维度:月、周、销量
fig = plt.figure(figsize = (9, 6))
ax3 = Axes3D(fig)
for m in month:
# 每个月都要绘制条形图
ax3.bar(np.arange(1, 5), # 横坐标
np.random.randint(1, 10, size = 4), # 纵坐标
zs = m ,
zdir = 'x', # 在哪个方向上,一排排排列,默认为'z'
alpha = 0.7, # alpha 透明度
width = 0.5) # 条形图的宽度
plt.rcParams['font.family'] = 'KaiTi'
ax3.set_xlabel('月份', fontsize = 18, color = 'red')
ax3.set_xticks(month)
ax3.set_ylabel('周', fontsize = 18, color = 'red')
ax3.set_yticks([1, 2, 3, 4])
ax3.set_zlabel('销量', fontsize = 18, color = 'green')
Seaborn是基于matplotlib的图形可视化python包。它提供了一种高度交互式界面,便于用户能够做出各种有吸引力的统计图表。
Seaborn是在matplotlib的基础上进行了更高级的API封装,从而使得作图更加容易,在大多数情况下使用seaborn能做出很具有吸引力的图,而使用matplotlib就能制作具有更多特色的图。应该把Seaborn视 为matplotlib的补充,而不是替代物。
如果你读过文章:matplotlib的安装教程以及简单调用,那么你只需要在命令行模式中输入:pip install seaborn
即可进行安装,否则你也可以直接输入:pip install seaborn -i https://pypi.tuna.tsinghua.edu.cn/simple
进入命令行模式: W i n d o w s Windows Windows系统:按下键盘上的 W i n d o w s + R Windows + R Windows+R,输入 c m d cmd cmd 后即可进入
如果你读过文章:最详细的Anaconda Installers 的安装【numpy,jupyter】(图+文),那么你无序再安装 S e a b o r n Seaborn Seaborn,安装 A n a c o n d a Anaconda Anaconda 的时候已经安装好了 S e a b o r n Seaborn Seaborn
出现上图所示就是已经安装过的意思,我们可以打开 jupyter 运行如下代码,看是否报错:
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
不报错即为安装成功,那么接下来,就让我来介绍 s e a b o r n seaborn seaborn
import seaborn as sns
sns.set(style = 'darkgrid',context = 'talk',font = 'STKaiti')
s t y l e style style设置,修改主题风格,属性如下:
style | 效果 |
---|---|
darkgrid | 黑色网格(默认) |
whitegrid | 白色网格 |
dark | 黑色背景 |
white | 白色背景 |
ticks | 四周有刻度线的白背景 |
c o n t e x t context context设置,修改大小,属性如下:
context | 效果 |
---|---|
paper | 越来越大越来越粗 |
notebook(默认) | 越来越大越来越粗 |
talk | 越来越大越来越粗 |
poster | 越来越大越来越粗 |
import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
sns.set(style = 'dark',context = 'poster',font = 'STKaiti') # 设置样式
plt.figure(figsize = (9, 6))
x = np.linspace(0, 2 * np.pi, 20)
y = np.sin(x)
sns.lineplot(x = x, y = y, color = 'green', ls = '--')
sns.lineplot(x = x, y = np.cos(x), color = 'red',ls = '-.')
首先我们需要下载几个 c s v csv csv 文件:
链接: https://pan.baidu.com/s/12CkTweXPT-El4z2M93HltQ?pwd=vaks
提取码: vaks
下载完成之后,把该文件和我们的代码放到同一个文件夹下,这一操作我们在之前的博客中已经反复说到,这里就不再进行演示
参数 p a l e t t e palette palette(调色板),用于调整颜色,系统默认提供了六种选择: d e e p , m u t e d , b r i g h t , p a s t e l , d a r k , c o l o r b l i n d deep, muted, bright, pastel, dark, colorblind deep,muted,bright,pastel,dark,colorblind
参数 p a l e t t e palette palette调色板,可以有更多的颜色选择, M a t p l o t l i b Matplotlib Matplotlib为我们提供了多达 178 178 178种,这足够绘图用,可以通过代码print(plt.colormaps())
查看选择
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
print(plt.colormaps())
import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd
# 设置样式
sns.set(style = 'dark', context = 'notebook', font = 'STKaiti')
plt.figure(figsize = (9, 6))
# fmri 这一核磁共振数据
fmri = pd.read_csv('./fmri.csv')
ax = sns.lineplot(x = 'timepoint',y = 'signal',
hue = 'event', # 根据 event 进行分类绘制
style = 'event', # 根据 event 属性分类指定样式
# 如图自动分配成了实现和虚线,●和×
data = fmri,
palette = 'deep', # 画板、颜色
markers = True,
markersize = 10)
plt.xlabel('时间节点',fontsize = 30)
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
data = pd.read_csv('./tips.csv') # 小费
plt.figure(figsize = (9, 6))
sns.set(style = 'darkgrid', context = 'talk')
# 散点图
fig = sns.scatterplot(x = 'total_bill', y = 'tip',
hue = 'time', data = data,
palette = 'autumn', s = 100)
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
plt.figure(figsize = (9, 6))
sns.set(style = 'whitegrid')
tips = pd.read_csv('./tips.csv') # 小费
ax = sns.barplot(x = "day", y = "total_bill",
data = tips,hue = 'sex',
palette = 'colorblind',
capsize = 0.2)
import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd
sns.set(style = 'ticks')
tips = pd.read_csv('./tips.csv')
ax = sns.boxplot(x = "day", y = "total_bill", data = tips, palette = 'colorblind')
import seaborn as sns
import numpy as np
import matplotlib.pyplot as plt
sns.set(style = 'dark')
x = np.random.randn(5000)
sns.histplot(x, kde = True)
import seaborn as sns
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
sns.set(style = 'darkgrid')
tips = pd.read_csv('./tips.csv')
sns.histplot(x = 'total_bill', data = tips, kde = True)
import seaborn as sns
import matplotlib.pyplot as plt
import pandas as pd
sns.set(style = 'darkgrid')
exercise = pd.read_csv('./exercise.csv')
sns.catplot(x = "time", y = "pulse", hue = "kind", data = exercise)
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
plt.figure(figsize = (12, 9))
flights = pd.read_csv('./flights.csv') # 飞行数据
# pivot() 实现了数据重塑,改变了DataFrame的形状
# month 作为行索引,year作为列索引,passengers作为数据
flights = flights.pivot("month", "year", "passengers") # 年,月,乘客
sns.heatmap(flights, annot = True, # 画上数值
fmt = 'd', # 数值为整数
cmap = 'RdBu_r', # 设置颜色
linewidths = 0.5) # 线宽为 0.5
我们最后来说一下数据重塑,在本题的基础上,我们查看一下我们的 f l i g h t s flights flights 数据:
咋们再来重新加载一下数据,看看原始的 f l i g h t s flights flights 数据:
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
flights = pd.read_csv('./flights.csv')
flights
不难看出,上述绘图过程中涉及到了数据重塑:代码:flights = flights.pivot("month", "year", "passengers")
实现了数据的重塑,使得 m o n t h month month 作为行索引, y e r r yerr yerr 作为列索引, p a s s e n g e r s passengers passengers 作为数据。
首先我们需要下载一个 Excel 文件:
链接: https://pan.baidu.com/s/1j2pn0vVN3-wJmSZ-01oiUg?pwd=niye
提取码: niye
下载完成之后,把该文件和我们的代码放到同一个文件夹下,这一操作我们在之前的博客中已经反复说到,这里就不再进行演示
数据查看:
import numpy as np
import pandas as pd
fund = pd.read_excel('./fund.xlsx')
print('基金总数据条目:', fund.shape)
print('基金公司一共有:', fund['公司'].nunique()) # 去重
print('基金总数量是:', fund['基金数量'].sum())
# 计算基金总规模
cnt = fund['基金规模'].str.endswith('亿元') # 判断是否以'亿元'结尾
fund2 = fund[cnt] # 数据筛选
size = fund2['基金规模'].str[: -2].astype('float').sum() # 去掉'亿元'
print('基金总规模是:%0.2f亿元' % (size))
print('查看前五条数据:')
fund.head(5)
import pandas as pd
fund = pd.read_excel('./fund.xlsx')
print('数据清洗前:', fund.shape)
# 过滤基金规模为空的数据
cnt = fund['基金规模'].str.endswith('亿元')
fund = fund[cnt]
# 过滤基金规模小于1亿的数据
cnt2 = fund['基金规模'].str[: -2].astype('float') > 1
fund = fund[cnt2]
# 过滤基金收益为空的数据
cnt3 = fund['基金收益'].str.endswith('%')
fund = fund[cnt3]
print('数据清洗后:', fund.shape)
fund.to_excel('./fund_clean.xlsx', index = False)
fund.head()
数据转换:
import pandas as pd
fund = pd.read_excel('./fund_clean.xlsx')
# 基金规模字符串转变为浮点数
fund['基金规模'] = fund['基金规模'].str[: -2].astype('float')
# 基金收益字符串转变为浮点数
def convert(x):
x = x[: -1]
x = float(x)
return x
fund['基金收益'] = fund['基金收益'].apply(convert)
# 修改列名
fund.columns = ['姓名', '公司', '基金数量', '年', '天', '基金规模(亿元)', '基金收益(%)']
# 数据保存
fund.to_excel('./fund_end.xlsx', index = False)
fund.head(10)
根据基金总规模,进行排序,水平条形图展示前十大公司
根据收益率,对所有数据进行降序排名,绘制前十佳基金经理。并将金额和收益率绘制到图片中。
十大基金公司:
%%time
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
plt.figure(figsize = (12, 9))
sns.set_theme(style = 'darkgrid', context = 'talk', font = 'KaiTi')
fund = pd.read_excel('./fund_end.xlsx')
# 分组聚合
com = fund.groupby(by = '公司')[['基金规模(亿元)']].sum()
# 排序
com.sort_values(by = '基金规模(亿元)',
ascending = False, # 降序排序
inplace = True) # 直接对原数据进行替换
# 行索引重置:变成自然数索引
com.reset_index(inplace = True)
# 画条形图
sns.barplot(x = '基金规模(亿元)', y = '公司', # x轴和y轴
data = com.iloc[: 10], # 切片出来前十个
orient = 'h') # 水平条形图
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
plt.figure(figsize = (12, 9))
sns.set_theme(style = 'darkgrid', context = 'talk', font = 'STKaiti')
fund = pd.read_excel('./fund_end.xlsx')
# 降序排序并直接替换原数据
fund.sort_values(by = '基金收益(%)', ascending = False, inplace = True)
sns.barplot(x = '基金收益(%)', y = '姓名',
data = fund.iloc[:10], orient = 'h',
palette = 'Set1') # 画板、颜色
for i in range(10):
rate = fund.iloc[i]['基金收益(%)']
pe = fund.iloc[i]['基金规模(亿元)']
# 绘制基金规模
plt.text(x = rate / 2, y = i, s = str(pe) + '亿元', ha = 'center', va = 'center')
# 绘制基金收益
plt.text(x = rate + 50, y = i, s = str(rate) + '%', va = 'center')
_ = plt.xlim(0, 2500) # 横坐标范围
_ = plt.xticks(np.arange(0, 2500, 200)) # 横坐标刻度