【问题标题】:Group time into time periods in Python Pands在 Python Pandas 中将时间分组为时间段
【发布时间】:2019-07-05 22:39:13
【问题描述】:

我想编写一个将时间分组为时间段的代码。我有两列fromto,我有列表periods。根据来自两列的值,我需要将新列插入到名为 periods 的数据框中,这将代表时间段。 这是代码:

import pandas as pd

df = pd.DataFrame({"from":['08:10', '14:00', '15:00', '17:01', '13:41'],
                   "to":['10:11', '15:32', '15:35' , '18:23', '16:16']})
print(df)

periods = ["00:01-06:00", "06:01-12:00", "12:01-18:00", "18:01-00:00"]
#if times are between two periods, for example '17:01' and '18:23', it counts as first period ("12:01-18:00") 

结果应如下所示:

    from     to       period
0  08:10  10:11  06:01-12:00
1  14:00  15:32  12:01-18:00
2  15:00  15:35  12:01-18:00
3  17:01  18:03  18:01-00:00
4  18:41  19:16  18:01-00:00

两列中的值是日期时间。

【问题讨论】:

  • 您应该始终包含您迄今为止尝试过的内容。
  • 问题是,我不知道该怎么做
  • 第 4 行的结果有误。周期应该是第三周期。此外,您的 df 的第五个“来自”值应该是 '18:41' 而不是 '13:41'。
  • 为什么17:01 - 18:03 属于18:01-00:00?我们只看to 列吗?
  • 如果您的问题已解决,请标记正确答案。

标签: python pandas


【解决方案1】:

这是一种方法(我假设“18:00”属于“12:01-18:00”期间):

results = [0 for x in range(len(df))]
for row in df.iterrows():
    item = row[1]
    start = item['from']
    end = item['to']

    for ind, period in enumerate(periods):
        per_1, per_2 = period.split("-")
        if start.split(":")[0] >= per_1.split(":")[0]:            #hours
            if start.split(":")[0] == per_1.split(":")[0]:
                if start.split(":")[1] >= per_1.split(":")[1]:    #minutes
                    if start.split(":")[1] == per_1.split(":")[1]:
                        results[row[0]] = period
                        break
                    #Wrap around if you reach the end of the list
                    index = ind+1 if ind<len(periods) else 0
                    results[row[0]] = periods[index]
                    break
                index = ind-1 if ind>0 else len(periods)-1
                results[row[0]] = periods[index]
                break

            if start.split(":")[0] <= per_2.split(":")[0]:
                if start.split(":")[0] == per_2.split(":")[0]:
                    if start.split(":")[1] == per_2.split(":")[1]:
                        results[row[0]] = period
                        break
                    #If anything else, then its greater, so in next period
                    index =  ind+1 if ind<len(periods) else 0
                    results[row[0]] = periods[index]
                    break
                results[row[0]] = period
                break

print(results)
df['periods'] = results
['06:01-12:00', '12:01-18:00', '12:01-18:00', '12:01-18:00', '18:01-00:00']

df['periods'] = results
df
    from     to      periods
0  08:10  10:11  06:01-12:00
1  14:00  15:32  12:01-18:00
2  15:00  15:35  12:01-18:00
3  17:01  18:23  12:01-18:00
4  18:41  16:16  18:01-00:00

这应该涵盖所有场景。但是您应该在可能的时间的每个边缘情况下对其进行测试以确保。

【讨论】:

    【解决方案2】:

    下面

    import pandas as pd
    from datetime import datetime
    
    df = pd.DataFrame({"from": ['08:10', '14:00', '15:00', '17:01', '13:41'],
                       "to": ['10:11', '15:32', '15:35', '18:23', '16:16']})
    print(df)
    
    periods = ["00:01-06:00", "06:01-12:00", "12:01-18:00", "18:01-00:00"]
    _periods = [(datetime.strptime(p.split('-')[0], '%H:%M').time(), datetime.strptime(p.split('-')[1], '%H:%M').time()) for
                p in periods]
    
    
    def match_row_to_period(row):
        from_time = datetime.strptime(row['from'], '%H:%M').time()
        to_time = datetime.strptime(row['to'], '%H:%M').time()
        for idx, p in enumerate(_periods):
            if from_time >= p[0] and to_time <= p[1]:
                return periods[idx]
        for idx, p in enumerate(_periods):
            if idx > 0:
                prev_p = _periods[idx - 1]
                if from_time <= prev_p[1] and to_time >= p[0]:
                    return periods[idx - 1]
    
    
    df['period'] = df.apply(lambda row: match_row_to_period(row), axis=1)
    print('-----------------------------------')
    print('periods: ')
    for _p in _periods:
        print(str(_p[0]) + ' -- ' + str(_p[1]))
    print('-----------------------------------')
    
    print(df)
    

    输出

        from     to
    0  08:10  10:11
    1  14:00  15:32
    2  15:00  15:35
    3  17:01  18:23
    4  13:41  16:16
    -----------------------------------
    periods: 
    00:01:00 -- 06:00:00
    06:01:00 -- 12:00:00
    12:01:00 -- 18:00:00
    18:01:00 -- 00:00:00
    -----------------------------------
        from     to       period
    0  08:10  10:11  06:01-12:00
    1  14:00  15:32  12:01-18:00
    2  15:00  15:35  12:01-18:00
    3  17:01  18:23  12:01-18:00
    4  13:41  16:16  12:01-18:00
    

    【讨论】:

      【解决方案3】:

      不确定,如果有更好的解决方案,但这里有一种使用 applyassign pandas 方法的方法,这通常比迭代 DataFrame 更 Pythonic,因为 pandas 针对完整的 df ix 分配操作进行了优化,而不是逐行更新(请参阅这个很棒的博客post)。

      附带说明,我在这里使用的数据类型是datetime.time 实例,而不是您的示例中的字符串。在处理时间时,最好使用适当的时间库而不是字符串表示。

      from datetime import time
      
      df = pd.DataFrame({
          "from": [
              time(8, 10),
              time(14, 00),
              time(15, 00),
              time(17, 1),
              time(13, 41)
          ],
          "to": [
              time(10, 11),
              time(15, 32),
              time(15, 35),
              time(18, 23),
              time(16, 16)
          ]
      })
      
      periods = [{
          'from': time(00, 1),
          'to': time(6, 00),
          'period': '00:01-06:00'
      }, {
          'from': time(6, 1),
          'to': time(12, 00),
          'period': '06:01-12:00'
      }, {
          'from': time(12, 1),
          'to': time(18, 00),
          'period': '12:01-18:00'
      }, {
          'from': time(18, 1),
          'to': time(0, 00),
          'period': '18:01-00:00'
      }]
      
      
      def find_period(row, periods):
          """Map the df row to the period which it fits between"""
          for ix, period in enumerate(periods):
              if row['to'] <= periods[ix]['to']:
                  if row['from'] >= periods[ix]['from']:
                      return periods[ix]['period']
      
      # Use df assign to assign the new column to the df
      df.assign(
          **{
              'period':
                  df.apply(lambda row: find_period(row, periods), axis='columns')
          }
      )
      
      Out:
             from        to       period
      0  08:10:00  10:11:00  06:01-12:00
      1  14:00:00  15:32:00  12:01-18:00
      2  15:00:00  15:35:00  12:01-18:00
      3  17:01:00  18:23:00         None
      4  13:41:00  16:16:00  12:01-18:00
      

      注意ix 3 处的行正确显示 None,因为它不准确地适合您定义的两个时期中的任何一个(而是桥接 12:00-18:0018:00-00:00

      【讨论】:

        猜你喜欢
        • 2021-03-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-04-22
        • 2018-02-12
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多