【问题标题】:Pandas: How to compute a conditional rolling/accumulative maximum within a groupPandas:如何计算组内的条件滚动/累积最大值
【发布时间】:2021-11-08 17:08:32
【问题描述】:

我希望在不使用极其缓慢的 for 循环的情况下在 condrolmax 列(基于 close 列)(条件滚动/累积最大值)中实现以下结果。

Index    close    bool       condrolmax
0        1        True       1
1        3        True       3
2        2        True       3
3        5        True       5
4        3        False      5
5        3        True       3 --> rolling/accumulative maximum reset (False cond above)
6        4        True       4
7        5        False      4
8        7        False      4
9        5        True       5 --> rolling/accumulative maximum reset (False cond above)
10       7        False      5
11       8        False      5
12       6        True       6 --> rolling/accumulative maximum reset (False cond above)
13       8        True       8
14       5        False      8
15       5        True       5 --> rolling/accumulative maximum reset (False cond above)
16       7        True       7
17       15       True       15
18       16       True       16

创建此数据框的代码:

# initialise data of lists.
data = {'close':[1,3,2,5,3,3,4,5,7,5,7,8,6,8,5,5,7,15,16],
        'bool':[True, True, True, True, False, True, True, False, False, True, False,
                False, True, True, False, True, True, True, True],
        'condrolmax': [1,3,3,5,5,3,4,4,4,5,5,5,6,8,8,5,7,15,16]}
 
# Create DataFrame
df = pd.DataFrame(data)

我确信可以将其矢量化(一个衬里)。有什么建议吗?

再次感谢!

【问题讨论】:

  • 选择的答案是没有按照你的要求做,所以这个答案或者你的问题有错误,你能澄清一下吗?
  • 请注意,问题是在condrolmax 列中实现预期的结果,这在问题中清楚地显示。术语rolling 可能无法准确描述需求,也不需要我们在解决方案中使用rolling() 函数。只是问题的措辞问题。
  • 没有提到rolling() 的窗口大小(如果适用),窗口大小实际上可以根据bool 列的模式而变化。因此,如果要使用滚动功能,最好是expanding() 而不是rolling()。在这种情况下,在组内使用cummax() 会更直接。这就是我们选择cummax() 而不是rolling.max() 的原因。

标签: python pandas conditional-statements


【解决方案1】:

您可以设置组,然后使用cummax(),如下:

# Set group: New group if current row `bool` is True and last row `bool` is False
g = (df['bool'] & (~df['bool']).shift()).cumsum()   

# Get cumulative max of column `close` within the group 
df['condrolmax'] = df.groupby(g)['close'].cummax()

结果:

print(df)

    close   bool  condrolmax
0       1   True           1
1       3   True           3
2       2   True           3
3       5   True           5
4       3  False           5
5       3   True           3
6       4   True           4
7       5  False           5
8       7  False           7
9       5   True           5
10      7  False           7
11      8  False           8
12      6   True           6
13      8   True           8
14      5  False           8
15      5   True           5
16      7   True           7
17     15   True          15
18     16   True          16

【讨论】:

  • cummaxrolling+max不是相同的东西,cummax 将等同于 expanding+max。 @plonfat 如果这是你想要的?如果是,请编辑问题,因为它不正确
  • @mozway 我认为 OP 使用了不正确的术语来描述要求。但从预期的结果中可以看出他/她想要什么。
  • 好的,谢谢,虽然condrolmax 实际上是可以使用的变量,谢谢!尽管如此,这个问题还是应该被编辑
  • @mozway 我同意这个问题的措辞有点混乱。让我们等待一段时间,看看 OP 是否会编辑它。如果没有,我会编辑它。不用担心。
  • 感谢您为我编辑问题并为您的困惑道歉。
【解决方案2】:

首先使用您的条件(布尔值从 False 更改为 True)和 cumsum 进行分组,然后在 groupby 之后应用您的 rolling

group = (df['bool']&(~df['bool']).shift()).cumsum()
df.groupby(group)['close'].rolling(2, min_periods=1).max()

输出:

0     0      1.0
      1      3.0
      2      3.0
      3      5.0
      4      5.0
1     5      3.0
      6      4.0
      7      5.0
      8      7.0
2     9      5.0
      10     7.0
      11     8.0
3     12     6.0
      13     8.0
      14     8.0
4     15     5.0
      16     7.0
      17    15.0
      18    16.0
Name: close, dtype: float64

作为一列插入:

df['condrolmax'] = df.groupby(group)['close'].rolling(2, min_periods=1).max().droplevel(0)

输出:

    close   bool  condrolmax
0       1   True         1.0
1       3   True         3.0
2       2   True         3.0
3       5   True         5.0
4       3  False         5.0
5       3   True         3.0
6       4   True         4.0
7       5  False         5.0
8       7  False         7.0
9       5   True         5.0
10      7  False         7.0
11      8  False         8.0
12      6   True         6.0
13      8   True         8.0
14      5  False         8.0
15      5   True         5.0
16      7   True         7.0
17     15   True        15.0
18     16   True        16.0

注意。如果您希望边界包含在滚动中,请在rolling中使用min_periods=1

【讨论】:

    【解决方案3】:

    我不确定我们如何使用线性代数和向量化来使这个函数更快,但是使用列表理解,我们可以编写一个更快的算法。首先,定义函数为:

    def faster_condrolmax(df):
        df['cond_index'] = [df.index[i] if df['bool'][i]==False else 0 for i in 
        df.index]
        df['cond_comp_index'] = [np.max(df.cond_index[0:i]) for i in df.index]
        df['cond_comp_index'] = df['cond_comp_index'].fillna(0).astype(int)
        df['condrolmax'] = np.zeros(len(df.close))
        df['condrolmax'] = [np.max(df.close[df.cond_comp_index[i]:i]) if 
                   df.cond_comp_index[i]<i else df.close[i] for 
                   i in range(len(df.close))]
        return df
    

    然后,你可以使用:

    !pip install line_profiler
    %load_ext line_profiler
    

    添加和加载行分析器并查看每行代码需要多长时间:

    %lprun -f faster_condrolmax faster_condrolmax(df)
    

    这将导致: Each line profiling results

    看看整个函数需要多长时间:

    %timeit faster_condrolmax(df)
    

    这将导致: Total algorithm profiling result

    如果您使用 SeaBean 的函数,您可以获得比我建议的函数速度减半的更好结果。但是,SeaBean 的估计速度似乎并不稳健,要估计他的功能,您应该在更大的数据集上运行它,然后再决定。这都是因为 %timeit 报告如下: SeaBean's function profiling result

    【讨论】:

      猜你喜欢
      • 2013-02-24
      • 2019-07-01
      • 2023-03-04
      • 2014-02-16
      • 2021-10-12
      • 1970-01-01
      • 1970-01-01
      • 2020-08-07
      • 1970-01-01
      相关资源
      最近更新 更多