繁体   English   中英

在Python中对1M记录进行排序的最佳方法

[英]Best way to sort 1M records in Python

我有一个运行的服务,它包含大约1,000,000个字典的列表并执行以下操作

myHashTable = {}
myLists = { 'hits':{}, 'misses':{}, 'total':{} }
sorted = { 'hits':[], 'misses':[], 'total':[] }
for item in myList:
  id = item.pop('id')
  myHashTable[id] = item
  for k, v in item.iteritems():
    myLists[k][id] = v

所以,如果我有以下词典列表:

[ {'id':'id1', 'hits':200, 'misses':300, 'total':400},
  {'id':'id2', 'hits':300, 'misses':100, 'total':500},
  {'id':'id3', 'hits':100, 'misses':400, 'total':600}
]

我结束了

myHashTable =
{ 
  'id1': {'hits':200, 'misses':300, 'total':400},
  'id2': {'hits':300, 'misses':100, 'total':500},
  'id3': {'hits':100, 'misses':400, 'total':600}
}

myLists = 

    {
      'hits': {'id1':200, 'id2':300, 'id3':100},
      'misses': {'id1':300, 'id2':100, 'id3':400},
      'total': {'id1':400, 'id2':500, 'id3':600}
    }

然后,我需要对每个myLists词典中的所有数据进行排序。

我目前正在做的事情如下:

def doSort(key):
  sorted[key] = sorted(myLists[key].items(), key=operator.itemgetter(1), reverse=True)

which would yield, in the case of misses:
[('id3', 400), ('id1', 300), ('id2', 200)] 

当我有大约100,000条记录时,这很有效,但是有1,000,000条,至少需要5到10分钟才能对每条记录进行排序,总共16条(我原来的字典列表实际上有17个字段,包括弹出的id)

*编辑*此服务是ThreadingTCPServer,它有一个允许客户端连接和添加新数据的方法。 新数据可能包括新记录(意味着对已经在内存中具有唯一“id”的字典)或修改后的记录(意味着具有其他键值对的不同数据的相同“id”

所以,一旦这个运行,我会传入

 [ {'id':'id1', 'hits':205, 'misses':305, 'total':480}, {'id':'id4', 'hits':30, 'misses':40, 'total':60}, {'id':'id5', 'hits':50, 'misses':90, 'total':20 ] 

我一直在使用字典来存储数据,这样我就不会有重复数据了。 在使用新的/修改的数据更新字典后,我会使用它们。

*结束编辑*

那么,对我进行排序的最佳方式是什么? 有更好的方法吗?

你可以从Guido找到这个相关的答案: 使用Python在2MB RAM中排序一百万个32位整数

你真正想要的是一个有序的容器,而不是一个无序的容器。 这会在插入结果时隐式排序。 这个标准数据结构是一棵树。

但是,Python中似乎没有这些。 我无法解释; 这是任何语言的核心基础数据类型。 Python的dict和set都是无序容器,它们映射到哈希表的基本数据结构。 它应该有一个优化的树数据结构; 你可以用很多东西做一些哈希表是不可能完成的事情,并且它们很难实现,因此人们通常不希望自己这样做。

(也没有映射到链表的东西,它也应该是核心数据类型。不,deque不等同。)

我没有一个现有的有序容器实现指向您(它可能应该是本机实现,而不是Python),但希望这将指向正确的方向。

一个好的树实现应该支持在一个范围内迭代迭代(“按顺序迭代[2,100]中的所有值”),从O(1)中的任何其他节点查找next / prev值,有效范围提取(“删除所有值[2,100]并将它们返回到一个新树“),等等。如果有人对Python有这样一个优化的数据结构,我很想知道它。 (并非所有操作都适合Python的数据模型;例如,要从另一个值获取next / prev值,您需要对节点的引用,而不是值本身。)

如果您有固定数量的字段,请使用元组而不是字典。 将要排序的字段放在第一个位置,然后使用mylist.sort()

其他人提供了一些很好的建议,试试看。

作为一般建议,在这种情况下,您需要分析您的代码。 确切知道大部分时间花在哪里。 瓶颈隐藏得很好,在你最不希望的地方。
如果涉及到大量的数字运算,那么像(现在已经死亡的)psyco这样的JIT编译器也可能有所帮助。 当处理需要几分钟或几小时时,2倍的加速确实很重要。

这似乎很快。

raw= [ {'id':'id1', 'hits':200, 'misses':300, 'total':400},
    {'id':'id2', 'hits':300, 'misses':100, 'total':500},
    {'id':'id3', 'hits':100, 'misses':400, 'total':600}
]

hits= [ (r['hits'],r['id']) for r in raw ]
hits.sort()

misses = [ (r['misses'],r['id']) for r in raw ]
misses.sort()

total = [ (r['total'],r['id']) for r in raw ]
total.sort()

是的,它通过原始数据进行三次传递。 我认为这比通过一次传递数据更快。

可能你可以使用堆队列,而不是试图保持列表的顺序。 它允许你推送任何项目,将'最小'项保持在h[0] ,然后弹出这个项目(和'冒泡'下一个最小项)是一个O(nlogn)操作。

所以,问问自己:

  • 我是否需要一直订购的整个清单? :使用一个有序结构(如Zope的B树包,如提到的由埃尔德武尔)

  • 或整个列表有序,但只在一天的随机插入工作后?:使用像你正在做的那样,或者像S.Lott的回答

  • 或者只是一些“最小”的物品? :使用heapq

sorted(myLists[key], key=mylists[key].get, reverse=True)

应该节省你一些时间,虽然不是很多。

我会研究使用不同的排序算法。 像Merge Sort这样的东西可能会起作用。 将列表分成较小的列表并单独排序。 然后循环。

伪代码:

list1 = []  // sorted separately
list2 = []  // sorted separately

// Recombine sorted lists
result = []
while (list1.hasMoreElements || list2.hasMoreElements):
   if (! list1.hasMoreElements):
       result.addAll(list2)
       break
   elseif (! list2.hasMoreElements):
       result.AddAll(list1)
       break

   if (list1.peek < list2.peek):
      result.add(list1.pop)
   else:
      result.add(list2.pop)

Glenn Maynard是正确的,这里排序的映射是合适的。 这是python的一个: http//wiki.zope.org/ZODB/guide/node6.html#SECTION000630000000000000000

我已经对原始方式和SLott的提议进行了一些快速分析。 在这两种情况下,每个场都需要5-10分钟。 实际的排序不是问题。 看起来大部分时间花费在投放数据和转换数据上。 此外,我的内存使用率暴涨 - 我的python超过350兆内存! 你确定你没有用完所有ram并分页到磁盘吗? 即使使用我的3年老式省电处理器笔记本电脑,我看到每个键的结果不到5-10分钟就有100万件。 我无法解释的是实际sort()调用的可变性。 我知道python sort在排序部分排序列表时非常好,所以也许他的列表在从原始数据到要排序的列表的转换中得到部分排序。

这是slott方法的结果:

done creating data
done transform.  elapsed: 16.5160000324
sorting one key slott's way takes 1.29699993134

这是获得这些结果的代码:

starttransform = time.time()
hits= [ (r['hits'],r['id']) for r in myList ]
endtransform = time.time()
print "done transform.  elapsed: " + str(endtransform - starttransform)
hits.sort()
endslottsort = time.time()
print "sorting one key slott's way takes " + str(endslottsort - endtransform)

现在是原始方法的结果,或者至少添加了一些检测的关闭版本:

done creating data
done transform.  elapsed: 8.125
about to get stuff to be sorted 
done getting data. elapsed time: 37.5939998627
about to sort key hits
done  sorting on key <hits> elapsed time: 5.54699993134

这是代码:

for k, v in myLists.iteritems():
    time1 = time.time()
    print "about to get stuff to be sorted "
    tobesorted = myLists[k].items()
    time2 = time.time()
    print "done getting data. elapsed time: " + str(time2-time1)
    print "about to sort key " + str(k) 
    mysorted[k] = tobesorted.sort( key=itemgetter(1))
    time3 = time.time()
    print "done  sorting on key <" + str(k) + "> elapsed time: " + str(time3-time2)

老实说,最好的方法是不使用Python。 如果性能是一个主要问题,请使用更快的语言。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM