我如何计算滚动 idxmax

how do I calculate a rolling idxmax

考虑 pd.Series s

import pandas as pd
import numpy as np

np.random.seed([3,1415])
s = pd.Series(np.random.randint(0, 10, 10), list('abcdefghij'))
s

a    0
b    2
c    7
d    3
e    8
f    7
g    0
h    6
i    8
j    6
dtype: int64

我想获取 rolling window of 3

的最大值的索引
s.rolling(3).max()

a    NaN
b    NaN
c    7.0
d    7.0
e    8.0
f    8.0
g    8.0
h    7.0
i    8.0
j    8.0
dtype: float64

我要的是

a    None
b    None
c       c
d       c
e       e
f       e
g       e
h       f
i       i
j       i
dtype: object

我做了什么

s.rolling(3).apply(np.argmax)

a    NaN
b    NaN
c    2.0
d    1.0
e    2.0
f    1.0
g    0.0
h    0.0
i    2.0
j    1.0
dtype: float64

这显然不是我想要的

没有简单的方法可以做到这一点,因为传递给滚动应用函数的参数是一个普通的 numpy 数组,而不是 pandas 系列,所以它不知道索引.此外,滚动函数必须 return 一个浮点数结果,因此如果它们不是浮点数,它们不能直接 return 索引值。

这是一种方法:

>>> s.index[s.rolling(3).apply(np.argmax)[2:].astype(int)+np.arange(len(s)-2)]
Index([u'c', u'c', u'e', u'e', u'e', u'f', u'i', u'i'], dtype='object')

我们的想法是获取 argmax 值并通过添加一个值来指示我们在系列中的距离,使它们与系列对齐。 (也就是说,对于第一个 argmax 值,我们添加零,因为它为我们提供了从原始序列中的索引 0 开始的子序列的索引;对于第二个 argmax 值,我们添加了一个,因为它为我们提供了一个子序列的索引原始系列中从索引 1 开始的子序列;等等)

这给出了正确的结果,但不包括开头的两个 "None" 值;如果需要,您必须手动将它们添加回去。

an open pandas issue添加滚动idxmax。

这是一种使用 broadcasting -

的方法
maxidx = (s.values[np.arange(s.size-3+1)[:,None] + np.arange(3)]).argmax(1)
out = s.index[maxidx+np.arange(maxidx.size)]

这会生成与滚动 windows 对应的所有索引,将这些索引放入提取的数组版本中,从而获得每个 window 的最大索引。为了更有效的索引,我们可以使用 NumPy strides,像这样 -

arr = s.values
n = arr.strides[0]
maxidx = np.lib.stride_tricks.as_strided(arr, \
                   shape=(s.size-3+1,3), strides=(n,n)).argmax(1)

我用了发电机

def idxmax(s, w):
    i = 0
    while i + w <= len(s):
        yield(s.iloc[i:i+w].idxmax())
        i += 1

pd.Series(idxmax(s, 3), s.index[2:])

c    c
d    c
e    e
f    e
g    e
h    f
i    i
j    i
dtype: object

您还可以通过创建 DataFrame 并使用 idxmax 来模拟滚动 window,如下所示:

window_values = pd.DataFrame({0: s, 1: s.shift(), 2: s.shift(2)})
s.index[np.arange(len(s)) - window_values.idxmax(1)]

Index(['a', 'b', 'c', 'c', 'e', 'e', 'e', 'f', 'i', 'i'], dtype='object', name=0)

如您所见,前两项是应用于长度 1 和 2 的初始 windows 而不是空值的 idxmax。 它不如公认的答案有效,并且对于大型 windows 来说可能不是一个好主意,但这只是另一个角度。

顺便说说我是如何解决我遇到的类似问题的。我不想准确地找到索引,我想知道最大值发生在多长时间前。但这也可用于查找索引。

我基本上使用的是轮班策略,但我正在迭代几个具有可配置长度的班次。它可能很慢,但对我来说已经足够好了。

import pandas as pd


length = 5

data = [1, 2, 3, 4, 5, 4, 3, 4, 5, 6, 7, 6, 5, 4, 5, 4, 3]
df = pd.DataFrame(data, columns=['number'])
df['helper_max'] = df.rolling(length).max()

for i in range(length, -1, -1):
    # Set the column to what you want. You may grab the index 
    # if you wish, I wanted number of rows since max happened
    df.loc[df['number'].shift(i) == df['helper_max'], 'n_rows_ago_since_max'] = i

print(df)

输出:

    number  helper_max  n_rows_ago_since_max
0        1         NaN                   NaN
1        2         NaN                   NaN
2        3         NaN                   NaN
3        4         NaN                   NaN
4        5         5.0                   0.0
5        4         5.0                   1.0
6        3         5.0                   2.0
7        4         5.0                   3.0
8        5         5.0                   0.0
9        6         6.0                   0.0
10       7         7.0                   0.0
11       6         7.0                   1.0
12       5         7.0                   2.0
13       4         7.0                   3.0
14       5         7.0                   4.0
15       4         6.0                   4.0
16       3         5.0                   2.0

我认为这是最简单的方法,只需使用下面的 lambda 即可:

rolling_max_index=df.rolling(period).apply(lambda x: x.idxmax())