【问题标题】:Pandas Rolling Conditional FunctionPandas 滚动条件函数
【发布时间】:2021-03-10 11:28:16
【问题描述】:

我在熊猫中滚动使用.apply.aggregate 时遇到了一些麻烦(当然假设这是解决我的问题的正确方法)。假设我有一个包含两列 A 和 B 的数据框。如果 A 的值为 1,我想创建一个包含 B 的滚动平均值的列 C。更一般地说,我希望能够应用滚动自定义函数,某些条件涉及数据框的几列(例如,当 B > x 和/或 C = y 等时,列 A 的滚动总和等)。

import pandas as pd
import numpy as np
df2 = pd.DataFrame({'A':[1,1,1,0,0,0,1,1,1],'B': [50,40,50,-20,20,10,10,-5,-2]}, index = np.arange(9))

所需的输出将是(假设滚动窗口为 3):

df2 = pd.DataFrame({'A':[1,1,1,0,0,0,1,1,1],'B': [50,40,50,-20,20,10,10,-5,-2],\
 'C': [np.nan, np.nan, 46.67, 45, 50, np.nan, 10, 2.50, 1]}, index = np.arange(9))

我尝试定义一个函数mean_1如下:

def mean_1(x):
    return np.where(x['A'] == 1, np.mean(x['B']), np.nan)

df2['C'] = df2.rolling(3).apply(mean_1)

并得到错误:'Series' object has no attribute 'A' 我想这与文档中的raw = False 有关 谢谢

【问题讨论】:

  • 你使用的是什么版本的 numpy 和 pandas?我无法重现您的错误。
  • @NabilDaoud 熊猫 1.1.2 和 numpy 1.18.5

标签: pandas rolling-computation


【解决方案1】:

你可以先屏蔽'A'不为1的'B'值,然后应用滚动方法:

mask_map = df2.A != 1
df2['C'] = df2.B.mask(mask_map).rolling(3, min_periods=1).mean().round(2)

输出:

   A   B      C
0  1  50  50.00
1  1  40  45.00
2  1  50  46.67
3  0 -20  45.00
4  0  20  50.00
5  0  10    NaN
6  1  10  10.00
7  1  -5   2.50
8  1  -2   1.00

请注意,第一个值不是NaN,因为我们指定了min_periods=1。这意味着无论缺失值的数量如何,我们都在取平均值。因此,如果是这种情况,并且您真的想将第一个值设置为 NaN,则可以这样做:

df2.iloc[:n-1, df2.columns.get_loc('C')] = np.nan

其中n 是窗口大小(在本例中为 3)。这将返回所需的确切输出。

最好的!

【讨论】:

  • 请注意 - 我通常相信 OP 并不是要使用 min_periods=1,因为它在这里以一种有意义的方式改变了结果(您的输出与 OP 要求的不同)。您可以在符合要求的答案之后将其作为评论发布,但不要将您的答案建立在纯粹的猜测上(在这种情况下,我会说这是一个远大的目标)
  • 我也错误地屏蔽了输出。更新答案,谢谢!事实上,我假设 OP 需要 min_periods=1 因为索引 6 的结果是 10,除了它自己的值之外只有 NaN。但是你说的OP不想要min_periods=1是什么意思?你的答案是这样做的(很好的答案,顺便说一句),但是手动,因为不管NaN,你都在取平均值。
【解决方案2】:

您可以矢量化您的解决方案:

df2['C'] = df2['A'].eq(1).mul(df2['B']).rolling(3).sum()\
    .div(df2['A'].eq(1).rolling(3).sum())\
    .round(2)

如果您在 any 功能方面要求更笼统 - 我的建议是 - 始终尝试矢量化,通常避免 .apply(...)

【讨论】:

  • 谢谢!我从没想过使用 .eq、.mul、.div 等来分解所有内容。
【解决方案3】:

这是一种接近所需输出的方法。

df2['C'] = df2.apply(lambda row: np.where(row['A']==1, row['B'], np.nan), axis=1).rolling(3, min_periods=1).apply(np.nanmean)

不同之处在于上面给出了索引 0 和 1 的值。

【讨论】:

  • 感谢您的回答。这是一个有趣的解决方案,并且具有索引 0 和 1 的值......我现在可以接受它
  • @CTRX - 检查 2 个其他解决方案。不知道数据的大小 - 但我推测,这种方法会比其他方法慢得多 - 单个 .apply(...) 已经很慢,在这里你有 2 个,一些参考:stackoverflow.com/a/54432584/11610186跨度>
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-26
  • 2018-12-17
  • 1970-01-01
  • 2020-04-11
  • 1970-01-01
  • 2015-05-14
相关资源
最近更新 更多