【问题标题】:Efficient calculation of timedeltas between two series where different cases can occur有效计算可能发生不同情况的两个系列之间的时间增量
【发布时间】:2021-03-06 20:24:15
【问题描述】:

我正在使用包含到达和离开数据的大约 100 万行数据集,转换来自 HTML 表的原始数据,并且在计算预定和实际离开之间的时间差时遇到了问题。下表显示了四种不同的时差情况,我需要一种快速计算时间增量的方法,转换为分钟,可以同时考虑所有这些情况。我目前使用的可以正确处理 4 种情况中的 3 种。

数据有两个日期/时间列,格式如下表所示,第三列是使用当前技术计算时间增量的结果(本文后面的代码)。

|          Sch Dp          | Act Dp  |  Diff  |
|--------------------------|---------|--------|
| 02/24/2014 10:22 PM (Mo) |         | NaN    |
| 02/25/2014 10:22 PM (Tu) | 10:24PM | 2.0    |
| 02/26/2014 10:22 PM (We) | 12:53AM | 151.0  |
| 11/02/2010 4:36 AM (Tu)  | 4:13AM  | 1417.0 |

上面说明的四种主要情况:

第 1 行:(缺少数据情况)实际出发列由于取消而缺少数据(在 df 中的其他地方表示)

第 2 行:(正常情况)实际出发时间与预定出发时间相同,准时或晚于预定时间

第 3 行:(Depart Next Day Case)实际出发时间较晚,但出发日期发生变化而没有正式指示

第 4 行:(在预定时间前出发)实际出发时间比预定时间早了几分钟

我遇到的问题是,由于实际出发列中没有给出日期,因此确定案例 3 和 4 的时间差更加复杂。我目前在加载的原始数据上运行以下代码进入数据框,它适用于案例 1-3,但不适用于案例 4。

sch_time =  pd.to_datetime(df['Sch Dp'], format='%I:%M %p', exact=False, errors='coerce')
act_time = pd.to_datetime(df['Act Dp'], format='%I:%M%p', exact=False, errors='coerce')
    
time_diff = pd.to_timedelta(act_time - sch_time,  errors='coerce') 
time_diff = time_diff - pd.to_timedelta(time_diff.dt.days, unit='d')
new_df['Diff'] =(60 * (time_diff.dt.days * 24 + time_diff.dt.seconds // 3600) + (time_diff.dt.seconds % 3600) // 60)

有没有办法以相对简单且计算效率高的方式处理此类事情?我可能会编写一个函数来执行此操作并使用 pd.series.apply(),但根据我在尝试解决此问题时所阅读和体验的内容,.apply() 非常慢并且出于方便而包含在内但不应该成为首选解决方案。由于我的数据框有近 100 万行,我预计 .apply() 不会是最佳的,甚至不会很快。我的猜测是必须有一种方法可以更有效地做到这一点。

(想知道战略数学计算是否有可能,可能是模数或绝对值,但实验产生了错误的结果。)

更新: 由于我还没有收到回复,所以我编写了这个(功能性但不优雅)函数,但我无法弄清楚如何将它与 .apply() 一起使用。它考虑了各个列(我将“Sch Dp”拆分为完整的日期+时间(没有星期几)、仅日期、仅时间,并将所有数据类型转换为适当的格式。

谁能给点建议?

def calc_diff(full_sched, sched_date, sched_time, act_time):
    if pd.isnull(act_time):
        return np.nan
    else:
        if sched_time > pd.to_datetime('12:00:00').time():
            act_datetime = pd.Timestamp.combine(sched_date, act_time)
            if act_datetime < full_sched:
                act_datetime = pd.to_datetime(act_datetime) + pd.Timedelta(1, unit='day')
        else: 
            act_datetime = pd.Timestamp.combine(sched_date, act_time) 
        time_diff = pd.to_timedelta(act_datetime - full_sched) 
        time_diff = time_diff.total_seconds() // 60
    return time_diff

【问题讨论】:

    标签: python pandas dataframe datetime timedelta


    【解决方案1】:

    如果我正确理解您的问题,您需要设置一个您预计延迟的时间增量范围(负/过早出发以及正/晚出发)。您可以使用它来确定是否应将一天添加到“实际出发”列(如您的示例中的第 3 行)或不(如您的示例中的第 4 行)。

    # departure, slice of the day name and to datetime...
    df['dep'] = pd.to_datetime(df['Sch Dp'].str[:-4])
    
    # use date of scheduled departure, and time from actual departure.
    # set specific format and errors=coerce so that the empty string gives NaT.
    df['adep'] = pd.to_datetime(df['dep'].dt.date.astype(str)+ " "+df['Act D'], 
                                format='%Y-%m-%d %I:%M%p', errors='coerce')
    
    # set the expected delay, derive a boolean mask from that.
    max_expected_delay = pd.Timedelta(hours=4)
    delta = df['adep']-df['dep']
    m_late = (delta < max_expected_delay) & (max_expected_delay*-1 > delta)
    m_early = (delta*-1 < max_expected_delay) & (max_expected_delay*-1 > delta*-1)
    
    # add (or remove) a day if actual departure falls within expected range
    df.loc[m_late, 'adep'] += pd.Timedelta(days=1)
    df.loc[m_early, 'adep'] -= pd.Timedelta(days=1)
    
    df['diff[min]'] = (df['adep']-df['dep']).dt.total_seconds()/60
    
    #                      Sch Dp    Act D  ...                adep diff[min]
    # 0  02/24/2014 10:22 PM (Mo)           ...                 NaT       NaN
    # 1  02/25/2014 10:22 PM (Tu)  10:24PM  ... 2014-02-25 22:24:00       2.0
    # 2  02/26/2014 10:22 PM (We)  12:53AM  ... 2014-02-27 00:53:00     151.0
    # 3   11/02/2010 4:36 AM (Tu)   4:13AM  ... 2010-11-02 04:13:00     -23.0
    # 4  11/02/2010 12:13 AM (Tu)  11:56PM  ... 2010-11-01 23:56:00     -17.0
    

    【讨论】:

    • 它几乎可以工作了!当我尝试该代码时,唯一的问题是当实际出发发生在早些时候(例如:计划的 Dp 是上午 12:13 但实际 Dp 是晚上 11:56)时,它给出的结果类似于 1423。我想我直到过滤掉正常的情况才知道这种情况。我正在考虑只删除这些行,因为它只有一百万行中的 75 行。感谢您的帮助!
    • @ECF,对,那个案子不见了。我认为您可以反转检查范围的逻辑,请参阅我的编辑。所有这些痛苦只是因为有人想节省几 MB 并且没有存储 date 和时间 ^^
    • 完美运行!你真的让我头疼不已,我不确定我是否会自己解决所有这些问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-01-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多