使用另一个事件数据框屏蔽日期的熊猫数据框

时间:2021-04-22 08:32:45

标签: python pandas

我有一个如下的数据框

df = pd.DataFrame({"date": pd.date_range(start="2012-03-01", end="2012-03-05"),
                       "date+1": pd.date_range(start="2012-03-02", end="2012-03-06"),
                       "date+2": pd.date_range(start="2012-03-03", end="2012-03-07")})

我还有另一个数据框,表示具有开始日期和结束日期的事件,如下所示。

event = pd.DataFrame({"event": ["A", "B"],
                      "start": ["2012-03-02", "2012-03-04"],
                      "end": ["2012-03-03", "2012-03-06"]})
event["start"] = pd.to_datetime(event["start"])
event["end"] = pd.to_datetime(event["end"])

我想创建一个掩码数据帧,如果 df 中的任何日期在事件数据帧中任何事件的开始日期和结束日期之间,则该掩码数据帧返回 True。预期的输出应该是

0,  1,  1
1,  1,  1
1,  1,  1
1,  1,  1
1,  1,  0

这个预期的输出对应于 df

2012-03-01,  2012-03-02,  2012-03-03
2012-03-02,  2012-03-03,  2012-03-04
2012-03-03,  2012-03-04,  2012-03-05
2012-03-04,  2012-03-05,  2012-03-06
2012-03-05,  2012-03-06,  2012-03-07

如您所见,只有 2012-03-01 和 2012-03-07 不在事件数据框中的任何事件之间。循环可能计算成本很高。我可以给您建议如何最大程度地减少循环吗?

2 个答案:

答案 0 :(得分:4)

您可以使用笛卡尔连接,然后检查日期是否在间隔的开始和结束之间,并进行聚合:

# cartesian join
z = (df
    .stack().reset_index().assign(k=1)
    .merge(event.assign(k=1)))

# check if date between start and end
z['mask'] = z[0].between(z['start'], z['end'])

# aggregate
df_m = z.groupby(['level_0', 'level_1'])['mask'].max().unstack().astype(int)
df_m

输出:

level_1  date  date+1  date+2
level_0                      
0           0       1       1
1           1       1       1
2           1       1       1
3           1       1       1
4           1       1       0

附言如果您使用的是较新版本的 k=1 (1.2.0+),则可以直接使用 pandas,而不是在合并之前将 merge(how='cross') 分配给两个帧的技巧

答案 1 :(得分:1)

events 创建一个 interval index

intervals = pd.IntervalIndex.from_tuples([*zip(event.start, event.end)], 
                                         closed = 'both')

IntervalIndex([[2012-03-02, 2012-03-03], [2012-03-04, 2012-03-06]],
              closed='both',
              dtype='interval[datetime64[ns]]')

df 上运行 applymap

df.applymap(lambda df: intervals.contains(df).any()).astype(int)
 
   date  date+1  date+2
0     0       1       1
1     1       1       1
2     1       1       1
3     1       1       1
4     1       1       0