【问题标题】:Comparing massive lists of dictionaries in python比较python中的大量字典列表
【发布时间】:2008-12-19 22:49:15
【问题描述】:

我从没想过我会在使用 python 时遇到速度问题,但我遇到了。我正在尝试根据字典值将非常大的字典列表相互比较。我比较了两个列表,第一个是这样的

biglist1=[{'transaction':'somevalue', 'id':'somevalue', 'date':'somevalue' ...}, {'transactio':'somevalue', 'id':'somevalue', 'date':'somevalue' ...}, ...]

'somevalue' 代表用户生成的字符串、整数或小数。现在,第二个列表非常相似,除了 id 值始终为空,因为它们还没有被分配。

biglist2=[{'transaction':'somevalue', 'id':'', 'date':'somevalue' ...}, {'transactio':'somevalue', 'id':'', 'date':'somevalue' ...}, ...]

所以我想获取 biglist2 中的字典列表,这些字典与 biglist1 中的字典匹配所有其他键 除了 id。

我一直在做

for item in biglist2:
    for transaction in biglist1:
       if item['transaction'] == transaction['transaction']:
          list_transactionnamematches.append(transaction)

for item in biglist2:
    for transaction in list_transactionnamematches:
       if item['date'] == transaction['date']:
          list_transactionnamematches.append(transaction)

... 等等,不比较 id 值,直到我得到最终的匹配列表。由于列表可能非常大(每个列表大约 3000 多个项目),因此 python 循环需要相当长的时间。

我猜这并不是真正应该进行这种比较的方式。有什么想法吗?

【问题讨论】:

    标签: python


    【解决方案1】:

    您要用于查找的字段的索引。 O(n+m)

    matches = []
    biglist1_indexed = {}
    
    for item in biglist1:
        biglist1_indexed[(item["transaction"], item["date"])] = item
    
    for item in biglist2:
        if (item["transaction"], item["date"]) in biglist1_indexed:
            matches.append(item)
    

    这可能比你现在做的快数千倍。

    【讨论】:

    • "if a in b:" 是一个搜索操作,不是常数时间。实际上,假设元组搜索是线性的,这仍然是 O(m*n)。
    • 这是一个糟糕的假设,因为事实并非如此。这是一个哈希表查找。
    • 更多信息:Python 的字典实现将字典查找的平均复杂度降低到 O(1) wiki.python.org/moin/DictionaryKeys
    • 好的,但是在“if a in b.keys()”语句中,它是在进行列表搜索,而不是字典查找正确吗?
    • 有关 python 3 和其他中 ​​dict.keys() 更改的信息:python.org/dev/peps/pep-3106
    【解决方案2】:

    你要做的是使用正确的数据结构:

    1. 创建一个字典,将第一个字典中其他值的元组映射到它们的 id。

    2. 在两个字典中创建两组值元组。然后使用集合操作得到你想要的元组集合。

    3. 使用从第 1 点开始的字典为这些元组分配 ID。

    【讨论】:

    • 我已经编写了一个代码示例来执行此操作,但我认为步骤 2 中的设置操作是不必要的,因为您可以廉价地检查您的目标元组是否在您的步骤 1 dict 键列表中。
    【解决方案3】:

    原谅我生疏的 python 语法,已经有一段时间了,所以考虑一下这个部分伪代码

    import operator
    biglist1.sort(key=(operator.itemgetter(2),operator.itemgetter(0)))
    biglist2.sort(key=(operator.itemgetter(2),operator.itemgetter(0)))
    i1=0;
    i2=0;
    while i1 < len(biglist1) and i2 < len(biglist2):
        if (biglist1[i1]['date'],biglist1[i1]['transaction']) == (biglist2[i2]['date'],biglist2[i2]['transaction']):
            biglist3.append(biglist1[i1])
            i1++
            i2++
        elif (biglist1[i1]['date'],biglist1[i1]['transaction']) < (biglist2[i2]['date'],biglist2[i2]['transaction']):
            i1++
        elif (biglist1[i1]['date'],biglist1[i1]['transaction']) > (biglist2[i2]['date'],biglist2[i2]['transaction']):
            i2++
        else:
            print "this wont happen if i did the tuple comparison correctly"
    

    这会将两个列表按(日期,交易)排序为相同的顺序。然后它并排穿过它们,逐个遍历寻找相对相邻的匹配项。它假定 (date,transaction) 是唯一的,并且在元组排序和比较方面我并没有完全不感兴趣。

    【讨论】:

      【解决方案4】:

      在 O(m*n)...

      for item in biglist2:
          for transaction in biglist1:
             if (item['transaction'] == transaction['transaction'] &&
                 item['date'] == transaction['date'] &&
                 item['foo'] == transaction['foo'] ) :
      
                list_transactionnamematches.append(transaction)
      

      【讨论】:

      • 这将遍历 biglist1 总共 len(biglist2) 次。
      【解决方案5】:

      我可能会采取的方法是创建一个非常非常轻量级的类,其中包含一个实例变量和一个方法。实例变量是指向字典的指针;该方法覆盖了内置的特殊方法__hash__(self),返回一个根据字典中除id之外的所有值计算得出的值。

      从那里解决方案似乎相当明显:创建两个最初为空的字典:NM(用于 no-matchesmatches。)循环遍历每个只列出一次,然后为每个代表事务的字典(我们称之为Tx_dict)创建一个新类的实例(Tx_ptr)。然后在NM中测试匹配这个Tx_ptr的项:如果N中没有匹配项,则将当前Tx_ptr插入N;如果N 中存在匹配项,但M 中没有匹配项,则将当前Tx_ptr 插入M,其中Tx_ptr 本身作为键,包含Tx_ptr 作为值的列表;如果NM 中存在匹配项,请将当前Tx_ptr 附加到与M 中的该键关联的值。

      在您浏览完每一项后,您的字典M 将包含指向与其他交易匹配的所有交易的指针,所有交易都整齐地组合到您的列表中。

      编辑:糟糕!显然,如果在N 中存在匹配的Tx_ptr 而在M 中没有匹配的Tx_ptr,正确的操作是在M 中插入一个键值对,其中当前的Tx_ptr 作为键和值,a当前Tx_ptr 的列表已经在N 中的Tx_ptr

      【讨论】:

        【解决方案6】:

        看看 Psyco。它是一个 Python 编译器,可以从您的源代码创建非常快速、优化的机器代码。

        http://sourceforge.net/projects/psyco/

        虽然这不是解决代码效率问题的直接解决方案,但它仍然可以帮助加快处理速度,而无需编写任何新代码。也就是说,我仍然强烈建议尽可能优化您的代码,并使用 Psyco 尽可能提高速度。

        他们的部分指南专门讨论了使用它来加速列表、字符串和数值计算繁重的函数。

        http://psyco.sourceforge.net/psycoguide/node8.html

        【讨论】:

          【解决方案7】:

          我也是新手。我的代码的结构与他的大致相同。

          for A in biglist:
              for B in biglist:
                  if ( A.get('somekey') <> B.get('somekey') and #don't match to itself
                       len( set(A.get('list')) - set(B.get('list')) ) > 10:
                      [do stuff...]
          

          遍历 10000 个字典列表需要几个小时。每个字典都包含很多东西,但我可能只提取 ids('somekey')和列表('list')并重写为 10000 个键:值对的单个字典。

          问题:那会快多少?而且我认为这比使用列表列表要快,对吧?

          【讨论】:

            猜你喜欢
            • 2017-12-19
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多