寻求比较和过滤熊猫日期范围内重叠部分的有效方法

时间:2019-10-28 18:59:05

标签: python python-3.x pandas dataframe

我有一个数据框,其中有两列包含日期范围。

原始数据如下:

df1:
personid, visitto, intime, outtime
8, 8, 2017-07-01 06:00:00, 2017-07-01 08:00:00
17 8, 2017-07-02 09:00:00, 2017-07-02 10:00:00

df2:
location, open, close
8, 2017-07-01 04:00:00, 2017-07-01 13:00:00

这是我到目前为止所做的: 1.在visitto-> location

上合并两个数据框
merged_df:
personid, visitto, intime, outtime, location, open, close
8, 8, 2017-07-01 06:00:00, 2017-07-01 08:00:00, 8, 2017-07-01 04:00:00, 2017-07-01 13:00:00
17 8, 2017-07-02 09:00:00, 2017-07-02 10:00:00, 8, 2017-07-01 04:00:00, 2017-07-01 13:00:00
  1. 将带有日期的四列转换为大熊猫间隔
personid, visitto, visittime, opentime
8, 8, [2017-07-01 06:00:00, 2017-07-01 08:00:00], [2017-07-01 04:00:00, 2017-07-01 13:00:00]
17 8, [2017-07-02 09:00:00, 2017-07-02 10:00:00], [2017-07-01 04:00:00, 2017-07-01 13:00:00]

我现在想过滤此合并的间隔数据框,以仅返回日期时间重叠的行。我的预期结果将是:

personid, visitto, visittime, opentime
8, 8, [2017-07-01 06:00:00, 2017-07-01 08:00:00], [2017-07-01 04:00:00, 2017-07-01 13:00:00]

我成功地使用iterrows()并逐行比较了重叠部分,从而获得了理想的结果,但是,这非常冗长。我想做的是这样的(非工作示例):

merged_df.loc[merged_df['visittime'].overlaps(merged_df['opentime'])]

或者,在合并数据帧期间完成此操作。 (不起作用的例子):

merge_df = pd.merge(df1[["personid", "visitto", "intime", "outtime"]], df2[["location", "open", "close"]], how='inner', left_on='visitto', right_on='location') #WHERE intime, outtime OVERLAPS open, close

是否可以通过熊猫功能而不是循环执行此操作?这可以通过数据库中的Sql轻松完成,但是,我正在努力寻找一种有效的方法来对数据框执行此操作。

4 个答案:

答案 0 :(得分:1)

使用内置的熊猫list功能(以及来自的答案),而不是创建您总是要反复遍历的复合对象列(如datetime s)。 here):

from io import StringIO
import pandas as pd

csv_buff = StringIO("""personid,visitto,intime,outtime,location,open,close
8,8,2017-07-01 06:00:00,2017-07-01 08:00:00,8,2017-07-01 04:00:00,2017-07-01 13:00:00
17,8,2017-07-02 09:00:00,2017-07-02 10:00:00, 8,2017-07-01 04:00:00,2017-07-01 13:00:00""")
csv_buff.seek(0)
dtypes = {
    "intime": "datetime64",
    "outtime": "datetime64",
    "open": "datetime64",
    "close": "datetime64"
}
df = pd.read_csv(csv_buff).astype(dtypes)

df[(df["intime"] <= df["close"]) & (df["open"] <= df["outtime"])]

结果:

   personid visitto              intime             outtime  location                open               close
0         8       8 2017-07-01 06:00:00 2017-07-01 08:00:00         8 2017-07-01 04:00:00 2017-07-01 13:00:00

答案 1 :(得分:0)

虽然从根本上来说仍然是一个循环,但这确实可以实现。

PORT 80

答案 2 :(得分:0)

如果可以将false与大熊猫一起使用:

IntervalIndex

答案 3 :(得分:0)

我假设所有“时间”列都是 datetime 类型。

从将两个时间都转换为 Interval 并删除 (现在是多余的)源时间列(在两个DataFrame中):

df1['visittime'] = df1.apply(lambda row: pd.Interval(
    row.intime, row.outtime, closed='both'), axis=1)
df1.drop(columns=['intime', 'outtime'], inplace=True)
df2['opentime'] = df2.apply(lambda row: pd.Interval(
    row.open, row.close, closed='both'), axis=1)
df2.drop(columns=['open', 'close'], inplace=True)

注意:如果由于某种原因需要“原始”时间列, 不要丢下它们。

然后合并它们并过滤掉“错误”的行:

df3 = df1.merge(df2, left_on='visitto', right_on='location')
df3 = df3[df3.apply(lambda row: row.visittime.overlaps(row.opentime), axis=1)]

没有循环或行迭代,只需调用 Pandas 方法。