我有两个pandas DataFrame对象:
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 |
P.S。我的数据采用日期时间格式,但我想解决方案与数字
的解决方案没有区别答案 0 :(得分:4)
你可以用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
这里说:
现在您与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
答案 1 :(得分:1)
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的答案实际上更好。以下是10 000行数据帧(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