【问题标题】:Python how to merge the time spans and make a bigger onePython如何合并时间跨度并制作更大的时间跨度
【发布时间】:2021-01-07 00:43:23
【问题描述】:

我有以下数据框。

       padel start_time  end_time  duration
38  Padel 10   08:00:00  09:00:00        60
40  Padel 10   10:00:00  11:30:00        90
42  Padel 10   10:30:00  12:00:00        90
44  Padel 10   11:00:00  12:30:00        90
46  Padel 10   11:30:00  13:00:00        90
49  Padel 10   16:00:00  17:30:00        90
51  Padel 10   16:30:00  18:00:00        90
53  Padel 10   17:00:00  18:30:00        90
55  Padel 10   17:30:00  19:00:00        90
57  Padel 10   18:00:00  19:30:00        90
59  Padel 10   18:30:00  20:00:00        90
61  Padel 10   19:00:00  20:30:00        90
63  Padel 10   19:30:00  21:00:00        90
65  Padel 10   20:00:00  21:30:00        90
67  Padel 10   20:30:00  22:00:00        90

我想选择两者之间最长的时间跨度。我想要的输出应该是这样的

       padel start_time  end_time  duration
38  Padel 10   08:00:00  09:00:00        60
40  Padel 10   10:00:00  13:00:00        180
49  Padel 10   16:00:00  22:00:00        360

我不在乎持续时间。我能做到。但是我将如何合并重叠的时间跨度。 谢谢

【问题讨论】:

  • 你必须由组做吗?与padel 列?
  • 好问题。如果是这样,请将 padel 添加到排序(首先),然后将 and row['padel'] == next_row['padel'] 添加到 elif 条件。

标签: python pandas datetime timespan relative-time-span


【解决方案1】:
  1. 如果start_time 是上方行的greater than end_time(即重叠),您可以使用shift() 创建组。
  2. 我们使用fillna'24:00:00' 来返回第一个值“True”,因为一天的时间不能超过24 小时。这是因为NaN 是第一行的输出,其中shift() 将返回False,如果我们不这样做的话。
  3. 这会返回 boolean 系列的 TrueFalse(即分别为 10,.),因此您只需使用 cumsum 获取累积总和。
  4. 这会创建一个grp 对象,我们可以将其包含在groupby 中。

df = df.sort_values(by=['padel', 'start_time'], ascending=[True, True])
grp = df['start_time'].gt(df['end_time'].shift().fillna('24:00:00')).cumsum() 
df = df.groupby([grp, 'padel'], as_index=False).agg({'start_time':'first', 'end_time':'last'})
df['duration'] = ((pd.to_timedelta(df['end_time']) - 
                   pd.to_timedelta(df['start_time'])).dt.seconds / 60).astype(int)
Out[1]: 
      padel start_time  end_time  duration
0  Padel 10   08:00:00  09:00:00        60
1  Padel 10   10:00:00  13:00:00       180
2  Padel 10   16:00:00  22:00:00       360

带有输入数据框的完整代码

df = pd.DataFrame(pd.DataFrame({'padel': {38: 'Padel 10',
  40: 'Padel 10',
  42: 'Padel 10',
  44: 'Padel 10',
  46: 'Padel 10',
  49: 'Padel 10',
  51: 'Padel 10',
  53: 'Padel 10',
  55: 'Padel 10',
  57: 'Padel 10',
  59: 'Padel 10',
  61: 'Padel 10',
  63: 'Padel 10',
  65: 'Padel 10',
  67: 'Padel 10'},
 'start_time': {38: '08:00:00',
  40: '10:00:00',
  42: '10:30:00',
  44: '11:00:00',
  46: '11:30:00',
  49: '16:00:00',
  51: '16:30:00',
  53: '17:00:00',
  55: '17:30:00',
  57: '18:00:00',
  59: '18:30:00',
  61: '19:00:00',
  63: '19:30:00',
  65: '20:00:00',
  67: '20:30:00'},
 'end_time': {38: '09:00:00',
  40: '11:30:00',
  42: '12:00:00',
  44: '12:30:00',
  46: '13:00:00',
  49: '17:30:00',
  51: '18:00:00',
  53: '18:30:00',
  55: '19:00:00',
  57: '19:30:00',
  59: '20:00:00',
  61: '20:30:00',
  63: '21:00:00',
  65: '21:30:00',
  67: '22:00:00'},
 'duration': {38: 60,
  40: 90,
  42: 90,
  44: 90,
  46: 90,
  49: 90,
  51: 90,
  53: 90,
  55: 90,
  57: 90,
  59: 90,
  61: 90,
  63: 90,
  65: 90,
  67: 90}}))
grp = df['start_time'].gt(df['end_time'].shift().fillna('24:00:00')).cumsum() 
df = df.groupby([grp, 'padel'], as_index=False).agg({'start_time':'first', 'end_time':'last'})
df['duration'] = ((pd.to_timedelta(df['end_time']) - \
                   pd.to_timedelta(df['start_time'])).dt.seconds / 60).astype(int)
df

【讨论】:

  • 这没有给出正确的输出``` Out[61]: padel start_time end_time duration 0 Padel 10 09:00:00 14:00:00 300 1 Padel 10 15:00:00 10:00 :00 1140 ```
  • @gulbazkhan 我的答案与您想要的输出完全匹配。
  • 但它给了我 9 到 14。但它应该给 8 到 9。我还尝试将列更改为 datetime 然后它给了我TypeError: dtype datetime64[ns] cannot be converted to timedelta64[ns]
  • @gulbazkhan 我将使用答案中包含的输入数据框运行完整代码,并确定与您的实际数据可能存在的差异。这对您问题中的示例数据 100% 正确。
  • 好的。谢谢 那行得通。首先需要对数据进行排序。
【解决方案2】:
#Coeece the start and end times to datetime
df['start_time']=pd.to_datetime(df['start_time'])
df['end_time']=pd.to_datetime(df['end_time'])

g=df.groupby(df.end_time.sub(df.start_time.shift(1)).ne('2h').cumsum()).tail(1).reset_index()#Find last entry in each set of pedal

g=g.assign(start_time=df.groupby(df.end_time.sub(df.start_time.shift(1)).ne('2h').cumsum()).start_time.head(1).reset_index().loc[:,'start_time'])#Set start_time to the start_time in each set of pedal


g=g.iloc[:,:-1].join(df.groupby(df.end_time.sub(df.start_time.shift(1)).ne('2h').cumsum()).apply(lambda x: (x['end_time'].max()-(x['start_time'].min())).total_seconds()/60).to_frame('duration').reset_index(drop=True))#Calc the duration



    padel start_time  end_time  duration
0  Padel 10   08:00:00  09:00:00        60
1  Padel 10   10:00:00  13:00:00       180
2  Padel 10   16:00:00  22:00:00       360

【讨论】:

  • 问题解决了。顺便提一句。谢谢。我在运行 ValueError: columns overlap but no suffix specified: Index(['duration'], dtype='object') 时得到了这个
  • 非常适合我。代码中的哪一行给出了这个错误?
  • 最后一行。发生。顺便提一句。它解决了我的问题。持续时间不是什么大问题。谢谢
【解决方案3】:

我想不出一个简单的 pandas 方法来做到这一点,所以我只需要一个 for 循环。尚未测试此代码,但类似:

df = df.sort_values(...)
out_df = pd.DataFrame(columns=df.columns)
next_row = None

for row in df.rows:
    if next_row is None:
        next_row = row
    elif row['start_time'] <= next_row['end_time']:
        next_row['end_time'] = row['end_time']
    else:
        out_df = out_df.append(next_row)
        next_row = None

out_df = out_df.append(next_row)

【讨论】:

  • 我应该得到 10:00 它给了 10:30。其他都很好。同16:30
猜你喜欢
  • 2021-03-30
  • 1970-01-01
  • 1970-01-01
  • 2020-05-03
  • 2011-04-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多