【问题标题】:How can I select all DataFrame rows that are within a certain distance of a given value in a specific column?如何选择特定列中给定值一定距离内的所有 DataFrame 行?
【发布时间】:2014-08-30 06:26:45
【问题描述】:

这是一个示例 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 成为event 列中包含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]

一些注意事项:

  1. 我正在寻找非迭代解决方案。
  2. 请注意,有一个pong 值,我们希望将窗口居中。但是,它是在以第二个 ping 为中心的结果中捕获的。

如何做到这一点?

【问题讨论】:

  • 1.对数据框进行排序; 2.获取ping出现的行数; 3. 围绕行获取行。这有意义吗?
  • @RJT,您能详细说明一下吗?我不明白这与我的问题有什么关系。
  • 抱歉,我太快按 Enter 键了。我的评论旨在询问这些步骤对您是否有意义,并澄清您的问题究竟出在哪里。
  • 刚刚发布了一个答案让我知道窗口和结果范围是否是您想要的,如果您想包含/排除值,可以轻松修改
  • @RJT,没问题!我很抱歉,但我仍然没有看到光明。为什么要排序才能得到ping的行号?我不能只做df[df.event == 'ping'].index 吗?另外,我应该按什么排序?您介意提交一个带有工作示例的正确答案吗?

标签: python pandas indexing vectorization


【解决方案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】:

    也许:

    >>> 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]
    

    【讨论】:

    • 这似乎没有给出正确的输出。我在结果系列的开头得到了四个 Falses,最后得到了 3 个,其余的都是 True
    • @blz 你是对的;我有一个错误,并修复了它
    • 我非常喜欢这个解决方案。我暂时保留这个问题,但这似乎是一个不错的候选人。谢谢!
    【解决方案3】:
    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()来缓解。

    【讨论】:

    • 非常感谢您的回答!这太美了!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-08
    • 1970-01-01
    • 2020-01-08
    • 2023-03-21
    • 2013-01-02
    • 2021-10-03
    相关资源
    最近更新 更多