Pandas groupby函数 transform函数 实战

 

Pandas(Python)中的transform函数一开始可能有点难以理解,并且大多数数据科学家不会马上使用到它。 但对于数据科学家来说,transform函数是一个用于数据清洗、转换或者生成报表的非常方便的工具,尤其是对于大量数据的处理速度上。

应用实例

考虑这样一个例子,我们有来自某商店的表格如下:

某商店的销售数据

而我们希望生成一个新列,统计每个用户的平均购买额,如下:

我们希望生成的表

我们起码有两种方法实现这个操作:

  1. 先采用groupby计算平均并生成一个新的dataframe,再和上一个dataframe进行merge
  2. 采用transform函数

第一种方法:

import pandas as pd 
# 读取数据
df=pd.read_csv(“purchase.csv”) 
# 1.生成新的dataframe,计算mean
mean_purchase =df.groupby('User_ID')["Purchase"].mean().rename("User_mean").reset_index()
# 2.和上一步的dataframe合并
df_1 = df.merge(mean_purchase) 

第二种方法:

# 直接使用transform函数
df["User_Mean"] = df.groupby('User_ID')["Purchase"].transform('mean')

很明显,使用transform函数非常清晰快捷地达到了我们的目标。

transform函数有多快?

至于transform函数比groupby-merge能快多少,我们可以用一个1000000行的数据来验证这个想法。

首先生成一个1000000行的数据:

import pandas as pd
import random

data = pd.DataFrame({
    'C' : [random.choice(('a','b','c')) for i in range(1000000)],
    'A' : [random.randint(1,10) for i in range(1000000)],
    'B' : [random.randint(1,10) for i in range(1000000)]

})

然后分别计算两种方法的时间:

第一种方法:

%%timeit

data.groupby('C')["A"].mean()
mean =data.groupby('C')["A"].mean().rename("N").reset_index()
df_1 = data.merge(mean)

我在anaconda的ipython里的Output为:268 ms ± 4.33 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

第二种方法:

%%timeit
data['N1'] = data.groupby(['C'])['A'].transform('mean')

这个得到的output为:68.4 ms ± 590 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

可以看出,具有大量数据的情况下,transform函数比groupby-merge要快得多,并且代码更加简洁清晰,不会带来中间产物(groupby-merge会产生一个中间的dataframe,如果写在一起的话会使函数嵌套层数过多)

transform和apply函数有什么区别?

我尝试了一些情景,发现这两个函数的功能有很多重叠:

df = pd.DataFrame({'A': range(3), 'B': range(1, 4)})

# 下面两个call得到的结果相同
df.apply(lambda x: x+1)
df.transform(lambda x: x+1)

# 下面两个call得到的结果也相同
df['d']=df.transform(lambda x: x.A+x.B, axis=1)
df['d']=df.apply(lambda x: x.A+x.B, axis=1)

# 下面两个call得到的结果还是相同
df.transform([np.exp,np.sqrt])
df.apply([np.exp,np.sqrt])

transform函数的官方文档是这么介绍的:

DataFrame.transform(self,func,axis=0,*args,**kwargs)→ 'DataFrame'[source]

Call func on self producing a DataFrame with transformed values.

Produced DataFrame will have same axis length as self.

看上去和apply区别没有很大。但起码在我刚刚列举的那个场景中,transform函数是很好用的。

如果大家有其他的发现,欢迎留言探讨~

  •  
  • Pandas具有丰富的功能让我们探索,transform就是其中之一,利用它可以高效地汇总数据。
  • Python Data Science Handbook 是一个关于pandas的优秀资源。
  • 在该书的描述中,transform是与groupby(pandas中最有用的操作之一)组合使用的。一般情况下,我们在groupby之后使用aggregate , filter 或 apply来汇总数据,transform可能稍难理解。
  • 该书对应的github资源 jupyter notebooks里的内容可能对理解transform的独特作用有所帮助。

aggregation会返回数据的缩减版本,而transformation能返回完整数据的某一变换版本供我们重组。这样的transformation,输出的形状和输入一致。一个常见的例子是通过减去分组平均值来居中数据。



作者:treelake
链接:https://www.jianshu.com/p/509d7b97088c
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 

 UnderstandingtheTransformFunctioninPandas, 假设我们有下面的销售数据,有三个不同的 订单id (10001, 10005和10006),每个 订单id又含有多个产品。

import pandas as pd

df = pd.read_excel("sales_transactions.xlsx")
df

 

 

问题

每个订单中各个单品费用分布及占比

例如,订单10001总价$576.12,细分一下

B1-20000 = $235.83 or 40.9%
S1-27722 = $232.32 or 40.3%
B1-86481 = $107.97 or 18.7%

 

如果还是没看懂,直接看代码吧。通过代码理解,比看大邓唠叨更有效果。

Talk is cheep, show me your code

第一种方法-merge

如果熟悉pandas的话,应该先试图对dataframe进行groupby

df.groupby('order')['ext price'].sum()

 

Run

order
10001 576.12
10005 8185.49
10006 3724.49
Name: ext price, dtype: float64

 

现在的难点是如何将刚刚分析出的数据与原始数据加工到一起,因为新旧数据的长度是不一样的。

最直接的方法就是新建一个dataframe

order_total = df.groupby('order')['ext price'].sum().rename('Order_Total').reset_index()
df_1 = df.merge(order_total)
df_1['Percent_of_Order'] = df_1['ext price']/df_1['Order_Total']
df_1

 

 

第二种方法- transform

使用原始dataframe,让我们看看经过transform与groupby处理后,我们得到的都是什么

df.groupby('order')['ext price'].transform('sum')

 

Run

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
Name: ext price, dtype: float64

 

我们发现得到的数据长度与groupby长度不同(长度是3),而与原始数据df的长度是一样的。

所以我们可以一行代码解决本文的问题

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

 

 

  • 8
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值