首页 > 解决方案 > 将熊猫数据框过滤为仅在两个日期之间处于活动状态的记录的 Pythonic 方法

问题描述

这是我反复遇到的事情,我想知道是否有比我正在做的更简单的方法来做到这一点。为了说明,这是一个人为的例子。

我有一个熊猫数据框。这是一些机器何时启动和关闭的日志:

| MachineID | StartDate  | EndDate    |
|-----------|------------|------------|
| 1         | 2020-01-01 | 2020-06-01 |
| 2         | 2020-01-01 | 2020-02-01 |
| 3         | 2020-03-01 | 2020-07-15 |
| 4         | 2020-04-01 | 2020-05-01 |
| 5         | 2020-04-01 | 2020-07-15 |
| 6         | 2020-05-01 |            |
| 7         | 2020-07-01 |            |

EndDate 列中的空值表示机器尚未关闭。日期可以是任何一天,为了简单起见,我只使用了第一个。

现在说我想将数据框过滤到仅在两个日期之间的任何时间处于活动状态的机器,在这种情况下,假设活动期间的开始日期是 2020-04-01,结束日期是 2020-06-30 .

                     start                    end
                   2020-04-01             2020-06-30
                       |                       |
Machine 1  xxxxxxxxxxxxxxxxxxxxxxxx
Machine 2  xxx
Machine 3         xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx      
Machine 4              xxxxxxxxxxxxxxxxxx
Machine 5              xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
Machine 6                               xxxxxxxxxxxxxxxxxxx...(still going)
Machine 7                                        xxxxxxxxxx...(still going)
                       |                       |

如果我只使用(df["StartDate"]>=start) & (df["EndDate"]<=end)我会过滤掉我绝对不想要的机器 3 和 6,因为它们在此期间处于活动状态。我刚才正在做的是构建一个非常冗长的过滤器,如下所示:

start = "2020-04-01"
end = "2020-06-30"

dff = df[
    # Machines that started before the start date
    ((df["StartDate"]<=start) & ((df["EndDate"]>=start) & (df["End date"] <= end))) | \ 
    ((df["StartDate"]<=start) & (df["EndDate"]>=end)) | \  
    ((df["StartDate"]<=start) & (df["EndDate"].isnull())) | \ 

    # Machines that started after the start date
    ((df["StartDate"]>=start) & ((df["EndDate"]>=start) & (df["End date"] <= end))) | \
    ((df["StartDate"]>=start) & (df["EndDate"]>=end)) | \
    ((df["StartDate"]>=start) & (df["EndDate"].isnull()))
              ]

这似乎可以完成工作,但必须有更好的方法。如果有人关心可以提供替代方案,我已经完全准备好面对一些明显的事情(我对此很陌生)?

标签: pythonpandasdatefilter

解决方案


我学到的一个技巧是翻转比较:比较一个范围的开始与另一个范围的结束,反之亦然:

start = pd.Timestamp('2020-04-01')
end = pd.Timestamp('2020-06-30')

cond = (df['StartDate'] < end) & (df['EndDate'].fillna(pd.Timestamp('2099-01-01')) > start)
dff = df[cond]

如果你反复使用这个,把它变成一个函数。


推荐阅读