提示:可以输入以下命令进行本地安装:
pip install matplotlib
Matplotlib 这个模块很庞大,最常用的是其中一个子模块——pyplot,通常这样导入它:
import matplotlib.pyplot as plt
pyplot 中最基础的作图方式是以点作图,即给出每个点的坐标,pyplot 会将这些点在坐标系中画出,并用线将这些点连起来。我们以正弦函数为例,看看如何用 pyplot 画出它的函数图像:
import numpy as np
import matplotlib.pyplot as plt # 导入模块
x = np.arange(0, 2 * np.pi, 0.1)
y = np.sin(x)
plt.plot(x, y)
plt.show()
首先是 x = np.arange(0, 2 * np.pi, 0.1)。np.pi 就是 π 的值。所以这行代码的作用是:生成一个从 0 到 2π 的数组,步长是 0.1,这样我们就获得了 x 轴的所有坐标。步长为 0.1 是为了让每个点间隔小一点,点的数量多一点。这样画出的图像更加接近真实情况,也更加的平滑。接下来是 y = np.sin(x)。np.sin() 方法是 numpy 中计算正弦函数的方法,我们将 x 的值传进去就得到对应的正弦值。
除了 np.sin() 方法之外,numpy 中也有 np.cos()、np.tan() 等计算三角函数的方法
有了 x 和 y 的值之后,将其分别传入 plt.plot() 方法,pyplot 会将其转换成对应的坐标。上述例子中,(0, 0) 是第一个坐标,(0.1, 0.09983342) 是第二个坐标,(6.2, -0.0830894) 是最后一个坐标。
将这些坐标连成线就得到了函数的图像,最后调用 plt.show() 方法就能将图像显示出来了。
上面这些方法中,最重要的是 plt.plot() 方法。plt.plot() 方法接收任意对数的 x 和 y,它会将这些图像在一张图上都画出来。比如你想再画一个余弦函数的图像,可以这样写:
x = np.arange(0, 2 * np.pi, 0.1)
y1 = np.sin(x)
y2 = np.cos(x)
plt.plot(x, y1, x, y2)
plt.show() # 这行代码不能丢,否则不显示图像
其实对于每一对 x 和 y,还有一个可选的格式化参数,用来指定线条的颜色、点标记和线条的类型。
x = np.arange(0, 2 * np.pi, 0.1)
y1 = np.sin(x)
y2 = np.cos(x)
plt.plot(x, y1, 'ro--') # 正弦函数图像
plt.plot(x, y2, 'b*-.') # 余弦函数图像
# 等价于 plt.plot(x, y1, 'ro--', x, y2, 'b*-.')
plt.show()
以 ‘ro–’ 为例,它分为 3 部分:r 代表红色(red),o 代表的是圆点标记,-- 代表着虚线。ro-- 的意思就是线条为红色虚线、坐标点标记为圆点。
格式化参数的这 3 部分都是可选的,也没有顺序要求。你可以只传入一个 ‘r’ 来指定颜色,也可以写成 --or。格式化参数的每部分都有很多选项,因此能形成很多组合,下图列出了格式化参数常用的选项及其含义:
可以用 np.power() 方法来求次方。求三次方可以写成:np.power(ndarray, 3)
import numpy as np
import matplotlib.pyplot as plt
t=np.arange(-6,6,0.01)
x=16*np.power(np.sin(t),3)
y=13*np.cos(t)-5*np.cos(2*t)-2*np.cos(3*t)-np.cos(4*t)
plt.plot(x,y,'r')
plt.show()
按照数据展示的目标可以把它们分为五种,分别是:趋势、比较、构成、分布和联系。
趋势:这是最常见的一种时间序列关系,关心数据如何随着时间变化,趋势类里的图表能直观反映出每年、每月、每天的变化趋势,增长、减少、上下波动还是基本不变。最常见的是折线图,它能很好地表现指标随时间呈现的趋势。
构成:主要关注每个部分占整体的比例,如果你想分析的目标诸如“份额”、“百分比”等。展示构成关系的图表类型里,最常见的就是饼图。
比较:可以展示某个维度上的排列顺序,分析某维度之间的对比是差不多,还是 “大于”、“小于”,比如分析男生和女生的身高差别。
分布:当你关心数据集中、频率、分布时,比如根据地理位置数据,通过地图来展示不同分布特征。比较常用的图表有地图、直方图、散点图。
联系:主要查看两个变量之间是否表达出我们预期所要证明的相关关系。比如预期销售额可能随着优惠折扣的增长而增长,常用于表达“与……有关”、“随……而增长”、“随……而不同”等维度间的关系。
plt.rcParams['font.family'] = ['Noto Sans CJK JP']
Noto Sans CJK JP 是我的电脑环境中安装的中文字体,在你的本地环境不一定适用,你可以通过如下代码打印出已经安装的字体,找到其中的中文字体名称替换掉 Noto Sans CJK JP 即可,例如 SimHei(黑体)等。
import matplotlib.font_manager as fm
for font in fm.fontManager.ttflist:
print(font.name)
加图例很简单,只要在调用 plt.plot() 方法时传入 label 参数,接着调用 plt.legend() 方法显示图例即可。
x = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
y1 = [61, 42, 52, 72, 86, 91, 73]
y2 = [23, 26, 67, 38, 46, 55, 33]
# 设置 x 轴标签
plt.xlabel('时间')
# 设置 y 轴标签
plt.ylabel('销量')
# 设置图表标题
plt.title('商品销量对比图')
# 传入 label 参数
plt.plot(x, y1, label='商品 A')
plt.plot(x, y2, label='商品 B')
# 显示图例
plt.legend()
#如果我们想将图例放到右下角就可以这么写:
#plt.legend(loc='lower right')
plt.show()
我们可以看到,图例被放在了图表的左上方,图例位置其实是可以设置的,不设置的情况下 matplotlib 会帮你自动找一个合适的位置放置。
如果想自行选择图例位置的话可以通过 plt.legend() 方法的 loc 参数实现,loc 参数共有如下 11 种方式可供选择:
收盘价.csv如下所示:
172.63,168.49,44.36
162.54,183.03,43.05
164.85,182.45,46.28
166.23,185.57,49.74
110.00,149.26,41.79
117.36,169.45,45.21
111.70,173.11,47.21
104.47,175.03,41.64
102.76,167.23,42.33
101.85,176.67,41.13
118.53,200.00,42.54
126.40,212.10,48.15
main()函数如下所示:
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.family'] = ['Noto Sans CJK JP']
dates = [
'01月', '02月', '03月', '04月', '05月', '06月',
'07月', '08月', '09月', '10月', '11月', '12月'
]
data=np.genfromtxt('收盘价.csv',delimiter=',')
baidu=data[:,0]
ali=data[:,1]
ten=data[:,2]
plt.plot(dates,baidu,'b*-',label='百度')
plt.plot(dates,ali,'g^-',label='阿里')
plt.plot(dates,ten,'r',label='腾讯')
plt.xlabel('时间')
plt.ylabel('收盘价(美元)')
plt.title('BAT股票收盘价走势2019')
plt.legend()
plt.show()
调用 plt.bar() 方法即可。我们需要传入两个参数,第一个参数是 x 轴上刻度的标签序列(列表、元组、数组等),第二个参数则用于指定每个柱子的高度,也就是具体的数据。
names = ['篮球', '羽毛球', '乒乓球']
nums = [26, 20, 19]
#plt.bar(names, nums)
plt.bar(names, nums, width=0.6, color='skyblue')
plt.show()
柱状图能直观地展现出不同分类数据上的差异,但有时候我们想更进一步地分析每个分类数据的分布,比如每门选修课的男女比例,这时就需要用到堆叠柱状图。
总体和前面画的柱状图是一样的,只是每个柱子被分成了多块。堆叠柱状图不仅可以展示每一个分类的总量,还能展示该分类包含的每个小分类的大小及占比,因此非常适合处理部分与整体的关系。与饼图显示单个部分到整体的关系不同的是,堆叠柱状图可以显示多个部分到整体的关系。例如某班选课的各项目人数统计,你可以用柱状图或饼图来展示。但是,当需要进一步区分男生和女生参与到不同项目中的人数分别是多少时,就需要把每个项目中包含的男生数和女生数都展示出来。如图选用堆叠柱状图,不仅能显示每个项目的总人数,还能展示出每个项目中的一部分与整体的关系。
names = ['篮球', '羽毛球', '乒乓球']
nums_boy = [16, 5, 11]
nums_girl = [10, 15, 8]
plt.bar(names, nums_boy, width=0.6, color='skyblue', label='男')
plt.bar(names, nums_girl, bottom=nums_boy, width=0.6, color='pink', label='女')
#bottom 参数作用就是控制柱状图低端的位置。我们将前一个柱状图的高度传进去,这样就形成了堆叠柱状图,如果我们没有设置 bottom 参数,后面的图形会盖在原来的图形之上
plt.legend()
plt.show()
分组柱状图经常用于不同组间数据的比较,这些组都包含了相同分类的数据。
x = np.arange(3)
width = 0.3
names = ['篮球', '羽毛球', '乒乓球']
nums_boy = [16, 5, 11]
nums_girl = [10, 15, 8]
plt.bar(x - width / 2, nums_boy, width=width, color='skyblue', label='男')
plt.bar(x + width / 2, nums_girl, width=width, color='pink', label='女')
plt.xticks(x, names)
plt.legend()
plt.show()
首先使用 np.arange(3) 方法创建了一个数组 x,值为 [0 1 2]。并定义了一个变量 width 用于指定柱子的宽度。在调用 plt.bar() 时,第一个参数不再是刻度上的标签,而是对应的刻度。以 [0 1 2] 为基准,分别加上和减去柱子的宽度得到 [-0.15 0.85 1.85] 和 [0.15 1.15 2.15],这些刻度将分别作为两组柱子的中点,并且柱子的宽度为 0.3。因为传入的是刻度,而不是刻度的标签。因此,我们还要调用 plt.xticks() 方法来将 x 轴上刻度改成对应的标签。该方法的第一个参数是要更改的刻度序列,第二个参数是与之对应的标签序列。同理,还可以用 plt.yticks() 方法来更改 y 轴上刻度的标签。
绘制一个降水量变化的图
第一种画法,折线图:
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.family'] = ['Noto Sans CJK JP']
#plt.rcParams['font.family'] = ['Cambria']
times = ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']
# 蒸发量
data1 = [2.0, 4.9, 7.0, 23.2, 25.6, 76.7, 135.6, 162.2, 32.6, 20.0, 6.4, 3.3]
# 降水量
data2 = [2.6, 5.9, 9.0, 26.4, 28.7, 70.7, 175.6, 182.2, 48.7, 18.8, 6.0, 2.3]
plt.plot(times,data1,'g',label='蒸发量')
plt.plot(times,data2,'r',label='降水量')
plt.xlabel('时间')
plt.ylabel('变化')
plt.title('蒸发量和降水量')
plt.legend()
plt.show()
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.family'] = ['Noto Sans CJK JP']
#plt.rcParams['font.family'] = ['Cambria']
times = ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月']
# 蒸发量
data1 = [2.0, 4.9, 7.0, 23.2, 25.6, 76.7, 135.6, 162.2, 32.6, 20.0, 6.4, 3.3]
# 降水量
data2 = [2.6, 5.9, 9.0, 26.4, 28.7, 70.7, 175.6, 182.2, 48.7, 18.8, 6.0, 2.3]
x=np.arange(12)
width=0.3
plt.bar(x-width/2,data1,width=width,label='蒸发量')
plt.bar(x+width/2,data2,width=width,label='降水量')
plt.xticks(x,times)
plt.xlabel('时间')
plt.ylabel('变化')
plt.title('蒸发量和降水量')
plt.legend()
plt.show()
直观很多,如果不加上plt.xticks(x,times),得到效果图如下,横坐标编码乱掉了
饼图广泛地应用在各个领域,用于表示不同分类的占比情况,通过弧度大小来对比各种分类。饼图通过将一个圆饼按照分类的占比划分成多个区块,整个圆饼代表数据的总量,每个区块(圆弧)表示该分类占总体的比例大小,所有区块(圆弧)的加和等于 100%。
饼图的画法很简单,只要传入数据和对应的标签给 plt.pie() 方法即可。但为了让饼图更加直观清晰,需要了解一些额外的参数。
以 2018 年国内生产总值(GDP)三大产业的占比为例,可以画出这样的饼图:
data = [64745.2, 364835.2, 489700.8]
labels = ['第一产业', '第二产业', '第三产业']
plt.pie(data, labels=labels, autopct='%0.1f%%')
plt.show()
plt.pie() 方法每个参数的作用:第一个参数是绘图需要的数据;参数 labels 指定了数据对应的标签;参数 autopct 则给饼图自动添加百分比显示。
这里详细说一下 autopct 参数的格式,这里用到了字符串格式化输出的知识。上述代码中 ‘%0.1f%%’ 可以分为两部分,一部分是 %0.1f 表示保留一位小数,同理 %0.2f 表示保留两位小数。
另一部分是 %%,其实它就表示输出一个 %,因为 % 还字符串格式化输出中有特殊的含义,所以想要输出 % 就得写成 %%。所以,’%0.1f%%’ 的含义是保留一位小数的百分数,例如:53.3%。
我们还能通过 explode 参数来让饼图中的某一部分突出显示,来强调某项数据。
data = [64745.2, 364835.2, 489700.8]
labels = ['第一产业', '第二产业', '第三产业']
explode = (0.1, 0, 0)
plt.pie(data, explode=explode, labels=labels, autopct='%0.1f%%')
plt.show()
默认 explode 参数里的元素都是 0,我们将需要突出显示的部分对应位置的值设大一点,就能将对应的区块抽离出来突出显示。这个数值越大,抽离的效果就越明显。
到目前我们学到的都是绘制一张图表的方法。但是一张图表很难同时分析多个维度,我们需要把多张图绘制在一起,进行多个维度的查看比较。
Matplotlib 提供了子图的概念,通过使用子图,你可以在一张图里绘制多个图表。
在 matplotlib 中,我们可以调用 plt.subplot() 方法来添加子图。 plt.subplot() 方法的前两个参数分别是子图的行数和列数,第三个参数是子图的序号(从 1 开始)。举个例子:
ax1 = plt.subplot(2, 2, 1)
ax2 = plt.subplot(2, 2, 2)
ax3 = plt.subplot(2, 2, 3)
ax4 = plt.subplot(2, 2, 4)
plt.subplot(2, 2, 1) 的作用是生成一个两行两列的子图,并选择其中序号为 1 的子图。所以上面的代码将一张图表分成了 4 个子图,并用 1、2、3、4 来选择对应位置的子图。
x = np.arange(0, 2 * np.pi, 0.1)
# 子图 1
ax1 = plt.subplot(2, 2, 1)
y1 = np.sin(x)
ax1.plot(x, y1)
# 子图 2
ax2 = plt.subplot(2, 2, 2)
y2 = np.cos(x)
ax2.plot(x, y2)
# 子图 3
ax3 = plt.subplot(2, 1, 2)
y3 = np.tan(x)
ax3.plot(x, y3)
plt.show()
我们还能使用 set_title()来为每个子图设置单独的标题。需要注意的是,如果想要给带有子图的图表设置总的标题的话,不是使用 plt.title() 方法,而是通过 plt.suptitle() 方法来设置带有子图的图表标题。
plt.suptitle('带有子图的图表标题')
ax1 = plt.subplot(2, 2, 1)
ax1.set_title('子图标题')