【问题标题】:Is there a faster alternative to this approach to get last update message from list of dict?这种方法是否有更快的替代方法来从字典列表中获取最新更新消息?
【发布时间】:2019-07-07 14:42:05
【问题描述】:

我需要从数据流中获取最后一条更新消息。数据是这样的:

test_data = 
[{u'category': u'3',
  u'entity': u'entityA',
  u'length': u'0',
  u'timestamp': u'1562422690'},
 {u'category': u'3',
  u'entity': u'entityA',
  u'length': u'1',
  u'timestamp': u'1562422680'},
 {u'category': u'3',
  u'entity': u'entityB',
  u'length': u'2',
  u'timestamp': u'1562422691'},
 {u'category': u'3',
  u'entity': u'entityB',
  u'length': u'3',
  u'timestamp': u'1562422688'},
 {u'category': u'3',
  u'entity': u'entityC',
  u'length': u'4',
  u'timestamp': u'1562422630'},
 {u'category': u'3',
  u'entity': u'entityC',
  u'length': u'5',
  u'timestamp': u'1562422645'},
 {u'category': u'3',
  u'entity': u'entityD',
  u'length': u'6',
  u'timestamp': u'1562422645'}]

建议使用以下方法here

test_alexander = {entity: sorted([d for d in test_data if d.get('entity') == entity], key=lambda x: x['timestamp'])[-1]
     for entity in set(d.get('entity') for d in test_data)}

返回这个(它完全按照预期工作):

{u'entityA': {u'category': u'3',
  u'entity': u'entityA',
  u'length': u'0',
  u'timestamp': u'1562422690'},
 u'entityB': {u'category': u'3',
  u'entity': u'entityB',
  u'length': u'2',
  u'timestamp': u'1562422691'},
 u'entityC': {u'category': u'3',
  u'entity': u'entityC',
  u'length': u'5',
  u'timestamp': u'1562422645'},
 u'entityD': {u'category': u'3',
  u'entity': u'entityD',
  u'length': u'6',
  u'timestamp': u'1562422645'}}

问题是我有 7k 个唯一的“实体”,并且“test_data”中有多达 700 万个列表项。上述解决方案需要很长时间,我想知道是否有更快的方法。

【问题讨论】:

    标签: python json python-2.7 performance dictionary


    【解决方案1】:

    您应该能够通过单个比较来执行此操作。在循环过程中,只需跟踪到目前为止每个类别看到的最大值:

    from collections import defaultdict
    
    def getMax(test_data):
        d = defaultdict(lambda: {'timestamp':0})
    
        for item in test_data:
            if int(item['timestamp']) > int(d[item['entity']]['timestamp']):
                d[item['entity']] = item
        return d
    

    返回值将是一个以entity 为关键字的字典,每个字典都有最大值。在循环中排序或构建数组应该要快得多。 700 万仍然需要一段时间。

    【讨论】:

    • 感谢@Mark Meyer - 我如何修改您的答案以获得与我问题中最后一段代码完全相同的输出?
    • @MV 默认字典只是一个带有一些额外内容的字典。您可以将其用作字典。如果你想要一个普通的字典,只需将它传递给 dict() -- dict(getMax(test_data)) 或类似的。或者return dict(d)在函数末尾。
    【解决方案2】:

    似乎纯 python 解决方案可能对您的需求来说太慢了,我建议使用pandas,它的性能可能会更好。

    你可以试试这个吗?

    import pandas as pd
    
    test_data = [{u'category': u'3',
                  u'entity': u'entityA',
                  u'length': u'0',
                  u'timestamp': u'1562422690'},
                 {u'category': u'3',
                  u'entity': u'entityA',
                  u'length': u'1',
                  u'timestamp': u'1562422680'},
                 {u'category': u'3',
                  u'entity': u'entityB',
                  u'length': u'2',
                  u'timestamp': u'1562422691'},
                 {u'category': u'3',
                  u'entity': u'entityB',
                  u'length': u'3',
                  u'timestamp': u'1562422688'},
                 {u'category': u'3',
                  u'entity': u'entityC',
                  u'length': u'4',
                  u'timestamp': u'1562422630'},
                 {u'category': u'3',
                  u'entity': u'entityC',
                  u'length': u'5',
                  u'timestamp': u'1562422645'},
                 {u'category': u'3',
                  u'entity': u'entityD',
                  u'length': u'6',
                  u'timestamp': u'1562422645'}]
    
    df = pd.DataFrame(test_data)
    df["timestamp"] = df["timestamp"].astype(int)
    
    print(df.loc[df.groupby("entity")["timestamp"].idxmax()].to_dict(orient='records'))
    

    【讨论】:

    • 感谢@Adam.Er8 花时间解决我的问题并提供答案。我通过使用其他人提到的 defaultdict 方法成功了。
    【解决方案3】:

    您可以使用max 代替sorted,因为您只需要最大条目而不需要对其余项目进行排序:

    test_alexander = {entity: max([d for d in test_data if d.get('entity') == entity], key=lambda x: x['timestamp'])
                      for entity in set(d.get('entity') for d in test_data)}
    

    (max 需要 O(n),排序需要 O(n*logn))

    【讨论】:

    • 仍然需要很长时间(大约 6 分钟) - 我开始认为问题出在第二行(“for entity in...”)而不是 max vs sort
    • @MV 我会发布一个不同的答案,建议使用 pandas,这可能会快很多。
    【解决方案4】:

    这应该可以解决问题。它扫描一次测试数据并记录每个实体的最新消息:

    from collections import defaultdict
    
    latest_message = defaultdict(lambda: dict('timestamp'=0)
    
    for data in test_data:
        latest = latest_message[data[entity]]
        if data['timestamp'] > latest['timestamp']:
            latest_message[data[entity]].update(data)
    

    【讨论】:

      【解决方案5】:

      我将从按实体分区开始,然后使用 max 获取每个实体的最新记录。这将具有线性复杂性。您拥有的代码过滤然后对每个接近立方的实体的记录进行排序。

      在 Python 中,这看起来像:

      partitions = dict()
      for record in test_data:
          partitions.setdefault(record['entity'], []).append(record)
      # replace this with defaultdict for 2x performance 
      
      for key in partitions:
          partitions[key] = max(partitions[key], key=lambda x: int(x['timestamp']))
      

      结果在partitions。并有形状{entity:[{}]}

      可以通过用 max 调用替换累积来减少内存使用量,但实际上可能会更慢。

      【讨论】:

      • 你的回答会抛出 KeyError: u'entityA' 知道为什么吗?
      • 抱歉,错过了在作业中添加下标。
      猜你喜欢
      • 2012-08-21
      • 1970-01-01
      • 2011-05-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-02-06
      • 2011-08-04
      • 2021-09-07
      相关资源
      最近更新 更多