熊猫:非重叠日期时间算法

3
我有一个pandas数据框(见下面的输入),其中包含多列,重要的列是start_datetime和end_datetime。我想要应用的算法如下所示。我将每一行与其下一行进行比较。如果end_datetime(当前行)超过了start_datetime(下一行),我想将该行移动到末尾。然后将当前行与之前的第二个下一行进行比较。我希望重复此过程,直到我有一系列时间不重叠的行块。由于将有问题的行移动到末尾,日期时间中将出现一个跳跃,这将作为一个新的不重叠块开始。我有一个带有输入、步骤1、步骤2和输出的示例数据集。
我尝试使用这个for循环来解决问题,但我无法以完成任务的方式进行调整。索引让我感到困惑。也许有一个内置的pandas解决方案可以解决这个问题吗?
for i in range(0, df_temp.shape[0] - 1):
    if df_temp.iloc[i+1].start_datetime < df_temp.iloc[i].end_datetime:
        df_temp = df_temp.reindex([index for index in df_temp.index if index != i + 1] + [i + 1], axis=0)

df_temp.to_csv("output.csv", header=True, index=False)
start_datetime          end_datetime
2023-01-01 00:55:13.000 2023-01-01 01:17:56.000
2023-01-01 01:29:57.000 2023-01-01 01:30:24.000
2023-01-01 01:30:55.000 2023-01-01 01:31:33.000
2023-01-01 01:32:05.000 2023-01-01 01:32:44.000
2023-01-01 01:33:59.000 2023-01-01 02:13:24.000
2023-01-01 04:59:59.000 2023-01-01 05:15:48.000
2023-01-01 05:54:04.000 2023-01-01 06:15:53.000
2023-01-01 06:05:56.000 2023-01-01 06:06:02.000 <-- will be shifted to end
2023-01-01 06:06:58.000 2023-01-01 06:42:34.000
2023-01-01 06:08:39.000 2023-01-01 06:31:53.000
2023-01-01 06:16:16.000 2023-01-01 06:39:13.000
2023-01-01 06:30:36.000 2023-01-01 06:38:15.000
2023-01-01 06:32:48.000 2023-01-01 06:49:34.000
2023-01-01 06:35:17.000 2023-01-01 07:02:44.000
2023-01-01 06:59:56.000 2023-01-01 07:47:47.000
2023-01-01 07:17:07.000 2023-01-01 07:20:11.000
2023-01-01 07:22:04.000 2023-01-01 07:57:34.000
2023-01-01 07:24:07.000 2023-01-01 07:54:18.000
2023-01-01 07:36:41.000 2023-01-01 08:07:18.000
2023-01-01 09:24:12.000 2023-01-01 09:46:57.000
2023-01-01 09:26:30.000 2023-01-01 09:58:42.000
2023-01-01 09:31:27.000 2023-01-01 10:00:19.000

步骤1

start_datetime          end_datetime
2023-01-01 00:55:13.000 2023-01-01 01:17:56.000
2023-01-01 01:29:57.000 2023-01-01 01:30:24.000
2023-01-01 01:30:55.000 2023-01-01 01:31:33.000
2023-01-01 01:32:05.000 2023-01-01 01:32:44.000
2023-01-01 01:33:59.000 2023-01-01 02:13:24.000
2023-01-01 04:59:59.000 2023-01-01 05:15:48.000
2023-01-01 05:54:04.000 2023-01-01 06:15:53.000
2023-01-01 06:06:58.000 2023-01-01 06:42:34.000 <-- will be shifted to end
2023-01-01 06:08:39.000 2023-01-01 06:31:53.000
2023-01-01 06:16:16.000 2023-01-01 06:39:13.000
2023-01-01 06:30:36.000 2023-01-01 06:38:15.000
2023-01-01 06:32:48.000 2023-01-01 06:49:34.000
2023-01-01 06:35:17.000 2023-01-01 07:02:44.000
2023-01-01 06:59:56.000 2023-01-01 07:47:47.000
2023-01-01 07:17:07.000 2023-01-01 07:20:11.000
2023-01-01 07:22:04.000 2023-01-01 07:57:34.000
2023-01-01 07:24:07.000 2023-01-01 07:54:18.000
2023-01-01 07:36:41.000 2023-01-01 08:07:18.000
2023-01-01 09:24:12.000 2023-01-01 09:46:57.000
2023-01-01 09:26:30.000 2023-01-01 09:58:42.000
2023-01-01 09:31:27.000 2023-01-01 10:00:19.000
2023-01-01 06:05:56.000 2023-01-01 06:06:02.000

第二步

start_datetime          end_datetime
2023-01-01 00:55:13.000 2023-01-01 01:17:56.000
2023-01-01 01:29:57.000 2023-01-01 01:30:24.000
2023-01-01 01:30:55.000 2023-01-01 01:31:33.000
2023-01-01 01:32:05.000 2023-01-01 01:32:44.000
2023-01-01 01:33:59.000 2023-01-01 02:13:24.000
2023-01-01 04:59:59.000 2023-01-01 05:15:48.000
2023-01-01 05:54:04.000 2023-01-01 06:15:53.000
2023-01-01 06:08:39.000 2023-01-01 06:31:53.000 <-- will be shifted to end
2023-01-01 06:16:16.000 2023-01-01 06:39:13.000
2023-01-01 06:30:36.000 2023-01-01 06:38:15.000
2023-01-01 06:32:48.000 2023-01-01 06:49:34.000
2023-01-01 06:35:17.000 2023-01-01 07:02:44.000
2023-01-01 06:59:56.000 2023-01-01 07:47:47.000
2023-01-01 07:17:07.000 2023-01-01 07:20:11.000
2023-01-01 07:22:04.000 2023-01-01 07:57:34.000
2023-01-01 07:24:07.000 2023-01-01 07:54:18.000
2023-01-01 07:36:41.000 2023-01-01 08:07:18.000
2023-01-01 09:24:12.000 2023-01-01 09:46:57.000
2023-01-01 09:26:30.000 2023-01-01 09:58:42.000
2023-01-01 09:31:27.000 2023-01-01 10:00:19.000
2023-01-01 06:05:56.000 2023-01-01 06:06:02.000
2023-01-01 06:06:58.000 2023-01-01 06:42:34.000

输出
start_datetime          end_datetime
2023-01-01 00:55:13.000 2023-01-01 01:17:56.000 <-- Start of first sequence
2023-01-01 01:29:57.000 2023-01-01 01:30:24.000
2023-01-01 01:30:55.000 2023-01-01 01:31:33.000
2023-01-01 01:32:05.000 2023-01-01 01:32:44.000
2023-01-01 01:33:59.000 2023-01-01 02:13:24.000
2023-01-01 04:59:59.000 2023-01-01 05:15:48.000
2023-01-01 05:54:04.000 2023-01-01 06:15:53.000
2023-01-01 06:16:16.000 2023-01-01 06:39:13.000
2023-01-01 06:59:56.000 2023-01-01 07:47:47.000
2023-01-01 09:24:12.000 2023-01-01 09:46:57.000 <-- End of first sequence without overlap
2023-01-01 06:05:56.000 2023-01-01 06:06:02.000 <-- Start of second sequence
2023-01-01 06:08:39.000 2023-01-01 06:31:53.000
2023-01-01 06:32:48.000 2023-01-01 06:49:34.000
2023-01-01 07:17:07.000 2023-01-01 07:20:11.000
2023-01-01 07:22:04.000 2023-01-01 07:57:34.000
2023-01-01 09:26:30.000 2023-01-01 09:58:42.000 <-- End of second sequence
2023-01-01 06:06:58.000 2023-01-01 06:42:34.000 <-- Start of third sequence
2023-01-01 07:24:07.000 2023-01-01 07:54:18.000
2023-01-01 09:31:27.000 2023-01-01 10:00:19.000 <-- End of third sequence
2023-01-01 06:30:36.000 2023-01-01 06:38:15.000 <-- Start of fourth sequence
2023-01-01 06:35:17.000 2023-01-01 07:02:44.000
2023-01-01 07:36:41.000 2023-01-01 08:07:18.000 <-- End of fourth sequence
2个回答

0
你可能听说过“避免循环!更倾向于向量化”的建议。
Pandas有很多函数,很难知道哪个适用于特定的业务问题。在这里,你需要使用shift
合成一个包含移动时间戳的新列:df_temp["shifted"] = df_temp.start_datetime.shift(-1)。 你可能会发现使用.dropna()很方便。
现在,你可以通过独立地检查每一行来实现你概述的算法。 合成另一个时间戳列,并使用来安排某些行的重新排序。当你按照时间戳sort时,你将会将某些行推向末尾,就像你想要的那样。
有一些算法细节,你的英文散文和令人惊讶的是你的代码都没有澄清。
建议你编写一个以yield结尾的辅助函数。
def rearrange_intervals(input_rows):
    buffer = []  # (or whatever you need, to remember pending rows)
    for i, row in enumerate(input_rows):
        ...  # evaluate current and nearby rows
        yield dict(start_datetime= ... ,
                   end_datetime= ... )

有了那个生成器,使用df = pd.DataFrame(rearrange_intervals(input_rows))来收集结果就很简单了。
我建议采用这种方法,这样你就可以随意插入/删除/重新排列行,而不必担心昂贵的数据框切片/复制问题。
你提到了“第三个序列”和“第四个序列”等细节。考虑创建一个整数列来记录这些数字,并且可以参与多列排序。

嗨J_H,新列的问题是有时我需要将当前行与多个后续行进行比较。我有一个额外的列,但它并没有帮助我解决这个问题。 - undefined

0
注意:我知道算法的实现可以更高效。但我只需要完成工作:D。
我用以下方法解决了这个问题。首先,创建一个函数来删除所有重叠的部分。如果删除了一行,使得与下一行的比较仍然可能,就重复这个步骤。我引入了一个索引,然后将数据分成一个有序但可能重叠的部分和一个有序但可能重叠的第二部分。
import uuid
import pandas


def order_data(df):
    df_temp = df.copy()
    for index in range(0, df_temp.shape[0] - 1):
        while True:
            try:
                if df_temp.iloc[index+1].start_datetime< df_temp.iloc[index].end_datetime:
                    df_temp = df_temp.drop(index=(index + 1))
                    df_temp = df_temp.reset_index(drop=True)
                else:
                    break
            except IndexError:
                break
    return df_temp, df.copy()[~df.index_ref.isin(df_temp.index_ref.values)].drop(columns=["index_ref"]).reset_index(names='index_ref', drop=False)

然后我定义了一个循环来收集所有的数据框。我还引入了一个uuid列来标记非重叠行的块。
dfs: list[pandas.DataFrame] = []

while True:
    df_temp, df_removed = order_data(df)
    dfs.append(df_temp.assign(uuid=str(uuid.uuid4())))
    if df_removed.shape[0] != 0:
        df = df_removed.copy()
    else:
        break

pandas.concat(dfs).to_csv("ordered.csv", header=True, index=False)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接