我如何计算滚动 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())
考虑 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())