python - 有效计算可能发生不同情况的两个系列之间的时间增量
问题描述
我正在使用包含到达和离开数据的大约 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 行:(Depart Before Scheduled Case)实际出发时间比预定时间早几分钟
我遇到的问题是,由于实际出发列中没有给出日期,因此确定案例 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
解决方案
如果我正确理解你的问题,你需要设置一个你期望延迟的时间增量范围(负/太早出发以及正/晚出发)。您可以使用它来确定是否应将一天添加到“实际出发”列(如您的示例中的第 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
推荐阅读
- c++ - 前向申报可以自动化吗?
- javascript - XPages View onClick 模态对话框显示
- python - 从文件末尾删除第一个逗号
- python - 密码检查器尝试 2
- flutter - 如何将这些 FlatButton 转换为 Switch
- javascript - 根据输入字段对数据列表项进行排序
- c# - 寻找多个扩展名和多个字符串格式的文件夹
- bash - 读取bash中命令后显示的表的方法是什么
- google-cloud-platform - Dialogflow 内联编辑器功能异常:Webhook 调用失败。错误:未知
- javascript - 将字符串从 html 表单返回到烧瓶