有效地检查值是否存在于任何给定范围内



>我有两个熊猫数据帧对象:

  • A包含'start'列和'finish'

  • B有列'date'

目标是有效地创建一个布尔掩码,指示date是否在[start, finish]区间内

天真的迭代花费太多时间,我想有一种方法可以更快地做到这一点

更新: AB具有不同的行数

UPDATE2:样本:

A
    | start     | finish    |
    |-------    |--------   |
    | 1         | 3         |
    | 50        | 83        |
    | 30        | 42        |
B
    | date      | 
    |-------    |
    | 31        | 
    | 20        | 
    | 2.5       |
    | 84        |
    | 1000      |
Output:
            | in_interval | 
            |-------    |
            | True      | 
            | False     | 
            | True      |
            | False     |
            | False     |

附言我有日期时间格式的数据,但我想解决方案与数字解决方案没有区别

你可以用O(n)的复杂度来做到这一点。这个想法是转换表示。在 A 中,每个间隔存储一行。我建议使用一个数据帧,它为每个转换存储一行(即输入一个间隔,留下一个间隔)。

A = pd.DataFrame(
    data={
        'start': [1, 50, 30],
        'finish': [3, 83, 42]    
    }
)
starts = pd.DataFrame(data={'start': 1}, index=A.start.tolist())
finishs = pd.DataFrame(data={'finish': -1}, index=A.finish.tolist())
transitions = pd.merge(starts, finishs, how='outer', left_index=True, right_index=True).fillna(0)
transitions
    start  finish
1       1       0
3       0      -1
30      1       0
42      0      -1
50      1       0
83      0      -1

此数据帧按日期存储转换的类型。现在,我们需要知道在每个日期我们是否处于间隔中。它看起来像计算开始和结束括号。你可以做:

transitions['transition'] = (transitions.pop('finish') + transitions.pop('start')).cumsum()
transitions
    transition
1            1
3            0
30           1
42           0
50           1
83           0

这里说:

  • 在 1,我在一个间隔
  • 3岁,我不是
  • 通常,如果该值严格大于 0,则它在区间内。
  • 请注意,这将处理重叠间隔

现在,您可以与 B 数据帧合并:

B = pd.DataFrame(
    index=[31, 20, 2.5, 84, 1000]
)
pd.merge(transitions, B, how='outer', left_index=True, right_index=True).fillna(method='ffill').loc[B.index].astype(bool)
       transition
31.0         True
20.0        False
2.5          True
84.0        False
1000.0      False

IIUC 如果日期至少有一个间隔,您希望输出True

apply(lambda)对您来说足够高效吗?(对于大型数据帧来说,当它迭代B行时,它可能有点长)。如果是,您可以尝试以下操作:

def in_range(date,start,finish):
    return (True in ((start < date) & (date < finish)).unique())
B.date.apply(lambda x: in_range(x,A.start,A.finish))

输出:

0     True
1    False
2     True
3    False
4    False

编辑:MaxU的答案实际上效果更好。以下是 10000 行数据帧(A 和 B)的计时器:

%timeit B2.date.apply(lambda x: in_range(x,A2.start,A2.finish))
1 loop, best of 3: 9.82 s per loop
%timeit B2.date.apply(lambda x: ((x >= A2.start) & (x <= A2.finish)).any())
1 loop, best of 3: 7.31 s per loop

相关内容

  • 没有找到相关文章

最新更新