如何选择特定列中给定值的特定距离内的所有DataFrame行?

时间:2014-07-09 09:35:22

标签: python pandas indexing vectorization

以下是一个示例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'

我需要创建以ping每次出现为中心的 n 行的窗口。

换句话说,让i成为ping列中包含event的行的索引。对于每个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]

一些警告:

  1. 我正在寻找非迭代解决方案。
  2. 请注意,我们想要将窗口置于中心位置pong。然而,它是以第二个ping为中心的结果捕获的。
  3. 如何实现这一目标?

3 个答案:

答案 0 :(得分: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()(在选择获取实际行索引位置而不是值之前)。

请注意,这里的错误是“事件”的设置。列将所有内容转换为对象,请参阅here。您可以使用df.convert_objects()来缓解。

答案 1 :(得分: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)

答案 2 :(得分: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]