作者:Ryoko
编辑:才哥
☆
大家好,我是才哥。
今天我们聊一个matplotlib
绘图问题,就是关于如何对坐标轴标签(常见的x轴标签)按照自定义的顺序走。
话说这是在昨天,发生在咱们交流群的故事:一位同学提问 “matplotlib
画柱状图时,横坐标是从表格中指定列获取的,如何设置横坐标的顺序呢?”
原始数据结构如下图所示,需要对学历分组求平均工资后画柱状图,顺序应为按学历由低到高,即 ['大专', '本科', '硕士', '博士']
。
原始数据预览
看到案例数据,感觉先分组求均值,如何再进行绘图就行了。但是似乎直接这样得到的可视化图不满足需求,坐标轴标签顺序与期望的不一致。怎么回事呢?
1. 直接作图
我们先看一下分组求均值后结果数据的情况:
代码语言:javascript复制grp = df.groupby('学历要求')['平均工资'].mean().reset_index()
grp
结果数据预览
我们发现,在分组后学历要求分组里的排序是博士、大专、本科和硕士,与我们期望的['大专', '本科', '硕士', '博士']
其实不一样。
这种情况下直接作图结果如下:
代码语言:javascript复制import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
x = grp['学历要求']
y = grp['平均工资']
plt.bar(x, y)
plt.show()
直接作图
很明显,这个图并非我们期望的,那么如何按照我们期望的x轴坐标轴标签顺序作图呢?
以下,我们将介绍多种方式,希望能供大家参考~
2. 绘图时设置坐标轴标签顺序
以下方法来自才哥,简单直接的处理:
代码语言:javascript复制plt.bar([3, 0, 1, 2], grp['平均工资'], tick_label=grp['学历要求'])
直接设置标签顺序
在以上作图代码中,我们简单讲解下三个参数作用:
- 第1个参数[3,0,1,2]含义可以理解为将原图里第0个数据也就是博士对应数据放在第3个位置,将1-3个数据放到0-2位置,从而可以得到我们需要的结果;
- 第2个参数
y = grp['平均工资']
就是柱状图高度数据,也就是会根据第1个参数设置的位置进行显示; - 第3个参数
tick_label
就是设置坐标轴标签,这里就是grp['学历要求']
。
核心是第1个参数,可以简单理解为它就是你期望的坐标轴标签顺序。
指定顺序
如果遇到标签较多的情况,我们已知期望顺序列表但是人均排序似乎有点累,这里可以用列表位置索引
帮我们快速找到期望顺序。
# 期望标签列表顺序(变量,自己决定顺序)
index_list = ['大专','本科','硕士','博士']
# 期望顺序列表
index = [index_list.index(x) for x in grp.学历要求.to_list()]
index
代码语言:javascript复制[3, 0, 1, 2]
3. 绘图前先对x,y数据进行排序
当然,除了上述在绘图时对坐标轴标签指定顺序外,我们还可以在绘图前将绘图核心参数x,y
的值进行指定排序。
# Series数据
grp = df.groupby('学历要求')['平均工资'].mean()
grp
Series数据
代码语言:javascript复制# 指定期望的标签列表顺序
x = ['大专', '本科', '硕士', '博士']
# 根据x 获取对应 y值
y = [grp[label] for label in x]
plt.bar(x, y)
绘图结果
由于忘记了 matplotlib
和 pandas
之间有着很好的兼容性,笔者一开始打算先得到需求顺序的 x = ['大专', '本科', '硕士', '博士']
和 y = [ 具体的值 ]
再通过 plt.bar(x, y)
制图,因此在数据整理上跑偏了,比如:
y = [grp.at[label] for label in x]
下面分享一下笔者走的歪路
4. 自定义文本排序
计算机系统中存在一些默认顺序,比如数字顺序 0123456
,字母顺序 abcdefg
,还有 ASCII 码
表顺序等,但对于中文习惯的顺序,比如学历,可能就需要自己制定了。那让我们专注排序本身,先来看看分组后的数据:
分组数据
x = grp['学历要求']
和 y = grp['平均工资']
分别得到两个 Series 对象。那么应该如何将它们绑定在一起,根据 x 按照指定的 order_x = ['大专', '本科', '硕士', '博士']
排序呢?
4.1. 打包排序
我们可以通过 zip()
函数将其打包使之成为一个整体,然后通过列表生成式,得到修改顺序后的 y 轴值列表 order_y
,将 order_x
和 order_y
传入制图即可。
order_y = [t[1] for i in order_x for t in list(zip(x, y)) if i == t[0]]
其中:
- list(zip(x, y)) 的打印结果为:
[('博士', 20.0), ('大专', 5.25), ('本科', 11.0), ('硕士', 15.33)]
; - 列表生成式重新取出的元祖列表为:
[('大专', 5.25), ('本科', 11.0), ('硕士', 15.33), ('博士', 20.0)]
; - order_y 为:
[5.25, 11.0, 15.33, 20.0]
。
4.2. 利用 pandas 重设索引排序
整体代码:
代码语言:javascript复制grp = df.groupby('学历要求')['平均工资'].mean().reset_index()
df_map = pd.DataFrame({'学历要求': ['大专', '本科', '硕士', '博士']}).reset_index().set_index('学历要求')
grp['order'] = grp['学历要求'].map(df_map['index'])
grp.sort_values('order', inplace=True)
x = grp['学历要求']
y = grp['平均工资']
首先制作了一个 df_map
,相当于是排序对照表,将索引作为新的顺序列。
df_map
将上面的顺序列,按照原 grp
的学历要求列,映射添加到新的 order 列 。
添加排序
再按照 order
列排序即可。
进行排序
4.3. 利用 CategoricalDtype 自定义顺序
CategoricalDtype
是 pandas 中一种用于处理【类别】的数据类型,可以指定类别是否有序。
我们通过这个方法创建了一个有序 “类别类”,并修改学历要求列的数据类型为此类,此时各类学历文本便具有了其默认顺序,之后便可以对其直接排序。
代码语言:javascript复制from pandas.api.types import CategoricalDtype
grp = df.groupby('学历要求')['平均工资'].mean().reset_index()
cat_order = CategoricalDtype(['大专', '本科', '硕士', '博士'], ordered=True)
grp['学历要求'] = grp['学历要求'].astype(cat_order)
grp['学历要求']
CategoricalDtype带有可排序属性
我们可以看到 类别下有排序属性'大专' < '本科' < '硕士' < '博士'
,然后排序作图即可。
grp.sort_values('学历要求', inplace=True)
x = grp['学历要求']
y = grp['平均工资']
plt.bar(x,y)
CategoricalDtype
以上就是本次全部内容,希望对大家在进行自定义坐标轴排序的时候有帮助。
题外话:其实关于 CategoricalDtype
,还有很多非常重要的作用,这个留给大家思考吧,后续我们可以继续讨论!