作者:Fabian Bosler

翻译:车前子

校对:吴振东

本文约4800字,建议阅读15分钟

本文将介绍如何利用Python生成图像并将结果做出可视化分析。

在上周的文章《用python从不同的表单中提取数据》中,学习了如何从不同的源(Google Sheets、CSV和Excel)检索和统一数据。本教程与上一篇文章是相互独立的,所以你不必担心错过了上周的文章。

在今天的教程中,你将会学到:
  • 如何预处理和合并数据,

  • 如何探索并分析数据,

  • 如何做出漂亮的图表对结果进行可视化。

这篇教程面向:
  • 经常从事数据相关工作,

  • 对Python和Pandas有初步理解的人。

情景概述:

你的任务是找出提高你的销售团队业绩的方法。在我们所假设的情况下,潜在客户有相当自发的需求。当客户提出需求时,你的销售团队会在系统中设置一个订单商机。然后,你的销售代表安排一次会议,会议将在发现订单商机前后举行。你的销售代表有一个开支预算,预算中包括会议费用和餐费。销售代表支付这些花销并将发票交给会计团队处理。在潜在客户决定是否愿意接受你的报价后,勤劳的销售代表会跟踪订单商机是否转化为销售。
你可以使用以下三个数据集进行分析:
  • order_leads(包含所有订单线索和转化信息)

  • sales_team(包括公司和负责的销售代表)

  • invoices(提供发票和参与者的信息)

载入程序包和属性设置:

1. import json
2. import pandas as pd
3. import numpy as np
4. %matplotlib inline
5. import matplotlib.pyplot as plt
6. import seaborn as sns
7. sns.set(
8.     font_scale=1.5,
9.     ,
10.     rc={'figure.figsize':(20,7)}
11. )  

这里用到都是相当标准的库。你有可能会需要运行下面的命令来在你的Notebook里安装seaborn。
1. !pip install seaborn

载入数据:

你可以下载并合并上周文章中的实例数据,或者点击这里下载文件并将其加载到Notebook中。

https://github.com/FBosler/Medium-Data-Exploration

1. sales_team = pd.read_csv('sales_team.csv')
2. order_leads = pd.read_csv('order_leads.csv')
3. invoices = pd.read_csv('invoices.csv')  

sales_team数据集的前两行

order_leads数据集的前两行

invoice数据集的前两行

开始探索数据:

总转化率的发展趋势:

转化率随时间的变化
显然,从2017年初开始转化率似乎有所下降。与首席销售官核实后发现,当时有一个竞争对手进入市场。很高兴知道这点,但我们现在对此无能为力。
1. _ = order_leads.set_index(pd.DatetimeIndex(order_leads.Date)).groupby(
2.     pd.Grouper(freq='D')
3. )['Converted'].mean()
4.
5. ax = _.rolling(60).mean().plot(figsize=(20,7),title='Conversion Rate Over Time')
6.
7. vals = ax.get_yticks()
8. ax.set_yticklabels(['{:,.0f}%'.format(x*100) for x in vals])
9. sns.despine()  

  • 1.我们使用下划线“_”作为临时变量。我通常会这样生成以后不会再使用的一次性变量。

  • 2.我们对order_leads.Date使用pd.DateTimeIndex,将其设置为序号。

  • 3.使用pd.grouped(freq='D')按天对数据进行分组。或者,你可以将频率更改为W、M、Q或Y(周、月、季或年)。

  • 4.我们计算每天“转化”的平均值,即当天订单的转化率。

  • 5.我们使用.rolling(60)和.mean()得到60天的平均值。

  • 6.然后,我们设置yticklables的格式,使其显示百分比符号。

不同销售代表的转化率:

销售代表之间的转化率似乎有很大的差异,我们再调查一下。
1. orders_with_sales_team = pd.merge(order_leads,sales_team,on=['Company Id','Company Name'])
2. ax = sns.distplot(orders_with_sales_team.groupby('Sales Rep Id')['Converted'].mean(),kde=False)
3. vals = ax.get_xticks()
4. ax.set_xticklabels(['{:,.0f}%'.format(x*100) for x in vals])
5. ax.set_title('Number of sales reps by conversion rate')
6. sns.despine()  

就使用的函数而言,这里没有太多的新函数。但请注意我们如何使用sns.distplot将数据绘制到轴上。
如果我们回忆销售团队的数据,我们记得并非所有的销售代表都有相同数量的客户,这肯定会对结果有影响!让我们检查一下。

不同分配账户数量的转化率
我们可以看到,转化率的数量似乎与分配给销售代表的帐户数量成反比,那些降低的转换率是有意义的。毕竟,销售代表的账户越多,他在每个人身上花费的时间就越少。
1. def vertical_mean_line(x, **kwargs):
2.     ls = {"0":"-","1":"--"}
3.     plt.axvline(x.mean(), linestyle =ls[kwargs.get("label","0")],
4.                 color = kwargs.get("color", "r"))
5.     txkw = dict(size=15, color = kwargs.get("color", "r"))
6.     tx = "mean: {:.1f}%\n(std: {:.1f}%)".format(x.mean()*100,x.std()*100)
7.     label_x_pos_adjustment = 0.015
8.     label_y_pos_adjustment = 20
9.     plt.text(x.mean() + label_x_pos_adjustment, label_y_pos_adjustment, tx, **txkw)
10.
11. sns.set(
12.     font_scale=1.5,
13.
14. )
15.
16. _ = orders_with_sales_team.groupby('Sales Rep Id').agg({
17.     'Converted': np.mean,
18.     'Company Id': pd.Series.nunique
19. })
20. _.columns = ['conversion rate','number of accounts']
21.
22. g = sns.FacetGrid(_, col="number of accounts", height=4, aspect=0.9, col_wrap=5)
23. g.map(sns.kdeplot, "conversion rate", shade=True)
24. g.set(xlim=(0, 0.35))
25. g.map(vertical_mean_line, "conversion rate")

在这里,我们先创建一个函数,它将把垂直线映射到每个子块中,并用数据的平均值和标准偏差来注释这条线。然后,我们设置一些seaborn绘图默认值,如较大的字体font_scale和白色网格作为样式 style。

用餐的影响:

用餐数据示例
看起来我们有用餐日期和时间的数据,来快速看一下时间的分布:
1. invoices['Date of Meal'] = pd.to_datetime(invoices['Date of Meal'])
2. invoices['Date of Meal'].dt.time.value_counts().sort_index()  

out:
07:00:00    5536
08:00:00    5613
09:00:00    5473
12:00:00    5614
13:00:00    5412
14:00:00    5633
20:00:00    5528
21:00:00    5534
22:00:00    5647
看起来我们可以总结一下:
1. invoices['Type of Meal'] = pd.cut(
2.     invoices['Date of Meal'].dt.hour,
3.     bins=[0,10,15,24],
4.     labels=['breakfast','lunch','dinner']
5. )

请注意如何使用pd.cut将连续变量分组,这样做的意义是早餐是8点还是9点开始可能并不重要。
另外,请注意如何使用.dt.hour,我们只能这样做,因为我们将invoices[‘Date of Meal’]转换为日期时间。.dt是一个“访问器”,一共有三类cat,str,dt。如果你的数据是正确的类型,则可以使用这些访问器及其方法进行直接操作(计算效率高且简洁)。
不凑巧的是,我们必须把第一个字符串invoices['Participants']转换成合法的JSON,这样可以提取参与者的数量。
1. def replace(x):
2.     return x.replace("\n ",",").replace("' '","','").replace("'",'"')
3.
4. invoices['Participants'] = invoices['Participants'].apply(lambda x: replace(x))
5. invoices['Number Participants'] = invoices['Participants'].apply(lambda x:  len(json.loads(x)))

现在来合并数据。为此,我们先将所有invoice数据与order_leads数据通过 company Id左连接。然而,合并数据会导致所有的用餐数据都匹配到订单上,也有些很久以前的用餐匹配到新的订单数据上。为了减少这种情况,我们计算了用餐和订单之间的时间差,并且只考虑在订单前后5天的用餐。
仍有一些订单匹配了多个用餐信息。这可能发生在同时有两个订单也有两次用餐的情况。两个订单线索都会匹配两次用餐。为了去掉那些重复数据,我们只保留与订单时间最接近的那个订单。
1. # combine order_leads with invoice data
2. orders_with_invoices = pd.merge(order_leads,invoices,how='left',on='Company Id')
3.
4. # calculate days between order leads and invocies
5. orders_with_invoices['Days of meal before order'] = (
6.     pd.to_datetime(orders_with_invoices['Date']) - orders_with_invoices['Date of Meal']
7. ).dt.days
8.
9. # limit to only meals that are within 5 days of the order
10. orders_with_invoices = orders_with_invoices[abs(orders_with_invoices['Days of meal before order']) < 5]
11.
12. # To mnake sure that we don't cross assign meals to multiple orders and therefore create duplicates
13. # we first sort our data by absolute distance to the orders
14. orders_with_invoices = orders_with_invoices.loc[
15.     abs(orders_with_invoices['Days of meal before order']).sort_values().index
16. ]
17.
18. # keep the first (i.e. closest to sales event) sales order
19. orders_with_invoices = orders_with_invoices.drop_duplicates(subset=['Order Id'])
20.
21. orders_without_invoices = order_leads[~order_leads['Order Id'].isin(orders_with_invoices['Order Id'].unique())]
22.
23. orders_with_meals = pd.concat([orders_with_invoices,orders_without_invoices],sort=True)

部分合并后数据集
我创建了一个柱状图函数,其中已经包含一些样式。通过该函数进行绘图,可以使可视化更快捷。我们现在就来使用这个函数。
1. def plot_bars(data,x_col,y_col):
2.     data = data.reset_index()
3.     sns.set(
4.         font_scale=1.5,
5.         ,
6.         rc={'figure.figsize':(20,7)}
7.     )
8.     g = sns.barplot(x=x_col, y=y_col, data=data, color='royalblue')
9.
10.     for p in g.patches:
11.         g.annotate(
12.             format(p.get_height(), '.2%'),
13.             (p.get_x() + p.get_width() / 2., p.get_height()),
14.             ha = 'center',
15.             va = 'center',
16.             xytext = (0, 10),
17.             textcoords = 'offset points'
18.         )
19.
20.     vals = g.get_yticks()
21.     g.set_yticklabels(['{:,.0f}%'.format(x*100) for x in vals])
22.
23.     sns.despine()

用餐种类的影响:

1. orders_with_meals['Type of Meal'].fillna('no meal',inplace=True)
2. _ = orders_with_meals.groupby('Type of Meal').agg({'Converted': np.mean})
3. plot_bars(_,x_col='Type of Meal',y_col='Converted')

真的!有没有用餐信息的订单转化率有明显的差别。不过,午餐的转化率似乎略低于晚餐或早餐。

时机的影响(如用餐发生在订单前或后):

1. _ = orders_with_meals.groupby(['Days of meal before order']).agg(
2.     {'Converted': np.mean}
3. )
4. plot_bars(data=_,x_col='Days of meal before order',y_col='Converted'))

“用餐在订单前几天发生”为负数意味着用餐是在订单线索出现之后。我们可以看到,如果用餐在在订单线索出现前发生似乎对转化率有一个积极的影响,看来对订单的事先了解使我们的销售代表更有优势。

合并所有结果:

现在我们将使用热图同时显示数据的多个维度。为此我们先创建一个函数。
1. def draw_heatmap(data,inner_row, inner_col, outer_row, outer_col, values):
2.     sns.set(font_scale=1)
3.     fg = sns.FacetGrid(
4.         data,
5.         row=outer_row,
6.         col=outer_col,
7.         margin_titles=True
8.     )
9.
10.     position = left, bottom, width, height = 1.4, .2, .1, .6
11.     cbar_ax = fg.fig.add_axes(position)
12.
13.     fg.map_dataframe(
14.         draw_heatmap_facet,
15.         x_col=inner_col,
16.         y_col=inner_row,
17.         values=values,
18.         cbar_ax=cbar_ax,
19.         vmin=0,
20.         vmax=.4
21.     )
22.
23.     fg.fig.subplots_adjust(right=1.3)
24.     plt.show()
25.
26. def draw_heatmap_facet(*args, **kwargs):
27.     data = kwargs.pop('data')
28.     x_col = kwargs.pop('x_col')
29.     y_col = kwargs.pop('y_col')
30.     values = kwargs.pop('values')
31.     d = data.pivot(index=y_col, columns=x_col, values=values)
32.     annot = round(d,4).values
33.     cmap = sns.color_palette("RdYlGn",30)
34.     # cmap = sns.color_palette("PuBu",30) alternative color coding
35.     sns.heatmap(d, **kwargs, annot=annot, center=0, fmt=".1%", cmap=cmap, linewidth=.5)  

然后,我们应用一些数据处理来探索用餐花销与订单价值的关系,并将我们的用餐时间划分为订单前(Before Order)、订单前后(Around Order)、订单后(After Order),而不是从负4到正4的天数,因为这解读起来会比较麻烦。
1. # Aggregate the data a bit
2. orders_with_meals['Meal Price / Order Value'] = orders_with_meals['Meal Price']/orders_with_meals['Order Value']
3. orders_with_meals['Meal Price / Order Value'] = pd.qcut(
4.     orders_with_meals['Meal Price / Order Value']*-1,
5.     5,
6.     labels = ['Least Expensive','Less Expensive','Proportional','More Expensive','Most Expensive'][::-1]
7. )
8.
9. orders_with_meals['Timing of Meal'] = pd.qcut(
10.     orders_with_meals['Days of meal before order'],
11.     3,
12.     labels = ['After Order','Around Order','Before Order']
13. )
14.
15.
16. data = orders_with_meals[orders_with_meals['Type of Meal'] != 'no meal'].groupby(
17.     ['Timing of Meal','Number Participants','Type of Meal','Meal Price / Order Value']
18. ).agg({'Converted': np.mean}).unstack().fillna(0).stack().reset_index()

运行下面的代码片段将生成多维热图。
1. draw_heatmap(
2.     data=data,
3.     outer_row='Timing of Meal',
4.     outer_col='Type of Meal',
5.     inner_row='Meal Price / Order Value',
6.     inner_col='Number Participants',
7.     values='Converted'
8. )

热图很漂亮,但一开始有点难以解读。让我们来看一下。

图表总结了4个不同维度的影响:

  • 用餐时间:订单后、订单前后、订单前(外行)

  • 用餐类型:早餐、晚餐、午餐(外列)

  • 餐费/订单价值:最低、较低、成比例、较贵、最贵(内行)

  • 参加人数:1,2,3,4,5(内列)

当然,看起来图表底部的颜色更暗/更高,这表明

  • 在订单前用餐的转化率更高

  • 似乎晚餐的转换率更高,当只有一个人用餐时

  • 看起来相对订单价值,更贵的餐费对转化率有积极的影响

结果:

1.不要给你的销售代表超过9个客户(因为转化率下降很快);

2.确保每个订单线索都有会议/用餐(因为这使转化率翻倍);

3.当只分配一名员工给顾客时,晚餐是最有效的;

4.你的销售代表应该支付大约为订单价值8%到10%的餐费;

5.时机是关键,理想情况下,让你的销售代表尽早知道交易即将达成。

点击这里获得以上代码:GitHub Repo/Jupyter Notebook

地址:https://github.com/FBosler/Medium-Data-Exploration

热图备注:

为了解决潜在的格式化问题,你可以先将其卸载(必须在终端中完成),然后再运行以下语句,将MaMattLIB降级到版本3.1.0:

!pip install matplotlib==3.1.0  

如果有任何问题请联系我。

原文链接:
https://towardsdatascience.com/how-to-explore-and-visualize-a-dataset-with-python-7da5024900ef
编辑:王菁
校对:林亦霖

译者简介

车前子,北大医学部,流行病与卫生统计专业博二在读。从临床医学半路出家到数据挖掘,感到了数据分析的艰深和魅力。即使不做医生,也希望用数据为医疗健康做一点点贡献。

翻译组招募信息

工作内容:需要一颗细致的心,将选取好的外文文章翻译成流畅的中文。如果你是数据科学/统计学/计算机类的留学生,或在海外从事相关工作,或对自己外语水平有信心的朋友欢迎加入翻译小组。

你能得到:定期的翻译培训提高志愿者的翻译水平,提高对于数据科学前沿的认知,海外的朋友可以和国内技术应用发展保持联系,THU数据派产学研的背景为志愿者带来好的发展机遇。

其他福利:来自于名企的数据科学工作者,北大清华以及海外等名校学生他们都将成为你在翻译小组的伙伴。

点击文末“阅读原文”加入数据派团队~

点击“阅读原文”拥抱组织

独家 | 手把手教你怎样用Python生成漂亮且精辟的图像(附教程代码)相关推荐

  1. 独家 | 手把手教你从有限的数据样本中发掘价值(附代码)

    作者:Bety Rodriguez-Milla 翻译:和中华 校对:吴金笛 本文约2800字,建议阅读8分钟. 本文展示了当数据稀缺时,如何一步步进行分析从而得到一些见解. [ 导读 ]本文是系列文章 ...

  2. 独家 | 手把手教TensorFlow(附代码)

    上一期我们发布了"一文读懂TensorFlow(附代码.学习资料)",带领大家对TensorFlow进行了全面了解,并分享了入门所需的网站.图书.视频等资料,本期文章就来带你一步步 ...

  3. 软件_手把手教vscode配置c++,python开发环境

    原创:软件_手把手教vscode配置c++,python开发环境 之前主用Python作为项目开发语言,将项目迁移到arm边缘盒子上后发现arm的cpu不给力,软件速度低于预期,所以计划将部分程序改为 ...

  4. 用python画皇冠_【推荐】手把手教你如何用Python画一棵漂亮樱花树含源码

    最近给大家整理了一下,挑了一些我觉得不错的代码分享给大家手把手教你如何用Python画一棵漂亮樱花树含源码. 动态生成樱花 效果图(这个是动态的): import turtle as T import ...

  5. 掌握python编程语言tensorflow_手把手教你eclipse集成Python语言+Tensorflow环境

    本文主要向大家介绍了手把手教你eclipse集成Python语言+Tensorflow环境,通过具体的内容向大家展示,希望对大家学习Python语言有所帮助. 1.安装Eclipse 百度Eclips ...

  6. python k线合成_手把手教你写一个Python版的K线合成函数

    手把手教你写一个Python版的K线合成函数 在编写.使用策略时,经常会使用一些不常用的K线周期数据.然而交易所.数据源又没有提供这些周期的数据.只能通过使用已有周期的数据进行合成.合成算法已经有一个 ...

  7. 手把手教你如何用Python制作一个电子相册?末附python教程

    这里简单介绍一下python制作电子相册的过程,主要用到tkinter和pillow这2个库,tkinter用于窗口显示照片,pillow用来处理照片,照片切换分为2种方式,一种是自动切换(每隔5秒) ...

  8. win7开机卡在正在启动_手把手教你大白菜PE启动盘安装win7最详细的图解教程

    一.准备工作,制作好PE启动盘U盘一个,把所需要的文件提前复制到U盘里: 2,把启动盘插到电脑USB插口上: 3-1,按下电脑主机开机按钮: 3-2,如果电脑是打开的请重启电脑: 4,设置从U盘启动: ...

  9. python程序创建词云 中国地图_就这么简单!使用Python生成漂亮的词云

    原标题:就这么简单!使用Python生成漂亮的词云 作者:Linux迷 链接:https://www.linuxmi.com 词云是一种数据可视化技术,用于表示文本数据,其中每个单词的大小表示其出现的 ...

最新文章

  1. 对学校的希望和寄语_家长对孩子的期望寄语精选
  2. Bitcoin0.21版 公链开发(4) Apache windows上安装
  3. 全国信息学奥林匹克联赛 ( NOIP2014) 复赛 模拟题 Day1 长乐一中
  4. thief book怎么用_战略管理工具箱--30个好用的战略管理好工具
  5. Oracle优化问题
  6. lambdaQuery中EQ、NE、GT、LT、GE、LE的用法 (来自网络收集)
  7. java.util.stream.IntStream
  8. 体脂的计算Java_简单测试体脂率的两种经验公式
  9. 关于电商平台推出无门槛优惠券不得不说的几个问题
  10. POJ-3580-SuperMemo(splay的各种操作)
  11. 网络直播平台是否需要《信息网络传播视听节目许可证》?
  12. matlab求异面直线的公垂线,求异面直线的公垂线
  13. 假设检验中的P值 与显著性水平的联系
  14. boost heap - d_ary_heap 的自定义compare函数用法
  15. Matlab/Simulink之STM32开发
  16. 灰点工业网口相机多相机同时使用
  17. Solidworks使用技巧:文件重命名:在装配体中重命名零部件 在文件夹中重命名文件
  18. ebay 获取商品详细信息 getitem getItemByLegacyId FindItemsByProduct getProductDetailsRequest
  19. 音视频格式不兼容?mac音视频格式转换器哪个好?
  20. 彻底搞懂JS事件循环机制(event loop)

热门文章

  1. SQLAlchemy的使用---外键ForeignKey数据库创建与连接
  2. 博客园自定义页面风格设计 后续篇(页面设计模式及代码高亮 鼠标点击效果升级)...
  3. 《云数据管理:挑战与机遇》2.3.3 恢复和提交
  4. 2015-2016-2 《Java程序设计》 学生博客及Git@OSC 链接
  5. Esper学习之十二:EPL语法(八)
  6. 转:Oracle greatest函数
  7. 想写点什么留下点念想
  8. 缩放浏览器不会换行_深入了解现代浏览器之三 - 渲染
  9. Java编程笔试时输入问题:如何输入固定长度、不定长度的一维数组?如何输入固定长度、不定长度的二维数组?
  10. 声明式服务调用feign原理图解