【问题标题】:Calculate time between events in a day计算一天中事件之间的时间
【发布时间】:2021-11-06 11:27:32
【问题描述】:

我有以下数据集:

date                  event      next_event            duration_Minutes
2021-09-09 22:30:00    1         2021-09-09 23:00:00        30
2021-09-09 23:00:00    2         2021-09-09 23:10:00        10
2021-09-09 23:10:00    1         2021-09-09 23:50:00        40
2021-09-09 23:50:00    4         2021-09-10 00:50:00        60
2021-09-10 00:50:00    4         2021-09-12 00:50:00        2880

主要问题是我想通过以下方式将多日活动拆分为单独的活动。我想将事件持续时间从2021-09-09 23:50:002021-09-10 00: 00: 00,然后将持续时间从2021-09-10 00: 00: 002021-09-10 00:50:00,依此类推。这将很有用,因为之后,我需要按天对事件进行分组并按天计算每个事件的持续时间,所以我想修复这些事件之间存在日期变化的情况。 我想得到这样的东西:

date                  event      next_event            duration_Minutes
2021-09-09 22:30:00    1         2021-09-09 23:00:00        30
2021-09-09 23:00:00    2         2021-09-09 23:10:00        10
2021-09-09 23:10:00    1         2021-09-09 23:50:00        40
2021-09-09 23:50:00    4         2021-09-10 00:00:00        10
2021-09-09 00:00:00    4         2021-09-10 00:50:00        50
2021-09-10 00:50:00    4         2021-09-11 00:00:00        1390
2021-09-11 00:00:00    4         2021-09-12 00:00:00        1440
2021-09-12 00:00:00    4         2021-09-12 00:50:00        50

它应该能够处理我们在示例中一整天或更长时间没有活动的情况。

我目前的解决方案是:

first_record_hour_ts = df.index.floor('H')[0]
last_record_hour_ts = df.index.floor('H')[-1]

# Create a series from the first to the last date containing Nan
df_to_join = pd.Series(np.nan, index=pd.date_range(first_record_hour_ts, last_record_hour_ts, freq='H'))
df_to_join = pd.DataFrame(df_to_join)
# Concatenate with current status dataframe
df = pd.concat([df, df_to_join[~df_to_join.index.isin(df.index)]]).sort_index()
# Forward fill the nana
df.fillna(method='ffill', inplace=True)

df['next_event'] = df.index.shift(-1)
# Calculate the delta between the 2 status
df['duration'] = df['next_event'] - df.index
# Convert into minutes
df['duration_Minutes'] = df['duration_Minutes'].apply(lambda x: x.total_seconds() // 60)

这并不能完全解决问题,但我认为它可以解决我的目标,即最终能够按事件和按天分组。

【问题讨论】:

  • 到目前为止你写了什么代码?
  • 所以现在我的解决方案如下:我只取日期和事件列。我按小时在数据范围内创建了一个熊猫系列。我加入以便我拥有每天所有可能的时间以及以前的时间戳。然后我用前一个值向前填充事件列中的“NaN”值。然后我计算下一个事件和持续时间。通过这种方式,我认为我能够解决多天事件的问题,即使我有更多的行,因为最终我的目标是按天对数据进行分组,所以我认为它应该像这样工作。
  • 我认为如果我们能看到您的实际代码会很有用——您可以编辑您的问题以将其添加到那里。大声思考,我想我想将 date-next_event 间隔跨越或不跨越午夜的情况分开。那些跨越午夜的必须分开处理,因为它们是分开的,可能会跨越数天。
  • 好的,我添加了我现在写的代码
  • 谢谢!我正在研究解决方案,我会告诉你进展如何

标签: python pandas datetime pandas-groupby


【解决方案1】:

好的,下面的代码看起来有点长——当然有更好/更有效/更短的方法来做这件事。但我认为这相当简单。

split_datetime_span_by_day 下面有两个日期:start_dateend_date。在您的情况下,它将是来自您的源数据的 datenext_event

然后该函数会检查该时间段(开始 -> 结束)是否跨越午夜。如果不是,则返回开始日期、结束日期和时间段(以秒为单位)。如果它确实跨越了午夜,它会创建一个新段(开始 -> 午夜),然后再次调用自身(即递归),并且该过程继续进行,直到时间段不跨越午夜。

请注意:返回的段列表由 (start, end, nmb_seconds) 的元组组成。我返回的是秒数,而不是您问题中的分钟数,因为我不知道您想如何舍入秒数(向上、向下等)。这留给读者作为练习:-)

from datetime import datetime, timedelta

def split_datetime_span_by_day(start_date, end_date, split_segments=None):
  assert start_date < end_date # sanity check

  # when is the next midnight after start_date?
  # adapted from https://ispycode.com/Blog/python/2016-07/Get-Midnight-Today
  start_next_midnight = datetime.combine(start_date, datetime.min.time()) + timedelta(days=1)

  if split_segments is None:
    split_segments = []

  if end_date < start_next_midnight:
    # end date is before next midnight, no split necessary
    return split_segments + [(
      start_date,
      end_date,
      (end_date - start_date).total_seconds()
    )]

  # otherwise, split at next midnight...
  split_segments += [(
    start_date,
    start_next_midnight,
    (start_next_midnight - start_date).total_seconds()
  )]

  if (end_date - start_next_midnight).total_seconds() > 0:  
    # ...and recurse to get next segment
    return split_datetime_span_by_day(
      start_date=start_next_midnight,
      end_date=end_date,
      split_segments=split_segments
    )
  else:
    # case where start_next_midnight == end_date i.e. end_date is midnight
    # don't split & create a 0 second segment
    return split_segments

# test case: 
start_date = datetime.strptime('2021-09-12 00:00:00', '%Y-%m-%d %H:%M:%S')
end_date = datetime.strptime('2021-09-14 01:00:00', '%Y-%m-%d %H:%M:%S')

print(split_datetime_span_by_day(start_date=start_date, end_date=end_date))

# returned values:
# [
#   (datetime.datetime(2021, 9, 12, 0, 0), datetime.datetime(2021, 9, 13, 0, 0), 86400.0),
#   (datetime.datetime(2021, 9, 13, 0, 0), datetime.datetime(2021, 9, 14, 0, 0), 86400.0),
#   (datetime.datetime(2021, 9, 14, 0, 0), datetime.datetime(2021, 9, 14, 1, 0), 3600.0)
# ]

【讨论】:

  • 谢谢,最后我还能够使我的解决方案适用于我的最终目标,即按天和事件分组。
猜你喜欢
  • 1970-01-01
  • 2016-09-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-02
  • 2012-10-21
  • 2021-10-14
  • 1970-01-01
相关资源
最近更新 更多