常见需求完整示例

下列所有函数均未预留更改样式的参数,需要在样式环境下使用(参考下文),以保持简洁,做到数据和样式分离。
先加入支持中文的代码,再自定义一个环境(可选),然后调用预先定义好的函数,就可以直接运行。

多系列折线图

  1. def extend_lim(lim, extend):
  2. if lim is None:
  3. return None
  4. minv, maxv = lim
  5. if minv is None:
  6. return (None, maxv * (1 + extend))
  7. elif maxv is None:
  8. return (minv * (1 - extend), None)
  9. else:
  10. section = maxv - minv
  11. minv = minv - section * extend
  12. maxv = maxv + section * extend
  13. return (minv, maxv)
  14. def plot(
  15. data,
  16. series,
  17. *,
  18. xlabel,
  19. ylabel,
  20. xlim,
  21. ylim,
  22. xinterval,
  23. yinterval,
  24. xlim_extend=0,
  25. ylim_extend=0
  26. ):
  27. fig, axe = plt.subplots(1, 1)
  28. for legend, (xarray, yarray) in data.items():
  29. axe.plot(xarray, yarray, label=legend)
  30. axe.set(
  31. xlim=extend_lim(xlim, xlim_extend),
  32. ylim=extend_lim(ylim, ylim_extend),
  33. xlabel=xlabel,
  34. ylabel=ylabel,
  35. )
  36. axe.xaxis.set_major_locator(plt.MultipleLocator(xinterval))
  37. axe.xaxis.set_minor_locator(plt.MultipleLocator(xinterval / 2))
  38. axe.yaxis.set_major_locator(plt.MultipleLocator(yinterval))
  39. axe.yaxis.set_minor_locator(plt.MultipleLocator(yinterval / 2))
  40. if len(series) > 1:
  41. axe.legend()
  42. fig.tight_layout()
  43. return fig

调用函数得到结果

  1. import matplotlib.pyplot as plt
  2. from cycler import cycler
  3. import numpy as np
  4. # 中文支持,必须写
  5. plt.rcParams.update(
  6. {
  7. "font.sans-serif": "SimSun", # 中文支持,必须写
  8. "axes.unicode_minus": False, # 中文支持,必须写
  9. })
  10. # 这个with块非必须
  11. with plt.rc_context(rc=myenv):
  12. fig = plot(
  13. {
  14. "菜场": [[0, 2, 4, 6], [4.0, 2.3, 3.1, 2.9]],
  15. "超市": [[0, 2, 4, 6], [0.9, 2.1, 1.8, 1.2]],
  16. "公园": [[0, 2, 4, 6], [1.6, 1.7, 1.2, 1.5]],
  17. },
  18. ["公园", "菜场", "超市"],
  19. xlabel="小时",
  20. ylabel="人流量(千人)",
  21. xlim=(0, 6),
  22. xinterval=1,
  23. xlim_extend=0.025,
  24. ylim=(0, 4),
  25. yinterval=0.5,
  26. ylim_extend=0.025,
  27. )
  28. plt.show()

bb.png

并列柱状图

matplotlib官网:bar

  1. class BarLoc(object):
  2. def __init__(self, total_bar_width, series, data_len):
  3. """total_bar_width:同一组的不同色柱子的总宽度,范围0~1
  4. series:图例列表
  5. data_len:每个颜色的柱子有多少个数据"""
  6. self.total_bar_width = total_bar_width
  7. series_count = len(series)
  8. self.series = dict(zip(series, np.arange(series_count)))
  9. self.single_bar_width = total_bar_width / series_count
  10. self.data_len = data_len
  11. # ========
  12. if series_count % 2 == 0:
  13. start_num = -(series_count / 2 - 0.5) * self.single_bar_width
  14. else:
  15. start_num = -(series_count // 2) * self.single_bar_width
  16. end_num = -start_num
  17. self.first_bar_loc = np.linspace(start_num, end_num, series_count)
  18. def iloc(self, index):
  19. return np.arange(self.data_len) + self.first_bar_loc[index]
  20. def loc(self, index):
  21. return self.iloc(self.series[index])
  22. def __getitem__(self, index):
  23. return self.loc(index)
  24. def parallel_bar(
  25. data, series, xticklabels, total_bar_width, *, xlabel, ylabel, ylim, yinterval
  26. ):
  27. fig, axe = plt.subplots(1, 1)
  28. bar_loc = BarLoc(total_bar_width, series, len(xticklabels))
  29. for legend, array in data.items():
  30. axe.bar(bar_loc[legend], array, width=bar_loc.single_bar_width, label=legend)
  31. axe.set_xticks(np.arange(len(xticklabels)), labels=xticklabels)
  32. axe.set_ylim(ylim)
  33. axe.yaxis.set_major_locator(plt.MultipleLocator(yinterval))
  34. axe.yaxis.set_minor_locator(plt.MultipleLocator(yinterval / 2))
  35. axe.set_xlabel(xlabel)
  36. axe.set_ylabel(ylabel)
  37. if len(series) > 1:
  38. axe.legend()
  39. fig.tight_layout()
  40. return fig

调用函数得到结果:

  1. import matplotlib.pyplot as plt
  2. from cycler import cycler
  3. import numpy as np
  4. # 中文支持,必须写
  5. plt.rcParams.update(
  6. {
  7. "font.sans-serif": "SimSun", # 中文支持,必须写
  8. "axes.unicode_minus": False, # 中文支持,必须写
  9. })
  10. # 这个with块非必须
  11. with plt.rc_context(rc=myenv):
  12. parallel_bar(
  13. {
  14. "黑笔": [4.0, 2.3, 3.1, 2.9],
  15. "蓝笔": [0.9, 2.1, 1.8, 1.2],
  16. "红笔": [1.6, 1.7, 1.2, 1.5],
  17. },
  18. ["黑笔", "红笔", "蓝笔"],
  19. ["旗舰店", "分店1", "分店2", "加盟店"],
  20. 0.72,
  21. xlabel="店铺",
  22. ylabel="销量(万只)",
  23. ylim=(0, 4.25),
  24. yinterval=0.5,
  25. )
  26. plt.show()

测试.png

样式控制

matplotlib官网:rcParams

设置对中文的支持

使用全局样式控制器plt.rcParams,可采用字典式写法

  1. import matplotlib.pyplot as plt
  2. from cycler import cycler
  3. import numpy as np
  4. # 中文支持,必须写
  5. plt.rcParams.update(
  6. {
  7. "font.sans-serif": "SimSun", # 中文支持,必须写
  8. "axes.unicode_minus": False, # 中文支持,必须写
  9. })

配置多个样式环境

matplotlib官网:自定义样式环境
推荐采用第一种方式,myenv作为dict对象,可以方便地进行组合,从而使不同的环境共享一部分设置

  1. myenv = {
  2. "figure.figsize": (4.8, 3.6), # 定义图片的长宽
  3. # figure.figsize和dpi相乘就是图片长宽的具体像素值
  4. "savefig.dpi": 200, # savefig.dpi是保存图片时的分辨率参数
  5. "figure.dpi": 200, # figure.dpi是临时显示图片时的分辨率参数
  6. # plot或scatter的每个系列的marker不一样
  7. "axes.prop_cycle": cycler(
  8. marker=["o", "^", "s", "D", "h", "x", "o", "^", "s", "D"],
  9. color=plt.get_cmap("tab10")(np.arange(10)),
  10. ),
  11. # 线粗和标记大小
  12. "lines.linewidth": 1, # 默认值
  13. "lines.markersize": 3, # 和上述分辨率配合较好
  14. # 经验证明下述文字大小和上述分辨率配合较好
  15. "legend.fontsize": 8,
  16. "xtick.labelsize": 12,
  17. "ytick.labelsize": 12,
  18. "axes.labelsize": 12,
  19. "mathtext.fontset": "stix", # 公式字体,此项不要修改
  20. }
  21. with plt.rc_context(myenv):
  22. #进行绘图操作
  23. #...
  24. #注意plt.show()写在with里面,尽量把图片保存到磁盘
  25. plt.show()

或在myrc.pltstyle中输入配置,然后在代码中加入:

  1. with plt.rc_context(fname="myrc.pltstyle"):
  2. #进行绘图操作
  3. #...
  4. #注意plt.show()写在with里面,尽量把图片保存到磁盘
  5. plt.show()

不太推荐使用文件式写法,因为有些值(如axes.prop_cycle)右侧可能是复杂的函数,难以确定在文件中应该怎么写。实在需要的话可以参考matplotlib默认rc文件

多系列图表的自动样式控制

cycler可以自动为多系列的图表的每个系列使用不同的颜色、标记形状等
创建cycler有两种写法,其中第二种写法只能指定一个键,一般用于含有特殊字符的键。
cycler可以相加,这保证了第二种写法也能写出有多个键的对象。
若cycler中指定了多个键,则每个键的列表长度必须相同。

  1. cycler(aa=[...],bb=[...],...)
  2. cycler("aa", [...])

最后,可以使用cycler.by_keys()将其转化为dict和python其他数据结构进行耦合。

颜色设置

颜色表示方法

可以用形如#ab1295的字符串表示单个颜色,["#aabbcc","#123456",...]表示一组颜色。字符串还支持各种其他表示方法,请参阅matplotlib官网:颜色表示方法
也可以用(R,G,B)(R,G,B,A)及其ndarray形式表示单个颜色和一组颜色,这种方式一般是给程序内部使用的,不适合人工指定。

颜色组库

matplotlib提供了plt.get_cmap()方法获取丰富的颜色库。颜色库中包含很多系列,每个系列都有对应的名称和样式预览。具体请参阅matplotlib官网:颜色库一览
颜色系列分为两种,一种是由固定的N个颜色组成,使用方式为用形如plt.get_cmap("Accent")(np.arange(2,7))的方式获取该颜色系列指定序号的颜色,或用形如plt.get_cmap("Accent")(4)的方式获取一个颜色。
还有一种是渐变色,接收一个0~1之间的浮点数,使用方式为用形如plt.get_cmap("Blues")(np.linspace(0,1,n))的方式获取n种“等距”的颜色。
诸如plt.get_cmap("Blues")这样的函数返回的是一个类,因此后面加上圆括号用于构造类,不能望文生义写成方括号。
颜色组几乎没有什么对象属性,诸如颜色组包含哪些颜色,颜色数量等属性全部通过看图得出答案,可以将官网的颜色组库图下载保存,以备不时之需。

颜色组与cycler

可以使用cycler的color键指定多系列图的颜色,方便在不同的环境中使用不同的系列颜色。

不能采用环境样式控制的项

图例

matplotlib官网:axes.legend
一般情况下,程序会自动找到一个不会阻挡图内容的位置放置图例,直接调用axe.legend()即可。
若图内容占满了整张图,则需要把图例放到图外面:

  1. axe.legend(bbox_to_anchor=(1, 1),loc="upper left")

参数说明:

  • bbox_to_anchor:先假设图例框是一个点,决定图例框放在图像框的哪个位置。(0,0)是左下角,(1,1)是右上角,可以小于0或大于1
  • loc:刚才设置的那个点,是图例框的哪一个点。先写上下,再写左右,中间有一个空格。
    -上下:upper center lower
    -左右:left center right

有时图例全部竖着排列太长,使其分列可以改变图例整体的形状

  1. axe.legend(ncols=2)

网格

matplotlib官网:axes.grid

  1. axe.grid(True, "both", "y", lw=0.5, ls=":",c="black")