Bump Charts with Tableau or Matplotlib

最近在读  Ryan Sleeper的《Practical Tableau》。Ryan Sleeper是 Tableau Zen Master,此书主要讲述了 100 多个Tableau技巧, 实例和布局,以及如何结合多个技巧就能绘制不同的新图。比如Bump Charts。

Chapter44 ,Ryan Sleeper这样介绍Bump Charts ,“Bump charts are an effective way to show how dimension members are ranking across different measures over time. For example, you may want to see how specific product categories have ranked in sales for your company from year to year. Or maybe you want to use discrete months as your element of time to see if the rankings for product categories change based on seasonality. Or maybe you want to do something outside of work and see how your fantasy football players are ranking across different statistics from week to week!” 

Bump Charts x轴通常为离散的时间,比如周、月、季度,y轴为排名,[1,2,3..],排名超过七八个的话看上去就不太好。

Bump Charts 想表达的信息:随着时间变化,同一维度下不同对象的排名变化。比如可以绘制公司2019年日化产品类别中几个子产品的销量排名,方便观察在期间某几个促销策略和其他事件的影响;再比如绘制一年中几个旅游地的旅行创收,寻找一些季节性规律等等。

跟着Ryan Sleeper 的书用tableau画了Bump Chart,直接拖拉拽,几十秒搞定。基础是数据在观察维度内排序,绘制直线和圆点图双轴。

下面是用tableau画的图,python造的随机数,某产品在A、B、C三个市场的月销售额排名,圆点文字为同市场月环比增长率

 

 

接着,我个人用matplotlib实现了一样的图,基础是三条点线图,需要提前处理数据,维度内的排名和环比增长率计算,最后还添加了图例说明。数据和完整代码:github地址

造数据和处理数据代码:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] # 指定默认字体
plt.rcParams['axes.unicode_minus'] = False # 解决保存图像是负号'-'显示为方块的问题

"""自己造数据 """
df=pd.DataFrame(pd.date_range(start='1/1/2019',end='31/12/2019',normalize=True),columns=['Date']) # pandas生成时间序列
df['Sales']=np.random.randint( low=100,high =500 , size = 365 )                      # 随机生成365个100-500之间的整数,实验数据,不加单位了
df['Market']=[ np.random.choice(['Market_'+i for i in 'ABC']) for c  in range(365)] # Market _A ,_B,_C
df['Month']=df['Date'].apply(lambda x : x.strftime("%m")+"")                      # 根据日期生成月份字段

"""处理数据"""
d1=df.groupby(["Month","Market"]).agg('sum').reset_index()                 # 按照月份、市场类型分组,求销售总额
d1['rank']=d1.groupby(["Month"])['Sales'].rank(ascending=False)           # 按照月份分组,在三个市场之间进行排序,即得到每月销量排名
d1=d1.sort_values(["Market","Month"])
d1["lmnth_sales"]=d1.groupby(["Market"])["Sales"].shift(1)
d1['pct']=round((d1['Sales']/d1['lmnth_sales']-1)*100,0)               #计算不同市场间,每月销售额环比增长率
d1['pct']=d1['pct'].fillna("").apply(lambda x : str(int(x))+"%"*bool(x) )      #增长率转换为百分比
# d1

造数据:生成2019年产品在A、B、C三个市场每天的销售额,此处忽略单位。用pandas生成2019年的365天的时间序列,用numpy中的random方法生成一系列随机数。

处理数据 :按照时间分组,分别对三个市场求和。每月三个市场销售额排名;计算每个市场月环比增长率。

matplotlib绘图代码:

"""绘图"""
mcolors=["#4e79a7","#e36668","#f28e2b"]                      
color="#294D4A"
plt.figure(figsize=(13,7),dpi=200)
# y轴倒序  rank=1,坐标为1,在y轴比 rank=2 高
plt.ylim(4,0)
# y轴标签
plt.yticks([1,2,3],["No.1","No.2","No.3"],fontsize=14)

for c,m in enumerate(['Market_A', 'Market_B', 'Market_C']):
    d=d1[d1['Market']==m]
    plt.plot(d['Month'],d['rank'],linewidth=1.5,marker="o",markersize=24,color=mcolors[c])

    for x,n in enumerate(d['Month']):
        y=d[d['Month']==n]['rank'].values[0]
        s=d[d['Month']==n]['pct'].values[0]
#         print(x,y,s)
        plt.text(x=x,y=y+0.03,s=s,fontdict={"fontsize":12,"color":'w','fontweight':'heavy'},ha="center")

#显示 图例
# markersccale  图中标记大小:图例标记大小    
plt.legend(['Market_A', 'Market_B', 'Market_C'],labelspacing=1,markerscale=0.4,fontsize=12,\
          edgecolor='w',frameon=False)
# print(plt.gca())

#将刻度小横线设置为白色,标签颜色为其他颜色
plt.tick_params(axis='x',color='w',labelcolor=color,labelsize=14)
plt.tick_params(axis='y',color='w',labelcolor=color)

#隐藏y轴
# plt.yticks([])
for i in ['top','right','left','bottom']:
    plt.gca().axes.spines[i].set_visible(False)
plt.title("2019年产品X在不同市场的销量与增长情况",fontdict={'color':color,'fontsize':16})
plt.text(-0.5,0.5,"By:chenboshi",fontdict={"family":'fantasy','color':'gray','fontsize':10})
plt.savefig(r"C:\mypython\plots\BumpCharts.png")
plt.show()
plt.close()

 Matplotlib画的图

posted on 2020-09-10 16:13  chenboshi  阅读(421)  评论(0编辑  收藏  举报

导航