Python:更新元组列表......最快的方法

时间:2009-07-27 04:58:42

标签: python

这个问题与此处提出的另一个问题有关: Sorting 1M records

我已经弄清楚了排序时遇到的问题。每次更新数据时,我都会将字典中的项目排序到列表中。我已经意识到Python排序的很多功能在于它可以更快地对已经部分排序的数据进行排序。

所以,这是问题所在。假设我有以下作为样本集:

self.sorted_records = [(1, 1234567890), (20, 1245678903), 
                       (40, 1256789034), (70, 1278903456)]
列表中每个元组的

t[1]是唯一ID。现在我想用下面的内容更新这个列表:

updated_records = {1245678903:45, 1278903456:76}

最快的方式是以最快的方式完成

self.sorted_records = [(1, 1234567890), (45, 1245678903),
                       (40, 1256789034), (76, 1278903456)]

目前我正在做这样的事情:

updated_keys = updated_records.keys()
for i, record in enumerate(self.sorted_data):
    if record[1] in updated_keys:
        updated_keys.remove(record[1])
        self.sorted_data[i] = (updated_records[record[1]], record[1])

但我确信那里有一个更快,更优雅的解决方案。

任何帮助?

*编辑 事实证明我使用了错误的exids,因为当我进行更新时它们按排序顺序排列。我实际上对t [0]按排序顺序感兴趣。在我进行更新后,我打算使用更新的数据,但看起来bisect可能是按排序顺序插入的票证。 结束编辑*

4 个答案:

答案 0 :(得分:2)

您正在扫描所有n条记录。您可以改为执行二进制搜索,即O(log(n))而不是O(n)。您可以使用bisect模块执行此操作。

答案 1 :(得分:1)

因为显然你并不关心self.sorted_records实际排序的结束值(你有1号,45号,20号,76号的值 - 那是没有排序的! - ),你似乎只关心updated_records中同样位于self.sorted_data的ID,listcomp(如果你想动态更改updated_record,还有副作用)会很好地为你服务,即:

self.sorted_data = [(updated_records.pop(recid, value), recid) 
                    for (value, recid) in self.sorted_data]

.pop调用会从updated_records中删除最终在新self.sorted_data中的键(和相应的值)(以及“recid的前一个值” ,value,作为pop的第二个参数提供,以确保在updated_record}中recid不在的地方没有变化;这留下了updated_record“新”的内容,所以你可以在重新排序之前将其附加到self.sorted_data,即我怀疑你想要继续使用类似

的内容
self.sorted_data.extend(value, recid 
                        for recid, value in updated_records.iteritems())
self.sorted_data.sort()

虽然这部分超出了你实际问的问题(而且我只是因为我已经看过你的以前的问题而给出了这些问题; - )。

答案 2 :(得分:1)

这里你可能最好通过某种形式的树来维护(保留排序顺序,同时允许O(log n)替换。)没有内置的balanaced树类型,但你可以找到很多第三方的例子。或者,你可以:

  1. 使用二进制搜索来查找节点。 bisect模块将执行此操作,但它会根据正常的python比较顺序进行比较,而您似乎根据每个元组的第二个元素进行排序。您可以撤消此操作,或者只编写您自己的二进制搜索(或者只是从bisect_left获取代码并对其进行修改)

  2. 同时使用dict 列表。该列表仅包含已排序的。您可以轻松地将dict类包装起来以确保它保持同步。这允许您在保持键的排序顺序的同时快速更新字典。这可以防止由于dict / list之间的持续转换而导致排序性能丢失的问题。

  3. 这是一个快速实现这样的事情:

    import bisect
    
    class SortedDict(dict):
        """Dictionary which is iterable in sorted order.
    
        O(n) sorted iteration
        O(1) lookup
        O(log n) replacement  ( but O(n) insertion or new items)
        """
    
        def __init__(self, *args, **kwargs):
            dict.__init__(self, *args, **kwargs)
            self._keys = sorted(dict.iterkeys(self))
    
        def __setitem__(self, key, val):
            if key not in self:
                # New key - need to add to list of keys.
                pos = bisect.bisect_left(self._keys, key)
                self._keys.insert(pos, key)
            dict.__setitem__(self, key, val)
    
        def __delitem__(self, key):
            if key in self:
                pos = bisect.bisect_left(self._keys, key)
                del self._keys[pos]
            dict.__delitem__(self, key)
    
        def __iter__(self):
            for k in self._keys: yield k
        iterkeys = __iter__
    
        def iteritems(self):
            for k in self._keys: yield (k, self[k])
    
        def itervalues(self):
            for k in self._keys: yield self[k]
    
        def update(self, other):
            dict.update(self, other)
            self._keys = sorted(dict.iterkeys(self)) # Rebuild (faster if lots of changes made - may be slower if only minor changes to large dict)
    
        def keys(self): return list(self.iterkeys())
        def values(self): return list(self.itervalues())
        def items(self): return list(self.iteritems())
    
        def __repr__(self):
            return "%s(%s)" % (self.__class__.__name__, ', '.join("%s=%r" % (k, self[k]) for k in self))
    

答案 3 :(得分:0)

由于你想用字典键替换,但是按字典值排序数组,你肯定需要线性搜索键。从这个意义上讲,您的算法是您所希望的最佳算法。

如果要保留旧字典值,则可以使用二进制搜索值,然后在二进制搜索引导的位置附近找到密钥。