数据可视化 | 手撕 Matplotlib 绘图原理(二)

2021-06-24 11:06:01 浏览数 (3)

由于篇幅限制,将文章分为两部分,这是第二部分。

点击链接《 Matplotlib 绘图原理(一)》可直达第一部分。

  • 线条样式
  • 在图上添加文本
    • 示例:节假日对美国出生率的影响
  • 添加箭头和文字说明
  • 误差线
  • 加网格线
  • 保存图片
  • 移动坐标轴
    • 使得轴刻度落在坐标轴上
  • 多子图
    • plt.subplot()
    • plt.subplotsf
    • fg.add_subplot()
  • 常用图形
    • 折线图
    • 散点图
    • 直方图
    • 饼图
    • 箱线图
    • 画一个填充好颜色的形状

线条样式

plot() 绘图接口中 mark参数

点标记名称

标记

点(point marker)

.

像素点(pixel marker)

,

圆形(circle marker)

o

正方形(square marker)

s’

三角形(向下,上,左,右)

v, ^, <, >

三角星(向下,上,左,右)

1, 2, 3, 4

五边星(pentagon marker)

p

星型(star marker)

*

1 号六角形(hexagon1 marker)

h

2 号六角形(hexagon2 marker)

H

号标记(plus marker)

x 号标记(x marker)

x

菱形(diamond marker)

D

窄型菱形(thin_diamond marker)

d

垂直线形(vline marker)

|

水平线形(hline marker)

_

示例

代码语言:javascript复制
x = np.linspace(-5, 5, 50)
# 创建画布
plt.figure(figsize=(8, 5))
plt.plot(x, x, color='r', marker='o', markersize=1, alpha=0.3)
plt.plot(x, np.sin(x), color='g', marker='p', markersize=5, alpha=0.6)
plt.plot(x, np.cos(x), color = 'y', marker='v', markersize=7, alpha=0.9)
plt.legend(["$y=x$", "$y=sin(x)$", "$y=cos(x)$"]);

在图上添加文本

常用参数方式: plt.text(x, y, 要添加的内容)

  • x: 位置的横坐标
  • y: 位置的纵坐标
  • 要添加的内容字符串,同样接受 LaTex 语法

示例:节假日对美国出生率的影响

数据来源:关注《数据 STUDIO》,回复【birth】获取数据。

数据预处理

代码语言:javascript复制
%matplotlib inline
import matplotlib.pyplot as plt
import matplotlib as mpl
plt.style.use('seaborn-whitegrid')
import numpy as np
import pandas as pd

births = pd.read_csv('birth.csv')
# 计算生日的百分位数
quartiles = np.percentile(births['births'], [25, 50, 75])
# 标准化IQR是一个结果变异性的量度,
# 它是稳健统计技术处理中用于表示数据分散程度的一个量,
# 它等于四分位间距(IQR)乘以因子0.741 3,其与一个标准偏差相类似。
mu, sig = quartiles[1], 0.74 * (quartiles[2] - quartiles[0])
# 使用布尔表达式查询数据帧的列,剔除离群值
births = births.query('(births > @mu - 5 * @sig) & (births < @mu   5 * @sig)')
births['day'] = births['day'].astype(int)
births.index = pd.to_datetime(10000 * births.year  100 * births.month   births.day
                              , format='%Y%m%d')
births_by_date = births.pivot_table('births'
                                    ,[births.index.month, births.index.day])
births_by_date.index = [pd.datetime(2020
                                    , month
                                    , day) for (month, day) in births_by_date.index]

绘图

代码语言:javascript复制
fig, ax = plt.subplots(figsize=(12, 4))
births_by_date.plot(ax=ax)
# 在图上增加文字标签
style = dict(size=10, color='gray')
ax.text('2020-1-1', 3950, "New Year's Day", **style)
ax.text('2020-7-4', 4250, "Independence Day", ha='center', **style)
ax.text('2020-9-4', 4850, "Labor Day", ha='center', **style)
ax.text('2020-10-31', 4600, "Halloween", ha='right', **style)
ax.text('2020-11-25', 4450, "Thanksgiving", ha='center', **style)
ax.text('2020-12-25', 3850, "Christmas ", ha='right', **style)
# 设置坐标轴标题
ax.set(title='USA births by day of year (1969-1988)',
ylabel='average daily births')
# 设置x轴刻度值,让月份居中显示
ax.xaxis.set_major_locator(mpl.dates.MonthLocator())
ax.xaxis.set_minor_locator(mpl.dates.MonthLocator(bymonthday=15))
ax.xaxis.set_major_formatter(plt.NullFormatter())
ax.xaxis.set_minor_formatter(mpl.dates.DateFormatter('%h'));

结果

添加箭头和文字说明

代码语言:javascript复制
plt.annotate(text, (x, y), (textx, texty), arrowprops=dict(arrowstyle="->", color='r'))

常用参数

  • text: 进行说明的文本
  • (x, y): 要进行说明的点的横纵坐标
  • (textx, texty): 说明的文本的要放的位置的横纵坐标
  • arrowprops=dict(arrowstyle="->")

示例

代码语言:javascript复制
fig, ax = plt.subplots(figsize=(12, 4))
births_by_date.plot(ax=ax)
# 在图上增加箭头标签
ax.annotate("New Year's Day", xy=('2020-1-1', 4100), xycoords='data',
            xytext=(50, -30), textcoords='offset points',
            arrowprops=dict(arrowstyle="->",
            connectionstyle="arc3,rad=-0.2"))
ax.annotate("Independence Day", xy=('2020-7-4', 4250), xycoords='data',
            bbox=dict(boxstyle="round", fc="none", ec="gray"),
            xytext=(10, -40), textcoords='offset points', ha='center',
            arrowprops=dict(arrowstyle="->"))
ax.annotate('Labor Day', xy=('2020-9-4', 4850), xycoords='data', ha='center',
            xytext=(0, -20), textcoords='offset points')
ax.annotate('', xy=('2020-9-1', 4850), xytext=('2020-9-7', 4850),
            xycoords='data', textcoords='data',
            arrowprops={'arrowstyle': '|-|,widthA=0.2,widthB=0.2', })
ax.annotate('Halloween', xy=('2020-10-31', 4600), xycoords='data',
            xytext=(-80, -40), textcoords='offset points',
            arrowprops=dict(arrowstyle="fancy",
            fc="0.6", ec="none",
            connectionstyle="angle3,angleA=0,angleB=-90"))
ax.annotate('Thanksgiving', xy=('2020-11-25', 4500), xycoords='data',
            xytext=(-120, -60), textcoords='offset points',
            bbox=dict(boxstyle="round4,pad=.5", fc="0.9"),
            arrowprops=dict(arrowstyle="->",
            connectionstyle="angle,angleA=0,angleB=80,rad=20"))
ax.annotate('Christmas', xy=('2020-12-25', 3850), xycoords='data',
            xytext=(-30, 0), textcoords='offset points',
            size=13, ha='right', va="center",
            bbox=dict(boxstyle="round", alpha=0.1),
arrowprops=dict(arrowstyle="wedge,tail_width=0.5", alpha=0.1));
# 设置坐标轴标题
ax.set(title='USA births by day of year (1969-1988)',
       ylabel='average daily births')
# 设置x轴刻度值,让月份居中显示
ax.xaxis.set_major_locator(mpl.dates.MonthLocator())
ax.xaxis.set_minor_locator(mpl.dates.MonthLocator(bymonthday=15))
ax.xaxis.set_major_formatter(plt.NullFormatter())
ax.xaxis.set_minor_formatter(mpl.dates.DateFormatter('%h'));
ax.set_ylim(3600, 5400);

关于箭头和注释风格的更多介绍与示例,可以在 Matplotlib 的画廊gallery[1]中看到,尤其推荐

误差线

对任何一种科学测量方法来说,准确地衡量数据误差都是无比重要的事情,甚至比数据本身还要重要。在数据可视化的结果中用图形将误差有效地显示出来,就可以提供更充分的信息。

代码语言:javascript复制
x = np.linspace(0, 10, 50)
dy = 0.8
y = np.sin(x)   dy * np.random.randn(50)
plt.figure(figsize=(8, 5))
plt.errorbar(x, y,
             yerr=dy,
             fmt='o',
             color='black',
             ecolor='lightgray',
             elinewidth=3,
             capsize=0);

重要参数

xerr, yerr : float or array-like, shape(N,) or shape(2, N), optional

  • scalar: 所有数据点的对称 /-值。
  • 形状(N,): 每个数据点的 /-值对称。
  • 形状(2,N): 每个条数据单独的-和 值。第一行包含较低的误差,第二行包含较高的误差。
  • None: 没有误差线。

fmt 是一种控制线条和点的外观的代码格式。语法与 plt.plot 的缩写代码相同。

加网格线

代码语言:javascript复制
plt.grid(b=None, which='major', axis='both', **kwargs)

常用参数方式 plt.grid(axis=方向, color=颜色, linestyle=线型)

  • axis : {'both', 'x', 'y'}, optional: 显示哪个方向的网格线
  • which : {'major', 'minor', 'both'}, optional 根据主次坐标轴更改网格线
  • color: 线的颜色
  • linestyle: 有以下一些选择
  • linewidth:设置网格线宽度。

线型

标记

直线

-

虚线

--

点线

:

点划线

-.

保存图片

plt.savefig(name, dpi, quality)

  • name: 图片的名字, 如name='picture.png'
  • dpi: 要保存的图片的像素,值越大, 保存的图片越清晰
  • quality: 仅当格式为'jpg' or 'jpeg'才能使用,从 1(最差)到 95(最好)

可以使用fig.canvas.get_supported_filetypes()查看系统支持的文件格式。

移动坐标轴

使得轴刻度落在坐标轴上

代码语言:javascript复制
# 创建画布对象
plt.figure(figsize=(8, 8), dpi=80)
# 获取当前的坐标对象
ax = plt.gca()
# 设置将X轴的刻度值放在底部X轴上
ax.xaxis.set_ticks_position('bottom')
# 设置将Y轴的刻度值放在左侧y轴上
ax.yaxis.set_ticks_position('left')
# 设置右边坐标轴线的颜色(设置为none表示不显示)
ax.spines['right'].set_color('none')
# 设置顶部坐标轴线的颜色(设置为none表示不显示)
ax.spines['top'].set_color('none')
# 设置底部坐标轴线的位置(设置在y轴为0的位置)
ax.spines['bottom'].set_position(('data', 0))
# 设置左侧坐标轴线的位置(设置在x轴为0的位置)
ax.spines['left'].set_position(('data', 0))
x = np.linspace(-1, 1, 100)
plt.plot(x, x**2);

多子图

可以在一张图上绘制多个图形,当然,也可以将不同的图形绘制到多个不同的区域当中。

子图有如下三种方式:

  • 通过figure对象调用add_subplot方法。
  • 通过plt的subplot方法。
  • 通过plt的subplots方法。

plt.subplot()

plt.subplot方法,由于plt可以隐式的创建一个figure对象,因此使用这个方法,来指定绘图布局,不需要显示的创建figure对象。因为plt.subplot方法直接可以返回子绘图区域的axes对象

plt.subplots_adjust方法可以用来调整子图与子图之间的距离。(left,right,top,bottom,wspace,hspace)

代码语言:javascript复制
fig = plt.figure(figsize=(8, 5))
fig.subplots_adjust(hspace=0.4, wspace=0.4)
for i in range(1, 7):
    ax = fig.add_subplot(2, 3, i)
    ax.text(0.5, 0.5, str((2, 3, i)),fontsize=18, ha='center')

plt.subplots

通过plt的subplots方法创建子绘图区域,该方法返回一个元组。如果是一个子绘图对象,那么返回的是一个axes坐标系对象。如果是多个子绘图对象,则返回一个ndarray数组

代码语言:javascript复制
fig, ax = plt.subplots(2, 3, sharex='col', sharey='row', figsize=(8, 5))
# 坐标轴存放在一个NumPy数组中,按照[row, col]取值
for i in range(2):
    for j in range(3):
        ax[i, j].text(0.5, 0.5, str((i, j)),fontsize=18, ha='center')

fg.add_subplot()

add_subplot指定绘图布局,需要指定子绘图区域的行数、列数和当前要绘制的子区域。

add_subplot方法会返回每个子绘图区域的对象,调用该对象即可实现在子区域的图形绘制。

可使用参数facecolor设置绘图区域的背景色。

代码语言:javascript复制
x = np.linspace(-1, 1, 100)
fg = plt.figure(figsize=(8, 6), dpi=120)

#第一个子图
fg.add_subplot(2, 2, 1)   # 两行两列的第一个
plt.plot(x, x)
plt.title('第一个子图')
plt.legend(["$y=x$"])

#第二个子图
fg.add_subplot(2, 2, 2)   # 两行两列的第二个
plt.plot(x, x**2)
plt.title("第二个子图")
plt.legend(["$y=x^{2}$"], loc=4)

#第三个子图
fg.add_subplot(2, 2, 3)
# 获取当前的坐标对象
ax = plt.gca()          # 这里获取的是这个子图的坐标对象, 也就是把这个子图的坐标轴改变
# 设置将X轴的刻度值放在底部X轴上
ax.xaxis.set_ticks_position('bottom')
# 设置将Y轴的刻度值放在左侧y轴上
ax.yaxis.set_ticks_position('left')
# 设置右边坐标轴线的颜色(设置为none表示不显示)
ax.spines['right'].set_color('none')
# 设置顶部坐标轴线的颜色(设置为none表示不显示)
ax.spines['top'].set_color('none')
# 设置底部坐标轴线的位置(设置在y轴为0的位置)
ax.spines['bottom'].set_position(('data', 0))
# 设置左侧坐标轴线的位置(设置在x轴为0的位置)
ax.spines['left'].set_position(('data', 0))
plt.plot(x, x**2)
plt.title("第三个子图")
plt.legend(["$y=x^{2}$"], loc=4, fontsize=5)

# 第四个子图
fg.add_subplot(2, 2, 4)
# 获取当前的坐标对象
ax = plt.gca()                          # 这里获取的是这个子图的坐标对象, 也就是把这个子图的坐标轴改变
# 设置将X轴的刻度值放在底部X轴上
ax.xaxis.set_ticks_position('bottom')
# 设置将Y轴的刻度值放在左侧y轴上
ax.yaxis.set_ticks_position('left')
# 设置右边坐标轴线的颜色(设置为none表示不显示)
ax.spines['right'].set_color('none')
# 设置顶部坐标轴线的颜色(设置为none表示不显示)
ax.spines['top'].set_color('none')
# 设置底部坐标轴线的位置(设置在y轴为0的位置)
ax.spines['bottom'].set_position(('data', 0))
# 设置左侧坐标轴线的位置(设置在x轴为0的位置)
ax.axis('equal') # 保证图形是圆的
ax.spines['left'].set_position(('data', 0))
plt.plot(x, (1-x**2)**0.5, color='r')
plt.plot(x, -(1-x**2)**0.5, color='r')
plt.title("第四个子图")  # 标题
plt.legend(["$x^{2} y^{2}=1$"], loc=4, fontsize=5);

常用图形

折线图

常用参数形式: plt.plot(x, y, marker=点的形状, color=颜色, linestyle=线形, linewidth=线宽, markersize=点的形状大小, alpha=透明度)

示例

代码语言:javascript复制
x = np.linspace(-5, 5, 50)
# 创建画布
plt.figure(figsize=(8, 5))
plt.plot(x, x, color='r', marker='o', markersize=1, alpha=0.3)
plt.plot(x, np.sin(x), color='g', marker='p', markersize=5, alpha=0.6)
plt.plot(x, np.cos(x), color = 'y', marker='v', markersize=7, alpha=0.9)
plt.legend(["$y=x$", "$y=sin(x)$", "$y=cos(x)$"]);

散点图

常用参数形式: plt.scatter(x, y, marker=点的形状, color=颜色, markersize=点的形状大小, alpha=透明度)

代码语言:javascript复制
x = np.random.randn(500)
plt.figure(figsize=(8, 5))
plt.scatter(x, 1/(1 np.exp(-x)), alpha=0.4, color='r')
plt.scatter(x, np.tanh(x), marker='v', alpha=0.7, color='g')
plt.scatter(0, 0, color='r')
plt.text(0.2, 0, "(0, 0)")

plt.legend(["sigmoid", 'tanh'], loc=4, fontsize=15);

直方图

常用参数形式: plt.bar(x, y, width=柱子宽度, color=颜色)

代码语言:javascript复制
x = np.arange(10, 0, -2)
plt.figure(figsize=(8, 5))
plt.bar(range(len(x)), x, width=0.5, color='g')
plt.xticks(range(len(x)), ['一月', '二月', '三月', '四月', '五月'])
for i in list(zip(range(len(x)), x)):
    plt.text(i[0], i[1] 0.1, i[1], horizontalalignment='center')#horizontalalignment='center'让数字在中间
plt.show();

饼图

常用参数形式: plt.pie(x, labels=标签, autopct=指定显示数值, explode=突出显示某个部分)

代码语言:javascript复制
#调节图形大小,宽,高
plt.figure(figsize=(7,8))
#定义饼状图的标签,标签是列表
labels = ['数据库', 'PowerBI', 'Python', 'Hive', '其他']
#每个标签占多大,会自动去算百分比
x = [35, 25, 25, 10, 5]
#将某部分爆炸出来, 使用括号,将第一块分割出来,数值的大小是分割出来的与其他两块的间隙
explode=[0.05, 0.00, 0, 0, 0]

patches,l_text,p_text = plt.pie(x,
                                explode=explode,
                                labels=labels,
#                                 colors=colors,
                                labeldistance = 1.1,
                                autopct = '%3.1f%%',
                                shadow = False,
                                startangle = 90,
                                pctdistance = 0.6)

# labeldistance,文本的位置离远点有多远,1.1指1.1倍半径的位置
# autopct,圆里面的文本格式,%3.1f%%表示小数有三位,整数有一位的浮点数
# shadow,饼是否有阴影
# startangle,起始角度,0,表示从0开始逆时针转,为第一块。一般选择从90度开始比较好看
# pctdistance,百分比的text离圆心的距离
# patches, l_texts, p_texts,为了得到饼图的返回值,p_texts饼图内部文本的,l_texts饼图外label的文本

#改变文本的大小
#方法是把每一个text遍历。调用set_size方法设置它的属性
for t in l_text:
    t.set_size(15)
for t in p_text:
    t.set_size(18)
# 设置x,y轴刻度一致,这样饼图才能是圆的
plt.title("数据分析各技能占比", fontsize=20)
plt.axis('equal')
plt.show()

箱线图

代码语言:javascript复制
np.random.seed(666)
x = np.random.randn(100)
plt.figure(figsize=(10, 6), dpi=80)
plt.boxplot(x,
            sym='*',           # 设定异常值形状
            notch=True,        # 是否在中位数处以凹凸口的形式显示箱线图
            whis=1.5)
plt.plot(np.linspace(0,2, 100), np.full(100, 1.739251),
         linestyle='--', color='y', label='上限')
plt.plot(np.linspace(0,2, 100), np.full(100, -1.942967),
         linestyle='--', color='r', label='下限')
plt.plot(np.linspace(0,2, 100), np.full(100, -0.049283),
         linestyle='--', color='g', label='中位数')
plt.legend()
plt.show();

画一个填充好颜色的形状

代码语言:javascript复制
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as mptaches
%matplotlib inline
xy1=np.array([2,2])
xy2=np.array([1,7])
xy3=np.array([8,2])
xy4=np.array([8,8])
fig,ax=plt.subplots(figsize=(8, 5))
#圆形,指定坐标和半径
circle=mptaches.Circle(xy1, radius=2)
ax.add_patch(circle)
#长方形
rect=mptaches.Rectangle(xy2,width=3, height=1,color='r')
ax.add_patch(rect)
#多边形
polygon=mptaches.RegularPolygon(xy3,numVertices=6,radius=1.5,color='g')
ax.add_patch(polygon)
# 椭圆
ellipse=mptaches.Ellipse(xy4,width=4, height=2,color='c')
ax.add_patch(ellipse)
ax.axis('equal')
plt.show()

参考资料

[1]

(gallery): https://matplotlib.org/examples/pylab_examples/annotation_demo2.html

推荐阅读

0 人点赞