2013-04-14 12 views
11

私は多次元辞書を持っています。私は、キー:キーのペアで値を取得したいと思います。最初のキーが存在しなければ 'NA'を返します。すべてのサブ辞書には同じキーがあります。多次元dictを持つPython dict.get()

d = { 'a': {'j':1,'k':2}, 
     'b': {'j':2,'k':3}, 
     'd': {'j':1,'k':3} 
    } 

は、私はそれがそうでなければリターン「NA」が存在した場合、サブ辞書を取得するためにd.get('c','NA')を使用することができます知っているが、私は本当に唯一のサブ辞書から一つの値を必要としています。もし存在すればd.get('c['j']','NA')のようなことをしたいと思います。

ここでは、トップレベルのキーが存在するかどうかを確認し、存在する場合は変数にサブ値を割り当てます。存在しない場合は 'NA'を割り当てます。しかし、私はこれを約500k回実行しており、他の場所から各トップレベルキーに関する他の情報を取得/生成しており、これを少しスピードアップしようとしています。

答えて

20

どの程度

d.get('a', {'j': 'NA'})['j'] 

ないすべてsubdictsが作成された同一のオブジェクトを削減するために、j鍵を持って、その後

d.get('a', {}).get('j', 'NA') 

 

場合、あなたは

class DefaultNASubdict(dict): 
    class NADict(object): 
     def __getitem__(self, k): 
      return 'NA' 

    NA = NADict() 

    def __missing__(self, k): 
     return self.NA 

nadict = DefaultNASubdict({ 
       'a': {'j':1,'k':2}, 
       'b': {'j':2,'k':3}, 
       'd': {'j':1,'k':3} 
      }) 

print nadict['a']['j'] # 1 
print nadict['b']['j'] # 2 
print nadict['c']['j'] # NA 

 

のようなものを考案することができます

同じアイデアdefaultdictを使用して:

import collections 

class NADict(object): 
    def __getitem__(self, k): 
     return 'NA' 

    @staticmethod 
    def instance(): 
     return NADict._instance 

NADict._instance = NADict() 


nadict = collections.defaultdict(NADict.instance, { 
       'a': {'j':1,'k':2}, 
       'b': {'j':2,'k':3}, 
       'd': {'j':1,'k':3} 
      }) 
+0

一見、すなわち' defaultdict(ラムダ取得する別の方法:defaultdictを(ラムダ: 'NA') ) ' – mtadd

+0

もちろん、' NADict'とその共有インスタンスを返す関数が必要です。私は例を追加します。 –

+0

@mtadd:アイデアはすべての誤ったルックアップで新しいdict/defaultdictを作成しないことでした。 –

2

ではなくネストされたdictオブジェクトの階層、あなたは、そのキーの階層を通るパスを表すタプルある1つの辞書を使用することができます。

In [34]: d2 = {(x,y):d[x][y] for x in d for y in d[x]} 

In [35]: d2 
Out[35]: 
{('a', 'j'): 1, 
('a', 'k'): 2, 
('b', 'j'): 2, 
('b', 'k'): 3, 
('d', 'j'): 1, 
('d', 'k'): 3} 

In [36]: timeit [d[x][y] for x,y in d2.keys()] 
100000 loops, best of 3: 2.37 us per loop 

In [37]: timeit [d2[x] for x in d2.keys()] 
100000 loops, best of 3: 2.03 us per loop 

この方法は、約15%速くなっているように見えます。あなたはまだ、デフォルト値でgetメソッドを使用することができます。

In [38]: d2.get(('c','j'),'NA') 
Out[38]: 'NA' 
4

ここでレベルの任意の数を入れ子に、普通の辞書でそれを行うための簡単で効率的な方法です:

d = {'a': {'j': 1, 'k': 2}, 
    'b': {'j': 2, 'k': 3}, 
    'd': {'j': 1, 'k': 3}, 
    } 

def chained_get(dct, *keys): 
    SENTRY = object() 
    def getter(level, key): 
     return 'NA' if level is SENTRY else level.get(key, SENTRY) 
    return reduce(getter, keys, dct) 

print chained_get(d, 'a', 'j') # 1 
print chained_get(d, 'b', 'k') # 3 
print chained_get(d, 'k', 'j') # NA 

またかもしれません再帰的に行われます:

def chained_get(dct, *keys): 
    SENTRY = object() 
    def getter(level, keys): 
     return (level if keys[0] is SENTRY else 
        'NA' if level is SENTRY else 
         getter(level.get(keys[0], SENTRY), keys[1:])) 
    return getter(dct, keys+(SENTRY,)) 

このように行う方法は、前者ほど効率的ではありませんが、

0

多次元dictの例(2回使用getメソッド)は既に提供された実装のためのcollections.defaultdict` `で

d.get('a', {}).get('j')