如何选取DataFrame中某列值距离特定值一定范围内的所有行?
这里有一个示例数据框(DataFrame),我将用它来更好地说明我的问题:
import pandas as pd
df = pd.DataFrame(pd.np.random.rand(30, 3), columns=tuple('ABC'))
df['event'] = pd.np.nan
df.loc[10, 'event'] = 'ping'
df.loc[20, 'event'] = 'ping'
df.loc[19, 'event'] = 'pong'
我需要创建一个窗口,窗口的大小是 n 行,窗口的中心是每次出现的 ping
。
换句话说,假设 i
是包含 ping
的那一行的索引。在每个 i
的情况下,我想选择 df.ix[i-n:i+n]
。
因此,对于 n=3
,我希望得到以下结果:
A B C event
7 0.8295863 0.2162861 0.4856461 NaN
8 0.156646 0.4730667 0.9968878 NaN
9 0.6709413 0.4796197 0.8747416 NaN
10 0.09942329 0.154008 0.5761598 ping
11 0.7168143 0.678207 0.7281105 NaN
12 0.8915475 0.8013187 0.9049722 NaN
13 0.9545411 0.4844835 0.1645746 NaN
17 0.9909208 0.1091025 0.6582635 NaN
18 0.2536326 0.4324749 0.8001643 NaN
19 0.4734659 0.5582809 0.1221296 pong
20 0.7230407 0.6695843 0.3902591 ping
21 0.3624909 0.2685049 0.5484445 NaN
22 0.05626284 0.6113877 0.9131929 NaN
23 0.8312294 0.5694373 0.4325798 NaN
[14 rows x 4 columns]
有几点需要注意:
- 我希望找到一个非迭代的解决方案。
- 请注意,有一个
pong
的值,但我们不想以它为中心来创建窗口。不过,它在围绕第二个ping
的结果中被包含了。
这该如何实现呢?
3 个回答
1
可能是:
>>> ts, n = df['event'] == 'ping', 3
>>> idx = ts.shift(n).fillna(False) # +n rows
>>> for j in range(-n, n): # -n to n-1 rows
... idx |= ts.shift(j).fillna(False)
...
>>> df[idx]
1
一种实现方法是使用嵌套的 np.where 语句。虽然代码看起来不太优雅,但能完成任务。
ping = pd.Series(np.where(df.event == 'ping', True,
np.where(df.event.shift(1) == 'ping', True,
np.where(df.event.shift(-1) == 'ping', True, False))), index=df.index)
df[ping]
有没有人能帮我把 i=1 的情况推广到一般情况?
补充:其实它们不需要嵌套。这样就可以了:
ping = pd.Series(np.where((df.event == 'ping') | (df.event.shift(1) == 'ping') |
(df.event.shift(-1) == 'ping'), True, False), index=df.index)
6
In [17]: n = 3
选择一个索引范围,比如目标索引上下各加3(要注意不要超过数据框的最大或最小范围)。把这些索引连接在一起,然后去掉重复的。
In [18]: indexers = np.unique(np.concatenate([ np.arange(max(i-n,0),min(i+n,len(df))) for i in df[df.event=='ping'].index ]))
In [19]: indexers
Out[19]: array([ 7, 8, 9, 10, 11, 12, 17, 18, 19, 20, 21, 22])
选择它们。
In [20]: df.iloc[indexers]
Out[20]:
A B C event
7 0.03348742 0.05735324 0.1220022 NaN
8 0.9567363 0.6539097 0.8409577 NaN
9 0.3115902 0.4955503 0.1749197 NaN
10 0.6883777 0.6185107 0.7933182 ping
11 0.5185129 0.6533616 0.1569159 NaN
12 0.1196976 0.9638604 0.7318006 NaN
17 0.02897615 0.1224485 0.5706852 NaN
18 0.02409971 0.4715463 0.4587161 NaN
19 0.9070592 0.3371241 0.9543977 pong
20 0.8533369 0.7549413 0.5334882 ping
21 0.9546738 0.8203931 0.8543028 NaN
22 0.05691086 0.2402766 0.3922318 NaN
注意,你可能需要先执行 df.reset_index()
(在选择之前,这样可以得到实际的行索引位置,而不是一个值)。
这里有个小问题,就是设置 'event' 列时会把所有内容都变成对象类型,具体可以查看 这里。你可以通过使用 df.convert_objects()
来缓解这个问题。