将不同的功能应用于组对象中的不同项目:Python pandas

Posted

技术标签:

【中文标题】将不同的功能应用于组对象中的不同项目:Python pandas【英文标题】:Apply different functions to different items in group object: Python pandas 【发布时间】:2013-02-22 02:48:11 【问题描述】:

假设我有一个如下的数据框:

In [1]: test_dup_df

Out[1]:
                  exe_price exe_vol flag 
2008-03-13 14:41:07  84.5    200     yes
2008-03-13 14:41:37  85.0    10000   yes
2008-03-13 14:41:38  84.5    69700   yes
2008-03-13 14:41:39  84.5    1200    yes
2008-03-13 14:42:00  84.5    1000    yes
2008-03-13 14:42:08  84.5    300     yes
2008-03-13 14:42:10  84.5    88100   yes
2008-03-13 14:42:10  84.5    11900   yes
2008-03-13 14:42:15  84.5    5000    yes
2008-03-13 14:42:16  84.5    3200    yes 

我想在时间14:42:10 对重复数据进行分组,并对exe_priceexe_vol 应用不同的函数(例如,求和exe_vol 并计算exe_price 的体积加权平均值)。我知道我能做到

In [2]: grouped = test_dup_df.groupby(level=0)

对重复的索引进行分组,然后使用first()last() 函数获取第一行或最后一行,但这并不是我真正想要的。

有没有办法对不同列中的值进行分组,然后将不同的(由我编写的)函数应用于值?

【问题讨论】:

这不会给我两个单独的数据框吗?我想将它放在一个数据框中(类似于 grouped.first() 或 grouped.last() 的输出)。我可能错过了什么吗?? 【参考方案1】:

应用你自己的函数:

In [12]: def func(x):
             exe_price = (x['exe_price']*x['exe_vol']).sum() / x['exe_vol'].sum()
             exe_vol = x['exe_vol'].sum()
             flag = True        
             return Series([exe_price, exe_vol, flag], index=['exe_price', 'exe_vol', 'flag'])


In [13]: test_dup_df.groupby(test_dup_df.index).apply(func)
Out[13]:
                    exe_price exe_vol  flag
date_time                                  
2008-03-13 14:41:07      84.5     200  True 
2008-03-13 14:41:37        85   10000  True
2008-03-13 14:41:38      84.5   69700  True
2008-03-13 14:41:39      84.5    1200  True
2008-03-13 14:42:00      84.5    1000  True
2008-03-13 14:42:08      84.5     300  True
2008-03-13 14:42:10     20.71  100000  True
2008-03-13 14:42:15      84.5    5000  True
2008-03-13 14:42:16      84.5    3200  True

【讨论】:

这太棒了! flag = True的目的是什么? 该函数应该返回一个包含与原始列相同的所有列的系列。我不知道 flag 是什么意思,所以直接返回 True。【参考方案2】:

我喜欢@waitingkuo 的回答,因为它非常清晰易读。

无论如何我都会保留它,因为它看起来确实更快——至少在 Pandas 0.10.0 版中是这样。 may (hopefully) change in the future 的情况,因此请务必重新运行基准测试,尤其是在您使用不同版本的 Pandas 时。

import pandas as pd
import io
import timeit

data = '''\
date time       exe_price    exe_vol flag
2008-03-13 14:41:07  84.5    200     yes
2008-03-13 14:41:37  85.0    10000   yes
2008-03-13 14:41:38  84.5    69700   yes
2008-03-13 14:41:39  84.5    1200    yes
2008-03-13 14:42:00  84.5    1000    yes
2008-03-13 14:42:08  84.5    300     yes
2008-03-13 14:42:10  10    88100   yes
2008-03-13 14:42:10  100    11900   yes
2008-03-13 14:42:15  84.5    5000    yes
2008-03-13 14:42:16  84.5    3200    yes'''

df = pd.read_table(io.BytesIO(data), sep='\s+', parse_dates=[[0, 1]],
                   index_col=0)


def func(subf):
    exe_vol = subf['exe_vol'].sum()
    exe_price = ((subf['exe_price']*subf['exe_vol']).sum()
                 / exe_vol)
    flag = True
    return pd.Series([exe_price, exe_vol, flag],
                     index=['exe_price', 'exe_vol', 'flag'])
    # return exe_price

def using_apply():
    return df.groupby(df.index).apply(func)

def using_helper_column():
    df['weight'] = df['exe_price'] * df['exe_vol']
    grouped = df.groupby(level=0, group_keys=True)
    result = grouped.agg('weight': 'sum', 'exe_vol': 'sum')
    result['exe_price'] = result['weight'] / result['exe_vol']
    result['flag'] = True
    result = result.drop(['weight'], axis=1)
    return result

result = using_apply()
print(result)
result = using_helper_column()
print(result)

time_apply = timeit.timeit('m.using_apply()',
                      'import __main__ as m ',
                      number=1000)
time_helper = timeit.timeit('m.using_helper_column()',
                      'import __main__ as m ',
                      number=1000)
print('using_apply: t'.format(t = time_apply))
print('using_helper_column: t'.format(t = time_helper))

产量

                     exe_vol  exe_price  flag
date_time                                    
2008-03-13 14:41:07      200      84.50  True
2008-03-13 14:41:37    10000      85.00  True
2008-03-13 14:41:38    69700      84.50  True
2008-03-13 14:41:39     1200      84.50  True
2008-03-13 14:42:00     1000      84.50  True
2008-03-13 14:42:08      300      84.50  True
2008-03-13 14:42:10   100000      20.71  True
2008-03-13 14:42:15     5000      84.50  True
2008-03-13 14:42:16     3200      84.50  True

timeit 基准为:

using_apply: 3.0081038475
using_helper_column: 1.35300707817

【讨论】:

非常感谢! PS:希望创建一个新的数据框不会占用太多内存,因为我有超过 200 万行... @kunitomo:看来我错了——waitingkuo 展示了一种在多列上聚合的方法。 这仍然是 pandas 0.18 和 python 3.4.5 的两倍。【参考方案3】:

pandas 不是很熟悉,但在纯 numpy 中你可以这样做:

tot_vol = np.sum(grouped['exe_vol'])
avg_price = np.average(grouped['exe_price'], weights=grouped['exe_vol'])

【讨论】:

感谢您的快速回复。我想知道,既然我的“分组”现在是一个熊猫 DataFrameGroupBy 对象,我真的不能直接应用你的功能吗? 也许你可以把它变成另一个数据框here 我明白了。非常感谢。

以上是关于将不同的功能应用于组对象中的不同项目:Python pandas的主要内容,如果未能解决你的问题,请参考以下文章

如何将云功能部署到 .firebaserc 中的不同项目?

面向对象

Python中的多态如何理解?(转帖,让我很理解。)

Python入门自学进阶-Web框架——22DjangoAdmin项目应用-定制页面

python 面向对象

您如何在不同的项目中将 JPA 对象与 GWT 一起使用?