查找并发 运行 个事务的总 "wait" 时间

Finding total "wait" time for concurrently running transactions

我需要评估制造执行系统的数百万行性能日志记录。我需要按日期、class 和名称对数据进行分组,并找到大量并发 运行 交易的总“等待时间”。数据看起来类似于此数据框中的内容:

    import pandas as pd

    d = {'START_DATE': ['2021-08-07 19:11:40', '2021-08-07 19:11:40', '2021-08-07 19:11:40',
                   '2021-08-07 19:20:40', '2021-08-07 19:20:40', '2021-08-07 19:20:40',
                   '2021-08-07 19:21:40', '2021-08-07 19:21:40', '2021-08-07 19:21:40',
                   '2021-08-10 19:20:40', '2021-08-10 19:20:40', '2021-08-10 19:20:40',
                   '2021-08-10 19:21:40', '2021-08-10 19:21:40', '2021-08-10 19:21:40'
                   ],
    
        'ELAPSED_TIME': ['00:00:00.465', '00:00:01.000', '00:00:00.165',
                         '00:00:00.100', '00:00:00.200', '00:03:00.000',
                         '00:05:00.000', '00:00:00.200', '00:00:03.000',
                         '00:00:00.100', '00:00:00.200', '00:03:00.000',
                         '00:05:00.000', '00:00:00.200', '00:00:03.000'
                         ],
    
        'TRANSACTION': ['a', 'b', 'c',
                        'a', 'd', 'c',
                        'e', 'a', 'b',
                        'a', 'd', 'c',
                        'e', 'a', 'b'
                        ],
    
        'USER': ['Bob', 'Bob', 'Bob',
                 'Biff', 'Biff', 'Biff',
                 'Biff', 'Biff', 'Biff',
                 'Bob', 'Bob', 'Bob',
                 'Bob', 'Bob', 'Bob'
                 ],
    
        'CLASS':  ['AA', 'AA', 'AA',
                   'BB', 'BB', 'BB',
                   'BB', 'BB', 'BB',
                   'AA', 'AA', 'AA',
                   'AA', 'AA', 'AA'
                   ]}

    df = pd.DataFrame(data=d)

查看交易时间如何同时开始并且 运行 彼此并发,但将在不同时间“完成”。例如。 Bob 的第一组事务(第 0-2 行)都需要不同的时间量,但是当我按 DATE、CLASS 和 USER 分组时——我想显示总等待时间为 1000 毫秒(基于第二行的等待时间)。

08/07/2021,Biff有两组不同时间开始的交易,但它们仍然会重叠到一个等待时间--6000ms。

预期输出类似于:

DATE           CLASS     USER     Wait
2021-08-07     AA        Bob      1000
2021-08-07     BB        Biff     360000
2021-08-10     AA        Bob      360000

就像我提到的实际数据有数百万行交易——我正在寻求帮助以找到更好的东西(希望比我have/found更快):

def getSecs1(grp):
    return pd.DatetimeIndex([]).union_many([ pd.date_range(
        row.START_DATE, row.END_DATE, freq='25ms', closed='left')
            for _, row in grp.iterrows() ]).size

我通过将毫秒添加到 START_DATE 来添加一个 END_DATE 列。我必须用 25 毫秒的块来完成它,否则它会花费太长时间。

任何 help/advice 将不胜感激。

###编辑 将重叠更改为分钟

此解决方案使用一个名为 staircase 的包,该包基于 pandas 和 numpy 构建,用于处理(数学)阶跃函数。您可以将间隔视为一个阶梯函数,它在间隔开始时从值 0 变为 1,在间隔结束时从值 1 变为 0。

附加设置

START_DATEELAPSED_TIME 转换为适当的 pandas 时间对象

df["START_DATE"] = pd.to_datetime(df["START_DATE"])
df["ELAPSED_TIME"] = pd.to_timedelta(df["ELAPSED_TIME"])

定义每日垃圾箱

dates = pd.period_range("2021-08-07", "2021-08-10")

解决方案

定义一个函数,它接受一个数据帧,从开始时间和结束时间(计算为开始 + 持续时间)生成一个阶梯函数,将非零值设置为 1,用 bin 对阶梯函数进行切片,然后积分。

import staircase as sc

def calc_dates_for_user(df_):
    return (
        sc.Stairs(  # creating step function
             start=df_["START_DATE"],
             end=df_["START_DATE"] + df_["ELAPSED_TIME"],
        )
        .make_boolean()  # where two intervals overlap the value of the step function will be 2.  This sets all non-zero values to 1 (effectively creating a union of intervals).
        .slice(dates)  # analogous to groupby
        .integral()/pd.Timedelta("1s")  # for each slice integrate (which will equal the length of the interval) and divide by seconds
    )

当我们按 USERCLASS 分组并应用此函数时,我们得到一个数据帧,由这些变量索引,列索引对应于周期范围内的间隔

USER CLASS   [2021-08-07, 2021-08-08)  [2021-08-08, 2021-08-09)  [2021-08-09, 2021-08-10)    [2021-08-10, 2021-08-11)                                              
Biff BB                      360000.0                        0.0                        0.0                       0.0 
Bob  AA                        1000.0                        0.0                        0.0                  360000.0

我们会像这样清理它

result = (
    df.groupby(["USER", "CLASS"])  
    .apply(calc_dates_for_user)
    .melt(ignore_index=False, var_name="DATE", value_name="WAIT")  # melt column index into a single column of daily intervals
    .query("WAIT != 0")  # filter out days where no time recorded
    .reset_index() # move USER and CLASS from index to columns
)

result 然后看起来像这样

   USER CLASS                      DATE      WAIT
0  Biff    BB  [2021-08-07, 2021-08-08)  360000.0
1   Bob    AA  [2021-08-07, 2021-08-08)    1000.0
2   Bob    AA  [2021-08-10, 2021-08-11)  360000.0

要获得预期结果,您可以将 DATE 列替换为与开始日期相关的时间戳

result["DATE"] = pd.IntervalIndex(result["DATE"]).left