【问题标题】:Merge list of timestamps in Python在 Python 中合并时间戳列表
【发布时间】:2015-03-19 09:38:02
【问题描述】:

请帮我找到一个不需要大量循环的解决方案。我有一个时间戳列表,例如

["2014-04-11 08:00:00.000000",
 "2014-04-11 09:35:00.000000",
 "2014-04-11 09:35:00.000000",
 "2014-04-11 09:40:00.000000",
 "2014-04-11 11:00:00.000000",
 ...]

我想“合并”列表中的时间戳,以便彼此在一个公共窗口(例如 10 分钟)内的时间戳成为一个条目。所以上面的示例列表会变成

["2014-04-11 08:00:00.000000",
 "2014-04-11 09:35:00.000000",
 "2014-04-11 11:00:00.000000",
 ...]

还请注意,合并的三个时间戳是针对“9:35”值而不是“9:40”进行的。我想合并时间戳以转到最频繁的条目。如果出现平局,则在较早/最频繁的时间戳上合并。

而且我还在尝试跟踪合并了多少时间戳。因此,对于上面的示例,保留计数的列表将是 [1,3,1,...]

【问题讨论】:

  • 你有什么可以部分有效的吗?
  • 另外,什么定义了公共窗口?可以创建连续的 10 分钟窗口并查看时间戳是否适合其中,还是必须从数据中导出时间窗口?
  • @SimeonVisser 给定时间戳的窗口被定义为它周围的 10 分钟间隔,其中有时间戳之前的 5 分钟和之后的 5 分钟。
  • 部分工作的代码很长,效率低下且令人困惑......它循环遍历时间戳列表的枚举,在此期间它 i。创建构成 10 分钟窗口的时间戳列表 ii.循环遍历原始列表以计算每个条目在 (i) iii 中的列表中出现的次数。再次遍历原始列表,检查时间戳之间的差异。如果差值 =

标签: python list collections timestamp


【解决方案1】:

如果您还没有看过它,在这种情况下,将其转换为 pandas DataFrame 将证明是有效的。

IMO 的一种方法是将这些时间戳记为 index,计数为 column。然后,通过循环一次,您可以删除位于同一公共窗口中的那些行(使用datetime.timedeltanumpy.timedelta64)并更新该行的列count 的值。

拥有更多信息将有助于提供更详细的答案。例如,您的列表是否已排序,如果没有,它是否必须保持与合并前相同的顺序? (从你的例子看来它已经排序了)

【讨论】:

    【解决方案2】:

    您可以将groupby 与在组之间“切换”的特殊键一起使用。首先准备数据:

    from itertools import groupby
    from datetime import datetime
    
    l = ["2014-04-11 08:00:00.000000",
     "2014-04-11 09:35:00.000000",
     "2014-04-11 09:35:00.000000",
     "2014-04-11 09:40:00.000000",
     "2014-04-11 11:00:00.000000"]
    l = map(lambda x: datetime.strptime(x, "%Y-%m-%d %H:%M:%S.%f"), l)
    

    现在你可以这样做了:

    class grouper():
        def __call__(self, d):
            if not hasattr(self, 'prev'):    # first element: init switch
                self.switch = 1
            elif (d - self.prev).total_seconds() > 10*60:    # 10min
                self.switch *= -1
            self.prev = d                    # save current value
            return self.switch
    
    
    def most_common(group):
        lst = list(group)
        return lst[0]   # choose the first element in the group
    
    >>> [most_common(g) for k, g in groupby(l, key = grouper())]
    [datetime.datetime(2014, 4, 11, 8, 0),
     datetime.datetime(2014, 4, 11, 9, 35),
     datetime.datetime(2014, 4, 11, 11, 0)]
    

    您可以调整most_common 函数以满足您的条件。

    【讨论】:

      【解决方案3】:

      假设时间戳是排序的,那么...:

      import datetime
      
      def merged_ts(timestamps):
          merged_strings = []
          counts = []
          for ts in timestamps:
              dt = datetime.datetime.strptime(ts, '%Y-%m-%d %H:%M:%S.%f')
              if not merged_strings:  # first-time switch
                  merged_string.append(ts)
                  counts.append(1)
                  oldt = dt
                  continue
              dif = dt - oldt
              if dif.total_seconds < 300:  # 5 minutes
                  counts[-1] += 1
                  continue
              merged_string.append(ts)
              counts.append(1)
              oldt = dt
          return merged_strings, counts
      

      添加:OP 指定时间戳不是最初排序的(但可能会为此目的进行排序),如果时间戳是 T、T+4 分钟、T+8 分钟、T+12 分钟等,它们必须合并到一个插槽中(w/适当的计数)。这个版本需要一点点改变,然后......:

      import datetime
      
      def merged_ts(timestamps):
          merged_strings = []
          counts = []
          for ts in sorted(timestamps):
              dt = datetime.datetime.strptime(ts, '%Y-%m-%d %H:%M:%S.%f')
              if not merged_strings:  # first-time switch
                  merged_string.append(ts)
                  counts.append(1)
                  oldt = dt
                  continue
              dif = dt - oldt
              oldt = dt
              if dif.total_seconds < 300:  # 5 minutes
                  counts[-1] += 1
              else:
                  merged_string.append(ts)
                  counts.append(1)
          return merged_strings, counts
      

      我刚刚添加了一个sorted 调用,并将oldt = dt 移动到它在循环的每一段发生的位置(第一次切换除外)——这样每个新传入的 ts 都会被检查与当前存储桶中的“最近”(最新)日期戳,而不是像以前那样与“第一个”(最旧)日期戳相比。 (仅出于风格问题,我将最后的条件改为if/else,而不是在那里使用continue,因为条件的两条腿现在已经很平衡了)。

      第一次开关是愚蠢的,但删除这个(不重复 strptime 需要稍微微妙的代码,例如:

      if not timestamps: return [], []
      it = iter(sorted(
          (ts,datetime.datetime.strptime(ts, '%Y-%m-%d %H:%M:%S.%f'))
          for ts in timestamps))
      first = next(it)
      merged_strings = [first[1]]
      oldt = first[0]
      counts = [1]
      for ts, st in it:
          dif = dt - oldt
          oldt = dt
          if dif.total_seconds < 300:  # 5 minutes
              counts[-1] += 1
          else:
              merged_string.append(ts)
              counts.append(1)
      return merged_strings, counts
      

      第一次切换的版本对我来说似乎更可取,在这种情况下,纯粹是出于风格的原因。

      【讨论】:

      • 时间字符串不一定是排序的,但我认为这可以从timestamps.sort()开始解决,或者here的另一种方法。为了更清楚地了解行为,[T, T+4, T+8, T+12] 的时间戳应该合并为计数 = 4 的 [T]。但是对于 [T, T+4, T +10, T+12],结果将是 [T, T+10],计数 = [2, 2]。我希望这会有所帮助:)
      • 好的,问题略有不同,编辑解决。
      【解决方案4】:

      可以这样解决:

      import datetime
      
      data = ["2014-04-11 08:00:00.000000", "2014-04-11 09:35:00.000000", "2014-04-11 09:35:00.000000", "2014-04-11 09:40:00.000000", "2014-04-11 11:00:00.000000"]
      
      delta = datetime.timedelta(minutes=10)
      result = []
      bucket = []
      current = None
      for item in data:
          datetime_obj = datetime.datetime.strptime(item, '%Y-%m-%d %H:%S:%M.%f')
          if current is None:
              current = datetime_obj
              bucket = [current]
              continue
          if (datetime_obj - current) <= delta:
              bucket.append(datetime_obj)
          else:
              result.append(bucket)
              current = datetime_obj
              bucket = [current]
      
      if bucket:
          result.append(bucket)
      
      for bucket in result:
          print(bucket)
      

      例子:

      >>> for bucket in result:
      ...     print(bucket)
      ...
      [datetime.datetime(2014, 4, 11, 8, 0)]
      [datetime.datetime(2014, 4, 11, 9, 0, 35), datetime.datetime(2014, 4, 11, 9, 0, 40)]
      [datetime.datetime(2014, 4, 11, 11, 0)]
      

      result 数据结构可用于计算所需的值:标识窗口的每个时间戳以及创建该窗口可用(“已使用”)的时间戳数。

      【讨论】:

      • 这样做了,而且很干净,谢谢!我做了一些调整:(i)用datetime_obj.sort()对时间戳进行排序,(ii)用merged.append(max(bucket, key=bucket.count))合并存储桶
      猜你喜欢
      • 2011-10-22
      • 2014-08-10
      • 1970-01-01
      • 2013-01-26
      • 1970-01-01
      • 1970-01-01
      • 2015-01-28
      • 2018-10-14
      • 1970-01-01
      相关资源
      最近更新 更多