Pandas中的链式方法

链式方法是当前比较流行的一种语法规则。
在过去的几个版本中,我们已经提到了几个支持链式方法的函数:

  • assign (0.16.0): 用于往 DataFrame 中增加新变量(类似于 dplyr 中的 mutate 函数)
  • pipe (0.16.2): 用于包含用户自定义的链式方法
  • rename (0.18.0): 用于改变轴名称
  • Window methods (0.18): 利用类似于 groupby 的 API 接口调用 pd.rolling* 和 pd.expanding* 顶层函数的 NDFrame 方法。

本文将从一个简单的例子说起:


Pandas中的链式方法_第1张图片
Pandas中的链式方法_第2张图片
Pandas中的链式方法_第3张图片

我觉得链式方法的代码非常易读,但是有些人却并了解它。它并不像重嵌套函数那样循环调用参数,它的所有代码和流程都是自上而下运行的,这大大增强了代码的可读性。

我最喜欢的示例来自 Jeff Allen,比较以下这两段功能相同但风格迥异的代码:


Pandas中的链式方法_第4张图片

Pandas中的链式方法_第5张图片

对比上述两种风格的代码,你会发现即使你不知道 R 语言中管道符号 %>% 的功能,你也能很轻易地看懂第二段代码。而对于第一段代码而言,你需要弄清楚代码的执行顺序以及如何处理相应的函数参数。

作为读者,你可能会说你不会写出类似于重嵌套风格的代码,但是大多数情况下你的代码应该是如下所示:

Pandas中的链式方法_第6张图片

我非常不喜欢这个风格的代码,因为我需要花费很多时间来思考如何对变量进行命名。这是非常令人困扰的事情,因为我们根本不关心 on_hill 这些中间变量。

上述代码的第四种实现方法是可行的,假设你拥有一个 JackAndJill 对象并且你可以自定义一些方法。那么你可以实现类似于 R 语言中的管道功能:

Pandas中的链式方法_第7张图片

但是这种方法的问题在于如果的数据不是 ndarray 或者 DataFrame 或者 DataArray,那么上述的方法就不存在了。而且我们很难对 DataFrame 的子类进行拓展从而来适应自定义的方法。同时,你所创建的从 DataFrame 中继承的子类可能仅适用于你自己的代码,无法和其他方法进行交互操作,因此你的代码将会非常零散。

或者你可以往 pandas 的项目中提交新的 pull request,从而实现自己的方法。但是你需要说服该项目的维护者,你的新方法值得加入到该项目中并维护之。而且 DataFrame 目前已经拥有超过 250 种的方法,因此我们不愿意增加更多的方法。

Pandas中的链式方法_第8张图片

DataFrame.pipe 的第一个参数是 DataFrame,我们只需要指明后续的参数即可。

成本

过长的链式代码的缺点是调试比较麻烦。由于没有生成中间变量值,所以如果代码出问题了,我们无法直接定位出问题在哪。Python 中的生成器也有类似的问题,借助生成器机制我们可以降低计算机内存消耗,但是此时我们比较难调试程序。

就我常用的探索分析过程而言,这并不是一个大问题。我平常处理的都是不会再更新的数据集,而且对原始数据集进行加工的步骤也不多。

对于规模较大的工作流程,你可能需要借助 pandas 的其他功能,比如 Airflow 或者 Luigi。
对于需要重复运行的中等规模 ETL 工作流程,我将借助装饰器来审查 DataFrame 每个工作步骤所产生的属性日志。

Pandas中的链式方法_第9张图片

借助我之前制作的一个用于验证管道中数据集有效性的软件库 engarde,我们可以很好地完成工作。

Inplace?

大多数 pandas 的方法都有一个默认值为 False 的关键词 inplace。通常来说,你不应该做 inplace 运算。

首先,如果你喜欢用链式规则来写代码的话,你肯定不会用 inplace 运算,因为这会导致最终返回的结果是 None,并中断相应的管道链。

其次,我怀疑存在一个适合 inplace 运算的构思模型。也就是说,最终结果并不会被分配到额外的存储器中。但实际上这可能是不真实的,pandas 中还存在许多下述用法:

Pandas中的链式方法_第10张图片

最后,类似于 ibis 或者 dask 这种类型的项目 inplace 运算并没有任何意义,因为此时你需要处理表达式或者建立可执行的 DAG 任务,而不仅仅是处理数据而已。

我觉得到此为止我并没有怎么写代码,更多的是在介绍一些额外的东西,我对此感到非常抱歉。接下来,让我们做一些探索性分析吧。


一架一天执行多趟航班执飞任务的飞机“堵机”了,会导致靠后的航班延误更长时间吗?

Pandas中的链式方法_第11张图片
Pandas中的链式方法_第12张图片

一天中较晚起飞的航班会延误更长时间吗?

Pandas中的链式方法_第13张图片
Pandas中的链式方法_第14张图片

我们将延误超过十小时的数据视为异常值并将其剔除掉。

Pandas中的链式方法_第15张图片
Pandas中的链式方法_第16张图片

接下来,我们仅考虑确实发生延误的航班数据。

Pandas中的链式方法_第17张图片
Pandas中的链式方法_第18张图片

哪个航班的延误情况最严重呢?

Pandas中的链式方法_第19张图片
Pandas中的链式方法_第20张图片

哪个航空公司的延误情况最严重呢?

Pandas中的链式方法_第21张图片

B6 是美国捷蓝航空公司。

I wanted to try out scikit-learn's new Gaussian Process module so here's a pretty picture.

Pandas中的链式方法_第22张图片

Pandas中的链式方法_第23张图片

[站外图片上传中……(26)]
[站外图片上传中……(27)]

[站外图片上传中……(28)]

[站外图片上传中……(29)]


谢谢阅读本文!由于我们更多地讨论了关于代码风格的问题而不是介绍实际案例操作,所以本文所介绍的内容比较抽象。谢谢你们的包容,下次我将介绍一个偏实务的话题!
[站外图片上传中……(30)]
原文链接:http://tomaugspurger.github.io/method-chaining.html
原文作者:Tom Augspurger
译者:Fibears

你可能感兴趣的:(Pandas中的链式方法)