百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

手把手教你怎样用Python生成漂亮且精辟的图像(附教程代码)

toyiye 2024-06-21 12:20 8 浏览 0 评论

作者: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. style="whitegrid", 
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. style="whitegrid" 
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. style="whitegrid", 
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数据派”及姊妹号“数据派THU”获取更多讲座福利及优质内容。

相关推荐

为何越来越多的编程语言使用JSON(为什么编程)

JSON是JavascriptObjectNotation的缩写,意思是Javascript对象表示法,是一种易于人类阅读和对编程友好的文本数据传递方法,是JavaScript语言规范定义的一个子...

何时在数据库中使用 JSON(数据库用json格式存储)

在本文中,您将了解何时应考虑将JSON数据类型添加到表中以及何时应避免使用它们。每天?分享?最新?软件?开发?,Devops,敏捷?,测试?以及?项目?管理?最新?,最热门?的?文章?,每天?花?...

MySQL 从零开始:05 数据类型(mysql数据类型有哪些,并举例)

前面的讲解中已经接触到了表的创建,表的创建是对字段的声明,比如:上述语句声明了字段的名称、类型、所占空间、默认值和是否可以为空等信息。其中的int、varchar、char和decimal都...

JSON对象花样进阶(json格式对象)

一、引言在现代Web开发中,JSON(JavaScriptObjectNotation)已经成为数据交换的标准格式。无论是从前端向后端发送数据,还是从后端接收数据,JSON都是不可或缺的一部分。...

深入理解 JSON 和 Form-data(json和formdata提交区别)

在讨论现代网络开发与API设计的语境下,理解客户端和服务器间如何有效且可靠地交换数据变得尤为关键。这里,特别值得关注的是两种主流数据格式:...

JSON 语法(json 语法 priority)

JSON语法是JavaScript语法的子集。JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔花括号保存对象方括号保存数组JS...

JSON语法详解(json的语法规则)

JSON语法规则JSON语法是JavaScript对象表示法语法的子集。数据在名称/值对中数据由逗号分隔大括号保存对象中括号保存数组注意:json的key是字符串,且必须是双引号,不能是单引号...

MySQL JSON数据类型操作(mysql的json)

概述mysql自5.7.8版本开始,就支持了json结构的数据存储和查询,这表明了mysql也在不断的学习和增加nosql数据库的有点。但mysql毕竟是关系型数据库,在处理json这种非结构化的数据...

JSON的数据模式(json数据格式示例)

像XML模式一样,JSON数据格式也有Schema,这是一个基于JSON格式的规范。JSON模式也以JSON格式编写。它用于验证JSON数据。JSON模式示例以下代码显示了基本的JSON模式。{"...

前端学习——JSON格式详解(后端json格式)

JSON(JavaScriptObjectNotation)是一种轻量级的数据交换格式。易于人阅读和编写。同时也易于机器解析和生成。它基于JavaScriptProgrammingLa...

什么是 JSON:详解 JSON 及其优势(什么叫json)

现在程序员还有谁不知道JSON吗?无论对于前端还是后端,JSON都是一种常见的数据格式。那么JSON到底是什么呢?JSON的定义...

PostgreSQL JSON 类型:处理结构化数据

PostgreSQL提供JSON类型,以存储结构化数据。JSON是一种开放的数据格式,可用于存储各种类型的值。什么是JSON类型?JSON类型表示JSON(JavaScriptO...

JavaScript:JSON、三种包装类(javascript 包)

JOSN:我们希望可以将一个对象在不同的语言中进行传递,以达到通信的目的,最佳方式就是将一个对象转换为字符串的形式JSON(JavaScriptObjectNotation)-JS的对象表示法...

Python数据分析 只要1分钟 教你玩转JSON 全程干货

Json简介:Json,全名JavaScriptObjectNotation,JSON(JavaScriptObjectNotation(记号、标记))是一种轻量级的数据交换格式。它基于J...

比较一下JSON与XML两种数据格式?(json和xml哪个好)

JSON(JavaScriptObjectNotation)和XML(eXtensibleMarkupLanguage)是在日常开发中比较常用的两种数据格式,它们主要的作用就是用来进行数据的传...

取消回复欢迎 发表评论:

请填写验证码