Python,Pandas:如何根据开始时间和结束时间合并数据帧的行

Python,Pandas:如何根据开始时间和结束时间合并数据帧的行,python,pandas,Python,Pandas,所以我有一个这样的数据帧: Start Time End Time IDs 15:02:13 15:10:24 BAMB30 19:46:19 19:46:29 BHI110 19:47:01 19:57:04 BHI110 19:47:01 19:56:58 BHI110 19:47:01 19:56:59 BHI110 12:01:46 12:06:30 A

所以我有一个这样的数据帧:

Start Time  End Time        IDs
15:02:13    15:10:24        BAMB30
19:46:19    19:46:29        BHI110
19:47:01    19:57:04        BHI110
19:47:01    19:56:58        BHI110
19:47:01    19:56:59        BHI110
12:01:46    12:06:30        AKB286
Start Time  End Time             IDs             Event
    15:02:13    15:10:24        BAMB30      
    19:46:19    19:46:29        BHI110
    19:47:01    19:57:04        BHI110           1
    19:47:01    19:56:58        BHI110           1
    19:47:01    19:56:59        BHI110           1
    12:01:46    12:06:30        AKB286
我想将行或(行中的ID)分组为一个名为“事件”的东西,其定义如下:

  • 事件中的开始时差应为120)或((et-tDlt.end).total_seconds()>120)或(sid==tDlt.id)或(c1!=tDlt.c1)或(c2!=tDlt.c2)): tDlt.start,tDlt.end,tDlt.id,tDlt.c1,tDlt.c2=st,et,sid,c1,c2 tDlt.ev+=1 返回tDlt.ev def gen_事件(): 全球测向 全球规模限制 x=列表(df[‘开始时间(HHMM)’)) y=列表(df[‘结束时间(HHMM)’)) 对于范围内的i(len(x)): x[i]=str(x[i]) 对于范围内的i(len(y)): y[i]=str(y[i]) df['开始时间(HHMM)]=x df['结束时间(HHMM)]=y df['Start Time(HHMM)]=df['Start Time(HHMM)')。应用(pd.Timedelta) df['End Time(HHMM)]=df['End Time(HHMM)].apply(pd.Timedelta) tDlt.start=None ev=df.sort_值(['开始时间(HHMM)'结束时间(HHMM)'和'ID'])。应用(tDlt,轴=1) ev=ev.groupby(ev.transform)(lambda-grp:str(grp.iloc[0]),如果grp.size>sizeLimit-else“”) df['Event']=ev[ev!='').groupby(ev,sort=False)。ngroup()+1 df.Event.replace(np.nan',inplace=True) df=df.dropna(how='all') fs=filedialog.asksaveasfilename(文件类型=[(“Excel文件”、“.xlsx.xls.xlsm”)、(“CSV文件”、“.CSV”)) 如果((str(fs))[-3:][=“csv”): df.to_csv(fs) 其他: df.to_excel(fs) 完成() 注意:我需要通过不同id对事件进行分组(不相同),这就是为什么我将
    (sid==tDlt.id)
    放在代码的else条件中

    但我在某些部分使用以下方法得到了不正确的结果:

    如您所见,我希望单个事件具有不同的ID,但它仍然使用一些相同的ID进行分组。我在代码中哪里出错了

    注意:这些不是我在数据框中拥有的唯一列。但是,在查找事件时,只有上述列才起作用。
    谢谢大家!

    为了给出一个更有启发性的例子,我举了以下例子 源数据帧:

       Start Time End Time     IDs  C1  C2
    0    15:02:13 15:10:24  BAMB30  X9  Y9
    1    19:46:19 19:46:29  BHI110  X9  Y9
    2    19:47:01 19:57:04  BHI110  D2  F2
    3    19:47:01 19:56:58  BHI110  D2  E2
    4    19:47:01 19:56:59  BHI110  D2  E2
    5    20:00:02 20:20:00  BHI110  G3  H3
    6    20:01:03 20:21:16  BHI110  G3  H3
    7    20:15:00 20:23:20  BHI110  X9  Y9
    8    12:01:46 12:06:30  AKB286  A1  B1
    9    12:02:48 12:06:50  AKB286  A1  B1
    10   12:02:50 12:06:55  AKB286  A1  C1
    
    我添加了C1列和C2列(在 根据您的评论,当前组)

    由于开始时间和结束时间列都是字符串类型, 第一步是将它们转换为Timedelta:

    然后,我定义了一个将被视为事件的组的大小限制。 你写了这个限制==5,但是因为你和我的数据 样本只包含较小的组,我将此限制设置为2

    sizeLimit = 2
    
    当然,在真实数据上运行我的代码时,将此限制更改为 你需要什么都行

    然后定义一个函数来检查当前 行和“起始行”并生成“事件编号”:

    由于其内部属性的使用,它是一个“带内存的函数”, 在“开始”和“结束”属性中保留来自 上一行和ev属性-事件编号

    此函数将应用于数据帧的每一行,但在 它的start属性将设置为None,以提供正确的处理 第一排

    请注意,起始行已设置为:

    • 在第一行(当tDlt.start为None时)
    • 在每一行上,从“起始”行开始“时间太远”或 C1或C2与“起始”行不同
    此函数生成连续的“事件编号”:

    • 从0开始,
    • 在任何条件下继续当前组时增加 没有得到满足
    • 对于所有组,即使是低于大小限制的组
    主要处理如下所示:

  • 在tDlt函数的开始属性中设置“初始值”:

     tDlt.start = None
    
  • 对df进行排序并对每行应用tDlt:

     ev = df.sort_values(['Start Time', 'End Time']).apply(tDlt, axis=1)
    
    结果(对于我的数据样本)是:

    当然,由于之前的排序,行顺序不同 应用程序

    检查索引为3、4和2的行。第三排是最早的 从这个群体。第4行在同一组内(满足所有条件)。 但第2行在C2列中有不同的值,所以它会启动一个新的 小组

  • 下一步是取消“太小”组的组号:

    步骤:

    • 取每组(按值)并检查其大小
    • 如果至少有sizeLimit行,则返回原始组 数字,但作为字符串(对于每行)
    • 否则,返回一个空字符串(也适用于每一行)-返回 实际取消
    结果是:

     8     0
     9     0
     10     
     0      
     1      
     3     4
     4     4
     2      
     5     6
     6     6
     7      
     dtype: object
    
  • 现在对新列执行“初始填充”:

     df['Event'] = ev[ev != ''].groupby(ev, sort=False).ngroup() + 1
    
    步骤:

    • 从ev中获取非空元素
    • 将它们(按其值)分组
    • 返回“全局”组编号,从1开始。注意 “初始组号”(到目前为止已计算)在此处更改 进入连续的数字
    但这还不是最终内容(现阶段打印df), 因为:

    • “太短”组的单元格包含NaN
    • 元素为浮动类型
  • 要消除上述缺陷,请运行:

     df.Event.replace(np.nan, '', inplace=True)
    
  • 按两次排序的最终结果为:

       Start Time End Time     IDs  C1  C2 Event
    8    12:01:46 12:06:30  AKB286  A1  B1     1
    9    12:02:48 12:06:50  AKB286  A1  B1     1
    10   12:02:50 12:06:55  AKB286  A1  C1      
    0    15:02:13 15:10:24  BAMB30  X9  Y9      
    1    19:46:19 19:46:29  BHI110  X9  Y9      
    3    19:47:01 19:56:58  BHI110  D2  E2     2
    4    19:47:01 19:56:59  BHI110  D2  E2     2
    2    19:47:01 19:57:04  BHI110  D2  F2      
    5    20:00:02 20:20:00  BHI110  G3  H3     3
    6    20:01:03 20:21:16  BHI110  G3  H3     3
    7    20:15:00 20:23:20  BHI110  X9  Y9      
    
    如你所见:

    • 前两行在时间上足够近,因此是事件1
    • 第三行的开始时间与前一行的距离太远, 因此,它未被纳入上述组
    • 它离下一排也太远了,所以它们不能在一起 在事件中分组
    • 第3行和第4行构成下一组
    • 由于C2中的值不同,不包括第2行
    • 等等
    最有可能的情况是,要检查的两列是否相等可以 其他名称,所以将它们的实际名称放在tDlt中,而不是C1和C2中

    编辑以下关于按ID分组的注释 将功能更改为:

    def tDlt(row):
        id, st, et, c1, c2 = row[['IDs', 'Start Time', 'End Time', 'C1', 'C2']]
        if tDlt.start is None:
            tDlt.id, tDlt.start, tDlt.end, tDlt.ev, tDlt.c1, tDlt.c2 = id, st, et, 0, c1, c2
        else:
            if id != tDlt.id\
                    or ((st - tDlt.start).total_seconds() > 120)\
                    or ((et - tDlt.end).total_seconds() > 120)\
                    or (c1 != tDlt.c1) or (c2 != tDlt.c2):
                tDlt.id, tDlt.start, tDlt.end, tDlt.c1, tDlt.c2 = id, st, et, c1, c2
                tDlt.ev += 1
        return tDlt.ev
    
    tDlt.start=None
    之后,将下一条指令更改为:

    ev = df.sort_values(['IDs', 'Start Time', 'End Time']).apply(tDlt, axis=1)
    
    我想
     df.Event.replace(np.nan, '', inplace=True)
    
       Start Time End Time     IDs  C1  C2 Event
    8    12:01:46 12:06:30  AKB286  A1  B1     1
    9    12:02:48 12:06:50  AKB286  A1  B1     1
    10   12:02:50 12:06:55  AKB286  A1  C1      
    0    15:02:13 15:10:24  BAMB30  X9  Y9      
    1    19:46:19 19:46:29  BHI110  X9  Y9      
    3    19:47:01 19:56:58  BHI110  D2  E2     2
    4    19:47:01 19:56:59  BHI110  D2  E2     2
    2    19:47:01 19:57:04  BHI110  D2  F2      
    5    20:00:02 20:20:00  BHI110  G3  H3     3
    6    20:01:03 20:21:16  BHI110  G3  H3     3
    7    20:15:00 20:23:20  BHI110  X9  Y9      
    
    def tDlt(row):
        id, st, et, c1, c2 = row[['IDs', 'Start Time', 'End Time', 'C1', 'C2']]
        if tDlt.start is None:
            tDlt.id, tDlt.start, tDlt.end, tDlt.ev, tDlt.c1, tDlt.c2 = id, st, et, 0, c1, c2
        else:
            if id != tDlt.id\
                    or ((st - tDlt.start).total_seconds() > 120)\
                    or ((et - tDlt.end).total_seconds() > 120)\
                    or (c1 != tDlt.c1) or (c2 != tDlt.c2):
                tDlt.id, tDlt.start, tDlt.end, tDlt.c1, tDlt.c2 = id, st, et, c1, c2
                tDlt.ev += 1
        return tDlt.ev
    
    ev = df.sort_values(['IDs', 'Start Time', 'End Time']).apply(tDlt, axis=1)
    
    11   20:00:02   20:20:00  XXX110  G3  H3
    12   20:01:03   20:21:16  XXX110  G3  H3
    
       Start Time End Time     IDs  C1  C2 Event
    8    12:01:46 12:06:30  AKB286  A1  B1     1
    9    12:02:48 12:06:50  AKB286  A1  B1     1
    10   12:02:50 12:06:55  AKB286  A1  C1      
    0    15:02:13 15:10:24  BAMB30  X9  Y9      
    1    19:46:19 19:46:29  BHI110  X9  Y9      
    3    19:47:01 19:56:58  BHI110  D2  E2     2
    4    19:47:01 19:56:59  BHI110  D2  E2     2
    2    19:47:01 19:57:04  BHI110  D2  F2      
    5    20:00:02 20:20:00  BHI110  G3  H3     3
    6    20:01:03 20:21:16  BHI110  G3  H3     3
    7    20:15:00 20:23:20  BHI110  X9  Y9      
    11   20:00:02 20:20:00  XXX110  G3  H3     4
    12   20:01:03 20:21:16  XXX110  G3  H3     4