2017-03-04 64 views
2

我有一個非常大的字典,其形式爲{(Tuple) : [int, int]}。例如,dict = {(1.0, 2.1):[2,3], (2.0, 3.1):[1,4],...}無法放入內存。數據結構:Top K排序字典鍵值

我只對這個字典中按照每個鍵的值的第一個元素排序的頂部K值感興趣。如果有一個數據結構可以讓我只保留最大的K個鍵值對?作爲一個例子,我只需要我的字典中的3個值。我可以放入以下鍵值對; (1.0, 2.1):[2,3], (2.0, 3.1):[1,4], (3.1, 4.2):[8,0], (4.3, 4.1):[1,1]和我的字典將是:(3.1, 4.2):[8,0], (1.0, 2.1):[2,3], (2.0, 3.1):[1,4](如果鍵值對具有相同的第一個元素,則會檢查第二個元素,並且將保留基於第二個元素的最大鍵值對)

+0

如何創建這本詞典沒有?你想在創建時或創建字典之後做到這一點? – Kasramvd

+0

如果你不反對使用numpy它有'partition'和'argpartition',它可以在O(n)中找到頂部或底部的k。 –

+0

對不起,我應該解釋一下,我無法將我的字典保存在內存中。 – Black

回答

0
import heapq 


class OnlyKDict(object): 

    def __init__(self,K,key=lambda x:x): 
     self.data = [] 
     self.dictionary = {} 
     self.key=key   # Lambda function for the comparator 
     self.K = K   # How many values to keep in dictionary 

    def push(self,item): 
     heapq.heappush(self.data,(self.key(item),item)) 
     self.dictionary[item[0]]=item[1] 
     if len(self.data)>self.K: #Size greater than k? pop minimum from heap and dict. 
      item = self.pop()  #This ensure only k largest are there. 
      self.dictionary.pop(item[0],None) 

    def pop(self): 
     return heapq.heappop(self.data)[1] 

    def __getitem__(self,key): 
     return self.dictionary[key] 

    def __setitem__(self,key,value): 
     if self.dictionary.has_key(key): 
      self.dictionary[key] = value #If key present update value 
     else: 
      self.push((key,value)) ##Else push key and value as a tuple 

h = OnlyKDict(8,lambda x:x[0][1] if x[0][1]==x[0][0] else x[0][0]) ##Compare 2nd value if both equal else compare 1st value only. 

for i in xrange(10): 
    h[(i,i)] = [i,i] 

print h.dictionary 

輸出:{(5,5):[5,5],(6,6):[6,6],(4,4):[4,4],(7,7 ):[7,7], (9,9):[9,9],(8,8):[8,8],(2,2):[2,2],(3,3) :[3,3]}

你可以看到只有前8個值存儲在這裏。

主要的東西取自heapq with custom compare predicate

我們所做的是創建我們的自定義堆類,該類需要一個關鍵參數,我們指定要排序的值。

接下來是每當這個尺寸大於8時,我們彈出最小項目。這確保我們始終只有最多8個值。

+0

爲什麼不用['heapq.nlargest'](https://docs.python.org/3/library/heapq.html#heapq.nlargest)與'key = ...'? –

+0

不,我們只保留8個值,因爲這是要求。接下來他還想要返回一本字典。這就是爲什麼make_dict函數.. –

+0

但是,你所說的是對的 –

0

如果您的數據不適合內存,您需要特別關注它的存儲方式。它是在數據庫,平面文件,csv文件,JSON還是什麼?

如果是「矩形」文件格式,那麼只需使用標準* nix排序實用程序,然後在第一行k行中讀取即可。

0

這裏是一個定製的OrderedDict這使N個最大鍵爲您提供:

from collections import OrderedDict 
from operator import itemgetter 


class LimitedSizeOrderedDict(OrderedDict): 
    def __init__(self, *args, **kwds): 
     self.maxlen = kwds.pop("maxlen", None) 
     if args: 
      try: 
       top_n = sorted(*args, key=itemgetter(0, 0))[-self.maxlen:] 
       self.min_key = top_n[0][0] 
      except TypeError: 
       raise Exception("keys should be in tuple format") 
     else: 
      self.min_key = (float("inf"), 0) 
     super(LimitedSizeOrderedDict, self).__init__(top_n, **kwds) 

    def __setitem__(self, key, value): 
     if self._check_size(): 
      OrderedDict.__setitem__(self, key, value) 
      if key[0] < self.min_key[0]: 
       self.min_key = key 
     elif key[0] > self.min_key[0]: 
      self.pop(self.min_key) 
      OrderedDict.__setitem__(self, key, value) 
      self.min_key = min(self, key=itemgetter(0)) 

    def _check_size(self): 
     if self.maxlen is not None: 
      if len(self) < self.maxlen: 
       return True 
      return False 
     return True 

演示:

In [2]: a = LimitedSizeOrderedDict([((7,2),3), ((2, 5), 3), ((6, 0), 1)], maxlen= 2) 

In [3]: a 
Out[3]: LimitedSizeOrderedDict([((6, 0), 1), ((7, 2), 3)]) 

In [4]: a[(12, 5)] = 10 

In [5]: a 
Out[5]: LimitedSizeOrderedDict([((7, 2), 3), ((12, 5), 10)]) 

In [6]: a[(10, 5)] = 9 

In [7]: a 
Out[7]: LimitedSizeOrderedDict([((12, 5), 10), ((10, 5), 9)]) 

In [8]: a[(0, 5)] = 9 

In [9]: a 
Out[9]: LimitedSizeOrderedDict([((12, 5), 10), ((10, 5), 9)]) 
+0

是否有'top_n = sorted(args,itemgetter(0))[:self.maxlen]'意思是我必須閱讀我所有的數據? – Black

+0

@Black不,如果您在創建時將任何項目傳遞到字典,它將在初始化時返回前N個項目。 – Kasramvd

+0

@Black簽出更新以獲得更全面的答案。 – Kasramvd