常见需求完整示例
下列所有函数均未预留更改样式的参数,需要在样式环境下使用(参考下文),以保持简洁,做到数据和样式分离。
先加入支持中文的代码,再自定义一个环境(可选),然后调用预先定义好的函数,就可以直接运行。
多系列折线图
def extend_lim(lim, extend):if lim is None:return Noneminv, maxv = limif minv is None:return (None, maxv * (1 + extend))elif maxv is None:return (minv * (1 - extend), None)else:section = maxv - minvminv = minv - section * extendmaxv = maxv + section * extendreturn (minv, maxv)def plot(data,series,*,xlabel,ylabel,xlim,ylim,xinterval,yinterval,xlim_extend=0,ylim_extend=0):fig, axe = plt.subplots(1, 1)for legend, (xarray, yarray) in data.items():axe.plot(xarray, yarray, label=legend)axe.set(xlim=extend_lim(xlim, xlim_extend),ylim=extend_lim(ylim, ylim_extend),xlabel=xlabel,ylabel=ylabel,)axe.xaxis.set_major_locator(plt.MultipleLocator(xinterval))axe.xaxis.set_minor_locator(plt.MultipleLocator(xinterval / 2))axe.yaxis.set_major_locator(plt.MultipleLocator(yinterval))axe.yaxis.set_minor_locator(plt.MultipleLocator(yinterval / 2))if len(series) > 1:axe.legend()fig.tight_layout()return fig
调用函数得到结果
import matplotlib.pyplot as pltfrom cycler import cyclerimport numpy as np# 中文支持,必须写plt.rcParams.update({"font.sans-serif": "SimSun", # 中文支持,必须写"axes.unicode_minus": False, # 中文支持,必须写})# 这个with块非必须with plt.rc_context(rc=myenv):fig = plot({"菜场": [[0, 2, 4, 6], [4.0, 2.3, 3.1, 2.9]],"超市": [[0, 2, 4, 6], [0.9, 2.1, 1.8, 1.2]],"公园": [[0, 2, 4, 6], [1.6, 1.7, 1.2, 1.5]],},["公园", "菜场", "超市"],xlabel="小时",ylabel="人流量(千人)",xlim=(0, 6),xinterval=1,xlim_extend=0.025,ylim=(0, 4),yinterval=0.5,ylim_extend=0.025,)plt.show()
并列柱状图
class BarLoc(object):def __init__(self, total_bar_width, series, data_len):"""total_bar_width:同一组的不同色柱子的总宽度,范围0~1series:图例列表data_len:每个颜色的柱子有多少个数据"""self.total_bar_width = total_bar_widthseries_count = len(series)self.series = dict(zip(series, np.arange(series_count)))self.single_bar_width = total_bar_width / series_countself.data_len = data_len# ========if series_count % 2 == 0:start_num = -(series_count / 2 - 0.5) * self.single_bar_widthelse:start_num = -(series_count // 2) * self.single_bar_widthend_num = -start_numself.first_bar_loc = np.linspace(start_num, end_num, series_count)def iloc(self, index):return np.arange(self.data_len) + self.first_bar_loc[index]def loc(self, index):return self.iloc(self.series[index])def __getitem__(self, index):return self.loc(index)def parallel_bar(data, series, xticklabels, total_bar_width, *, xlabel, ylabel, ylim, yinterval):fig, axe = plt.subplots(1, 1)bar_loc = BarLoc(total_bar_width, series, len(xticklabels))for legend, array in data.items():axe.bar(bar_loc[legend], array, width=bar_loc.single_bar_width, label=legend)axe.set_xticks(np.arange(len(xticklabels)), labels=xticklabels)axe.set_ylim(ylim)axe.yaxis.set_major_locator(plt.MultipleLocator(yinterval))axe.yaxis.set_minor_locator(plt.MultipleLocator(yinterval / 2))axe.set_xlabel(xlabel)axe.set_ylabel(ylabel)if len(series) > 1:axe.legend()fig.tight_layout()return fig
调用函数得到结果:
import matplotlib.pyplot as pltfrom cycler import cyclerimport numpy as np# 中文支持,必须写plt.rcParams.update({"font.sans-serif": "SimSun", # 中文支持,必须写"axes.unicode_minus": False, # 中文支持,必须写})# 这个with块非必须with plt.rc_context(rc=myenv):parallel_bar({"黑笔": [4.0, 2.3, 3.1, 2.9],"蓝笔": [0.9, 2.1, 1.8, 1.2],"红笔": [1.6, 1.7, 1.2, 1.5],},["黑笔", "红笔", "蓝笔"],["旗舰店", "分店1", "分店2", "加盟店"],0.72,xlabel="店铺",ylabel="销量(万只)",ylim=(0, 4.25),yinterval=0.5,)plt.show()
样式控制
设置对中文的支持
使用全局样式控制器plt.rcParams,可采用字典式写法
import matplotlib.pyplot as pltfrom cycler import cyclerimport numpy as np# 中文支持,必须写plt.rcParams.update({"font.sans-serif": "SimSun", # 中文支持,必须写"axes.unicode_minus": False, # 中文支持,必须写})
配置多个样式环境
matplotlib官网:自定义样式环境
推荐采用第一种方式,myenv作为dict对象,可以方便地进行组合,从而使不同的环境共享一部分设置
myenv = {"figure.figsize": (4.8, 3.6), # 定义图片的长宽# figure.figsize和dpi相乘就是图片长宽的具体像素值"savefig.dpi": 200, # savefig.dpi是保存图片时的分辨率参数"figure.dpi": 200, # figure.dpi是临时显示图片时的分辨率参数# plot或scatter的每个系列的marker不一样"axes.prop_cycle": cycler(marker=["o", "^", "s", "D", "h", "x", "o", "^", "s", "D"],color=plt.get_cmap("tab10")(np.arange(10)),),# 线粗和标记大小"lines.linewidth": 1, # 默认值"lines.markersize": 3, # 和上述分辨率配合较好# 经验证明下述文字大小和上述分辨率配合较好"legend.fontsize": 8,"xtick.labelsize": 12,"ytick.labelsize": 12,"axes.labelsize": 12,"mathtext.fontset": "stix", # 公式字体,此项不要修改}with plt.rc_context(myenv):#进行绘图操作#...#注意plt.show()写在with里面,尽量把图片保存到磁盘plt.show()
或在myrc.pltstyle中输入配置,然后在代码中加入:
with plt.rc_context(fname="myrc.pltstyle"):#进行绘图操作#...#注意plt.show()写在with里面,尽量把图片保存到磁盘plt.show()
不太推荐使用文件式写法,因为有些值(如axes.prop_cycle)右侧可能是复杂的函数,难以确定在文件中应该怎么写。实在需要的话可以参考matplotlib默认rc文件。
多系列图表的自动样式控制
cycler可以自动为多系列的图表的每个系列使用不同的颜色、标记形状等
创建cycler有两种写法,其中第二种写法只能指定一个键,一般用于含有特殊字符的键。
cycler可以相加,这保证了第二种写法也能写出有多个键的对象。
若cycler中指定了多个键,则每个键的列表长度必须相同。
cycler(aa=[...],bb=[...],...)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()即可。
若图内容占满了整张图,则需要把图例放到图外面:
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
有时图例全部竖着排列太长,使其分列可以改变图例整体的形状
axe.legend(ncols=2)
网格
axe.grid(True, "both", "y", lw=0.5, ls=":",c="black")
