译:理解pandas中的tansform函数

原文链接:Understanding the Transform Function in Pandas

transform示例

引言

pandas库拥有着丰富的方法操控数据,这是他的一大优势,但有时候,因为庞大的功能,你总会碰到一些不了解功能和用法的函数。如果你的大脑保持着Excel处理数据的思维惯性,在切换到pandas解决类似问题时就会遇到困难[1]transform就是这样一个函数。我已经使用pandas有一段时间了,但是还没有机会使用它,所我最近花了点时间去了解下他的用法,以及他能干些什么。这篇文章通过一个例子来演示transform如何在汇总数据时提示效率的。

什么是transform?

对于transform介绍的最好的是jake VanderPlas的书Python Data Science Handbook,就像书中提到的,transform函数是用来配合groupby操作的。这里假定大多数读者都可能都经常使用groupby中的aggregate,filter或者apply来汇总数据。但是transform跟这些相比理解起来有点小小困难,特别是对那些Excel用户来说。这里并不是说transform概念复杂,而是需要一个思路上的转换。jake将他的书已jupyter notebook形式发布,通过阅读可以了解下为什么transform'非常特别。

aggregation操作返回压缩版本的数据,transformation可以返回经过完整数据经过转换的版本。经过转换,输出的数据和输入的数据结构相同

With that basic definition, I will go through another example that can explain how this is useful in other instances outside of centering data.

problem set

在这个例子中,我们将会虚构一个销售数据:点我下载,为了节约篇幅,这里只保留12行。

account name order sku quantity unit price ext price
0 383080 Will LLC 10001 B1-20000 7 33.69 235.83
1 383080 Will LLC 10001 S1-27722 11 21.12 232.32
2 383080 Will LLC 10001 B1-86481 3 35.99 107.97
3 412290 Jerde-Hilpert 10005 S1-06532 48 55.82 2679.36
4 412290 Jerde-Hilpert 10005 S1-82801 21 13.62 286.02
5 412290 Jerde-Hilpert 10005 S1-06532 9 92.55 832.95
6 412290 Jerde-Hilpert 10005 S1-47412 44 78.91 3472.04
7 412290 Jerde-Hilpert 10005 S1-27722 36 25.42 915.12
8 218895 Kulas Inc 10006 S1-27722 32 95.66 3061.12
9 218895 Kulas Inc 10006 B1-33087 23 22.55 518.65
10 218895 Kulas Inc 10006 B1-33364 3 72.30 216.90
11 218895 Kulas Inc 10006 B1-20000 -1 72.18 -72.18

如果所见,表中有三个订单(10001, 10005 and 10006) ,每个订单包含多个产品。

现在我们想通过以上数据知道:每个产品占总订单百分比
例如,加入我们知道订单10001总计金额是576.12,那么这个订单下面每个产品的价值比例依次是。

  • B1-20000 = $235.83 or 40.9%
  • S1-27722 = $232.32 or 40.3%
  • B1-86481 = $107.97 or 18.7%
    通常要计算这些信息,并且添加到原表中应该如何实现呢?先计算每个订单的总价,然后将这些数据置回明细表,得到百分比。在Excel中,你可以通过分类汇总计算值。

第一种方法-合并

如果你非常熟悉pandas,你可能会先进行分组,得到一个新的DataFrame,然后进行合并,经过系列步骤得到最终结果,你可能通常是这样擦做的
导入模块,读取数据

import pandas as pd
df = pd.read_excel("sales_transactions.xlsx")

然后,你会通过groupby根据订单号进行分组,然后使用sum函数对价格进行汇总。

df.groupby('order')["ext price"].sum()
order
10001     576.12
10005    8185.49
10006    3724.49
Name: ext price, dtype: float64

下面这张图演示了进行groupby操作时发生了什么。

groupby:split、apply、combine

最关键的部分是,如何将分组后的计算结果拼接到原始数据中,本能的想法是利用聚合数据创建一个新的dataframe,然后通过merge执行右连接拼接到原始数据。

order_total = df.groupby('order')["extprice"].sum().rename("Order_Total").reset_index()
df_1 = df.merge(order_total)
#merge的时候省略了一些细节,因为有共同字段Order,所以自动对齐,实际上相当于df.merge(order_total,on ='order',how = left)
df_1["Percent_of_Order"] = df_1["ext price"] / df_1["Order_Total"]
account name order sku quantity unit price ext price order total Order_Total Percent_of_Order
0 383080 Will LLC 10001 B1-20000 7 33.69 235.83 576.12 576.12 0.409342
1 383080 Will LLC 10001 S1-27722 11 21.12 232.32 576.12 576.12 0.403249
2 383080 Will LLC 10001 B1-86481 3 35.99 107.97 576.12 576.12 0.187409
3 412290 Jerde-Hilpert 10005 S1-06532 48 55.82 2679.36 8185.49 8185.49 0.327330
4 412290 Jerde-Hilpert 10005 S1-82801 21 13.62 286.02 8185.49 8185.49 0.034942
5 412290 Jerde-Hilpert 10005 S1-06532 9 92.55 832.95 8185.49 8185.49 0.101759
6 412290 Jerde-Hilpert 10005 S1-47412 44 78.91 3472.04 8185.49 8185.49 0.424170
7 412290 Jerde-Hilpert 10005 S1-27722 36 25.42 915.12 8185.49 8185.49 0.111798
8 218895 Kulas Inc 10006 S1-27722 32 95.66 3061.12 3724.49 3724.49 0.821890
9 218895 Kulas Inc 10006 B1-33087 23 22.55 518.65 3724.49 3724.49 0.139254
10 218895 Kulas Inc 10006 B1-33364 3 72.30 216.90 3724.49 3724.49 0.058236
11 218895 Kulas Inc 10006 B1-20000 -1 72.18 -72.18 3724.49 3724.49 -0.019380

这样当然没问题,但是要经过很多中介步骤。

第二种方法:使用Transform

使用之前的数据,我们看看transformgroupby能得到什么

df.groupby('order')["ext price"].transform('sum')
0      576.12
1      576.12
2      576.12
3     8185.49
4     8185.49
5     8185.49
6     8185.49
7     8185.49
8     3724.49
9     3724.49
10    3724.49
11    3724.49
dtype: float64

你可以看到,单纯的groupby进行sum聚合得到的数据尺寸和transform不同。普通聚合函数返回聚合以后的结果,而transform返回的计算结果保留的原始数据的索引,这就是transform的独特性。
借此,我们可以不用费什么脑子,两部结算处结果

df["Order_Total"] = df.groupby('order')["ext price"].transform('sum')
df["Percent_of_Order"] = df["ext price"] / df["Order_Total"]
account name order sku quantity unit price ext price order total Order_Total Percent_of_Order
0 383080 Will LLC 10001 B1-20000 7 33.69 235.83 576.12 576.12 0.409342
1 383080 Will LLC 10001 S1-27722 11 21.12 232.32 576.12 576.12 0.403249
2 383080 Will LLC 10001 B1-86481 3 35.99 107.97 576.12 576.12 0.187409
3 412290 Jerde-Hilpert 10005 S1-06532 48 55.82 2679.36 8185.49 8185.49 0.327330
4 412290 Jerde-Hilpert 10005 S1-82801 21 13.62 286.02 8185.49 8185.49 0.034942
5 412290 Jerde-Hilpert 10005 S1-06532 9 92.55 832.95 8185.49 8185.49 0.101759
6 412290 Jerde-Hilpert 10005 S1-47412 44 78.91 3472.04 8185.49 8185.49 0.424170
7 412290 Jerde-Hilpert 10005 S1-27722 36 25.42 915.12 8185.49 8185.49 0.111798
8 218895 Kulas Inc 10006 S1-27722 32 95.66 3061.12 3724.49 3724.49 0.821890
9 218895 Kulas Inc 10006 B1-33087 23 22.55 518.65 3724.49 3724.49 0.139254
10 218895 Kulas Inc 10006 B1-33364 3 72.30 216.90 3724.49 3724.49 0.058236
11 218895 Kulas Inc 10006 B1-20000 -1 72.18 -72.18 3724.49 3724.49 -0.019380

如果你不需要中间字段(汇总字段),可以一步得出百分比

df["Percent_of_Order"] = df["ext price"] / df.groupby('order')["ext price"].transform('sum')

下图演示了transform的工作逻辑,可以对照下之前groupby

transform-example.png

通过上面的例子,我相信你也认为transform功能非常强大。

译者讨论

以前我解决这类问题的思路是先groupby,然后通过自己写的lambda函数查找到结果在apply到新列中,通过第一个例子,才知道merge原来是这样用的,缺少sql的思维真是害死人。


  1. 这段翻译可能谬误比较大

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 161,601评论 4 369
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,367评论 1 305
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 111,249评论 0 254
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,539评论 0 217
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,967评论 3 295
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,929评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,098评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,825评论 0 207
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,609评论 1 249
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,796评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,282评论 1 265
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,603评论 3 261
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,277评论 3 242
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,159评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,959评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 36,079评论 2 285
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,874评论 2 277

推荐阅读更多精彩内容