>我有两个熊猫数据帧对象:
-
A
包含'start'
列和'finish'
列 -
B
有列'date'
目标是有效地创建一个布尔掩码,指示date
是否在[start, finish]
区间内
天真的迭代花费太多时间,我想有一种方法可以更快地做到这一点
更新: A
和B
具有不同的行数
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