我是阿布布的老父亲,技能树有点杂......
历史学研究僧、不知名管理咨询顾问、实用主义代码搬运者、偷懒至上数据民工、美妆市场的色弱钢铁直男、目前在学带娃。
这年头出来混口饭吃,谁还能不跟数据和图表打点交道?
尤其是咱们数据民工,作为全公司地势最低的一群人,各种数据如流水般从四方奔泻而来,简直不舍昼夜。
今天分享一个批量制图技巧,帮数据民工们减轻负担。
Photo by Alex on Unsplash
应用场景
同一维度下存在数量较多的数据分组,且需要对所有分组进行固定模式的可视化展现时。
用人来话就是:需要做几十张或者几百张图,这些图的类型是一样的,只不过展现的数据对象有点不同。
举个例子:
制定一个规模在一二百人的销售团队的奖金制度时,需要展示出销售人员上一期的奖金实绩,和切换到新的奖金制度后各种销售预测之下的预期奖金进行比较。
这样可以看到是哪几家欢喜哪几家愁,然后进一步检验新制度是否有效激励销售、是否存在影响销售士气的地方。
再举一例:
各种产品线、品牌、产品分类之下,有数量众多的SKU,需要把它们在某个期间内的增长表现展示出来,并且和市场大盘/竞品数字/公司内部标杆进行比较。
这样可以找到问题SKU和问题时间段后,然后进一步再从内部或者外部的各个维度切入做交叉分析,找到更加明确的问题点。
原理
偷懒这件事,有积极的和消极的两种方向。
消极向的上策是成功说服对方这件事其实不用做;中策乃是拖着让大家发现不做也没什么影响;下策就是在截止日前一天晚上通宵做。
我们来看积极向的偷懒方法,把一件事情做成功一次,然后用经济效率的方式让成功的做法多次重复。就像这样:
数据用例
今天用的示例数据,是从公开渠道获得的,2000年以来我国分省年度GDP指数(上年=100),这个指数反映的是当年GDP与上一年相比的变动趋势和程度。
我们想要知道,与全国整体的GDP变动相比,各个省份的GDP变动在时间序列上有什么特点?
在我国34个省级行政区中,我们获取了除港澳台之外的31个区划的数据。为了简化示例,这边先采用4个直辖市的指数来演示:
数据准备
我们用python的matplotlib库来实现可视化,matplotlib库可以接受的理想数据类型是numpy.array,所以我们打算先把excel文件里的数据转换成numpy.array格式。
# 导入pandas库,准备从excel里读取数据
import pandas as pd
# 指定文件位置, 读取数据到名为df_raw的DataFrame
file = 'D:/data.xlsx'
df_raw = pd.read_excel(file)
# 把指定列的value存入根据列名命名的numpy.array里
arr_CN = df_raw.loc[:,'全国'].values
arr_BJ = df_raw.loc[:,'北京'].values
arr_TJ = df_raw.loc[:,'天津'].values
arr_SH = df_raw.loc[:,'上海'].values
arr_CQ = df_raw.loc[:,'重庆'].values
# 运行print(type(arr_CN)),看到返回对象的类型是:
#
先成功一次
我们先来画全国和北京的GDP指数的对比图。
# 导入制图需要的matplotlib.pyplot库
import matplotlib.pyplot as plt
# 设定图片的风格、字体和字体大小
# 这边设定字体是因为matplotlib默认字体不支持中文字符
# 如果使用英语的话,可以忽略字体关键词
plt.style.use('seaborn')
plt.rcParams.update({"font.size":20,
"font.sans-serif":"SimHei"})
# 创建折线图的X轴和Y轴刻度名称
# X轴使用源数据里的年份
# Y轴可以使用默认值,也可以根据数据的范围定制
arr_xtick = df_raw.loc[:,'年度'].values
arr_ytick = [100,102,104,106,108,110,112,114,116,118,120]
# 创建构成图表的对象fig和ax
# 写程序的根本不需要找对象,因为可以自己创建~
fig, ax = plt.subplots()
# 画第一条线:全国GDP指数
ax.plot(arr_xtick, arr_CHN,
label = '全国',
linestyle = ':',
marker = 'o',
linewidth = 1.5)
# 画第二条线:北京GDP指数
ax.plot(arr_xtick, arr_BJ,
label = '北京',
marker = 's',
linewidth = 3.0)
# 指定X轴和Y轴上的刻度名称和字号
# 为了避免X轴刻度名称重复,我们让刻度名称旋转个45度
ax.set_xticks(arr_xtick)
ax.set_yticks(arr_ytick)
plt.xticks(rotation = 45)
ax.tick_params(axis = 'both',
which = 'major',
labelsize = 16)
# 给X轴、Y轴和图表整体标注名称,增加图表的可读性
ax.set_xlabel('年度', fontsize = 20)
ax.set_ylabel('增长指数(去年 = 100)', fontsize = 20)
ax.set_title('国内生产总值增长:全国 vs 北京', fontsize = 20)
ax.legend(prop = {'size':16})
# 规定图表的尺寸,前一个数字是宽度,后一个数字是高度
fig.set_size_inches([15, 8])
运行上面的代码,可以获得下面这张图:
复制成功方法
我们再来看一下这张图:
为了复制成功的做法,我们还需要一对好基友。
一个是蹲着的雪人
这是一个函数,负责接收待处理的数据,按照规定动作,输出图像。
另一个是流水线
我们要做一个Dict对象,把待处理的数据依次喂给函数。
完整的代码是这样的:
# 导入两个需要用到的python库
import matplotlib.pyplot as plt
import pandas as pd
# 指定excel文件的位置, 读取数据到名为df_raw的DataFrame
file = 'D:/data.xlsx'
df_raw = pd.read_excel(file)
# 把指定列的value存入以列名命名的numpy.array里
arr_CN = df_raw.loc[:,'全国'].values
arr_BJ = df_raw.loc[:,'北京'].values
arr_TJ = df_raw.loc[:,'天津'].values
arr_SH = df_raw.loc[:,'上海'].values
arr_CQ = df_raw.loc[:,'重庆'].values
# 这个就是流水线
# dict的key是城市名
# 对应的value是包含GDP指数的numpy.array
arr_dict = {'北京':arr_BJ,
'天津':arr_TJ,
'上海':arr_SH,
'重庆':arr_CQ}
# 设定图片的风格、字体和字体大小
# 这边设定字体是因为matplotlib默认字体无法显示中文字符
# 如果使用英语的话,可以忽略字体关键词
plt.style.use('seaborn')
plt.rcParams.update({"font.size":20,
"font.sans-serif":"SimHei"})
# 创建折线图的X轴和Y轴刻度名称
# X轴使用源数据里的年份
# Y轴可以使用默认值,也可以根据数据的范围定制
arr_xtick = df_raw.loc[:,'年度'].values
arr_ytick = [100,102,104,106,108,110,112,114,116,118,120]
# 把之前成功的方法打包成一个函数(雪人)
# 函数接收的参数有三个:
# 城市名,对应的numpy.array,城市名构成的图表标题
def plot_line_duel(city, arr, title):
# 创建构成图表的对象fig和ax
fig, ax = plt.subplots()
# 画第一条线:全国GDP指数
ax.plot(arr_xtick, arr_CN,
label = '全国',
linestyle = ':',
marker = 'o',
linewidth = 1.5)
# 画第二条线:传进来哪个城市就画哪个城市的
ax.plot(arr_xtick, arr,
label = city,
marker = 's',
linewidth = 3.0)
# 指定X轴和Y轴上的刻度名称和字号
# 为了避免X轴刻度名称重复,我们让刻度名称旋转个45度
ax.set_xticks(arr_xtick)
ax.set_yticks(arr_ytick)
plt.xticks(rotation = 45)
ax.tick_params(axis = 'both',
which = 'major',
labelsize = 16)
# 给X轴、Y轴和图表整体标注名称,增加图表的可读性
ax.set_xlabel('年度', fontsize = 20)
ax.set_ylabel('增长指数(去年 = 100)', fontsize = 20)
ax.set_title(title, fontsize = 20)
ax.legend(prop = {'size':16})
# 规定图表的尺寸,前一个数字是宽度,后一个数字是高度
fig.set_size_inches([15,8])
# 规定函数的输出:把图片保存到本地
# 保存文件名:就是传入给到程序的图表标题title
file_path = 'D:/plotting/'
file_name = title + '.jpg'
fig.savefig(file_path + file_name, quality = 100)
# 启动流水线:遍历arr_dict里的每一组key:value
# 把城市名、array、图表标题传给雪人
for key, value in arr_dict.items():
title = '国内生产总值增长:全国 vs ' + key
plot_line_duel(key, value, title)
运行完上面这段代码,四个直辖市的GDP指数和全国GDP指数的对比图就静静地躺在目标文件夹里面了:
接下来我们顺手看看这四张对比图告诉我们的信息:
北京和上海有着类似的模式:以2005年为界,之前的增长速度高于全国增长,之后便趋近于全国水平。
天津和重庆则是另外的模式:与北京和上海不同,2007年之后的十年间这两个直辖市的GDP增长高于全国增长,但是在最近两三年里先后出现回落。
写在最后
本文用四个直辖市的示例数据,介绍了批量生成时间序列上对比折线图的方法。运用中需要注意以下两点:
由于示例数据仅选取了四组,所以这边采用的是比较粗暴的数据获取方式。当需要制图的数据组数较多,比如说我们需要全部31个行政区与全国水平的对比图时,手动创建numpy.array的方式就比较不效率。此时需要再创建一个函数来从源文件里自动读取数据到各自的numpy.array里。
示例演示的图表类型是折线图。其实根据不同分析目的和数据特征,可以采用的还有如柱状图(类别对比)、直方图与箱线图(分布对比)、散点图(相关关系)等。这些图表都可以利用matplotlib生成,各位可以百度一下示例代码,或者参考matplotlib的官方文档。后续我也会在其他的话题里面分享。
谢谢你读到这里
辛苦了
请作者吃个冰淇淋
↓ ↓ ↓