Pandas数据框重采样聚合函数如何使用多列和自定义函数?

16

这里有一个例子:

# Generate some random time series dataframe with 'price' and 'volume'
x = pd.date_range('2017-01-01', periods=100, freq='1min')
df_x = pd.DataFrame({'price': np.random.randint(50, 100, size=x.shape), 'vol': np.random.randint(1000, 2000, size=x.shape)}, index=x)
df_x.head(10)
                     price   vol
2017-01-01 00:00:00     56  1544
2017-01-01 00:01:00     70  1680
2017-01-01 00:02:00     92  1853
2017-01-01 00:03:00     94  1039
2017-01-01 00:04:00     81  1180
2017-01-01 00:05:00     70  1443
2017-01-01 00:06:00     56  1621
2017-01-01 00:07:00     68  1093
2017-01-01 00:08:00     59  1684
2017-01-01 00:09:00     86  1591

# Here is some example aggregate function:
df_x.resample('5Min').agg({'price': 'mean', 'vol': 'sum'}).head()
                     price   vol
2017-01-01 00:00:00   78.6  7296
2017-01-01 00:05:00   67.8  7432
2017-01-01 00:10:00   76.0  9017
2017-01-01 00:15:00   74.0  6989
2017-01-01 00:20:00   64.4  8078

然而,如果我想要提取依赖于多个列的其他聚合信息,我该怎么办?

例如,我想在此处添加2列,分别称为all_upall_down

这2列的计算定义如下:

每5分钟内,1分钟采样价格和成交量下跌的次数,称为all_down列,在此期间它们上涨的次数称为all_up列。

以下是我期望看到的2列:

                     price   vol  all_up  all_down
2017-01-01 00:00:00   78.6  7296       2         0
2017-01-01 00:05:00   67.8  7432       0         0
2017-01-01 00:10:00   76.0  9017       1         0
2017-01-01 00:15:00   74.0  6989       1         1
2017-01-01 00:20:00   64.4  8078       0         2

这个功能依赖于2列。但在Resampler对象的agg函数中,它似乎只接受3种类型的函数:

  • 一个字符串或分别应用于每个列的函数。
  • 一个分别应用于每个列的函数列表。
  • 一个键匹配列名的字典。仍然只将值作为函数应用于单个列。

所有这些功能似乎都不符合我的需求。


你能给我们一个需要的例子吗?你期望的输出是什么? - cs95
1
@cᴏʟᴅsᴘᴇᴇᴅ,您好,我刚刚添加了一个预期输出数据框,all_up列计算每5分钟内1分钟价格上涨且交易量也上涨的次数。all_down则相反。 - StayFoolish
好的,这很有帮助,但我需要更多的数据。你只给了10分钟的数据。你能加上20分钟的数据,以便得出你的输出吗? - cs95
我认为你的 all_down 值不正确。请再检查一下?根据我的计算,我得到了一个不同的答案。 - cs95
谢谢。我认为pricevol数据是随机整数,这可能是我们得到不同值的原因?实际上,我只手动计算了前10分钟的all_upall_down - StayFoolish
1个回答

24

我认为你需要使用groupby+Grouper和自定义函数的apply,而不是resample

def func(x):
   #code
   a = x['price'].mean()
   #custom function working with 2 columns
   b = (x['price'] / x['vol']).mean()
   return pd.Series([a,b], index=['col1','col2'])

df_x.groupby(pd.Grouper(freq='5Min')).apply(func)

或者使用resample对所有支持的聚合函数进行采样,并将其输出与自定义函数的输出连接在一起:

def func(x):
    #custom function
    b = (x['price'] / x['vol']).mean()
    return b

df1 = df_x.groupby(pd.Grouper(freq='5Min')).apply(func)
df2 = df_x.resample('5Min').agg({'price': 'mean', 'vol': 'sum'}).head()

df = pd.concat([df1, df2], axis=1)

编辑:使用函数diff检查递减和递增,与0比较,将两个条件使用&连接,并通过sum计数:

EDIT: 为检查递减和递增,使用函数 diff 并与 0 进行比较,将两个条件用 & 连接,最后通过 sum 进行计数。
def func(x):
    v = x['vol'].diff().fillna(0)
    p = x['price'].diff().fillna(0)
    m1 = (v > 0) & (p > 0)
    m2 = (v < 0) & (p < 0) 
    return pd.Series([m1.sum(), m2.sum()], index=['all_up','all_down'])


df1 = df_x.groupby(pd.Grouper(freq='5min')).apply(func)
print (df1)
                     all_up  all_down
2017-01-01 00:00:00       2         0
2017-01-01 00:05:00       0         0

df2 = df_x.resample('5Min').agg({'price': 'mean', 'vol': 'sum'}).head()
df = pd.concat([df2, df1], axis=1)
print (df)
                      vol  price  all_up  all_down
2017-01-01 00:00:00  7296   78.6       2         0
2017-01-01 00:05:00  7432   67.8       0         0

感谢您提出pd.Grouper函数,这是我不知道的。但是我尝试了您建议的方法,似乎并没有按照我的预期工作。df_x.groupby(pd.Grouper(freq='5Min')).apply(func) - StayFoolish
它起作用了。谢谢。只是跟进一下,这是否意味着 groupby + Grouper + applyresample + apply 更灵活?另外,使用 apply 方法和 aggaggregate 方法有什么区别吗? - StayFoolish
1
嗯,我想是这样。但不是100%确定。但是groupby+apply更常见,所以在我看来更稳定、更好实现。 - jezrael

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接