Python:キーとしてのタプル/辞書、選択、並べ替え


104

たとえば、24個の青いバナナ、12個の緑のリンゴ、0個の青いイチゴなど、さまざまな色の果物がたくさんあるとします。それらをPythonのデータ構造に整理して、選択と並べ替えが簡単にできるようにしたいと思います。私のアイデアは、タプルをキーとしてそれらを辞書に入れることでした、例えば、

{ ('banana',    'blue' ): 24,
  ('apple',     'green'): 12,
  ('strawberry','blue' ): 0,
  ...
}

または辞書さえ、例えば、

{ {'fruit': 'banana',    'color': 'blue' }: 24,
  {'fruit': 'apple',     'color': 'green'}: 12,
  {'fruit': 'strawberry','color': 'blue' }: 0,
  ...
}

たとえば、すべての青い果実、またはすべての色のバナナのリストを取得したり、この辞書を果実の名前でソートしたりします。これをきれいに行う方法はありますか?

タプルをキーとして持つ辞書は、この状況を処理する適切な方法ではないかもしれません。

すべての提案を歓迎します!


26
データベースが必要なようです...
Adam Rosenfield

4
これらの値の異なるコレクションを調整しようとするのではなく、このデータをモデル化するためにクラスを定義するのが最善でしょう
Cuga

2
@AdamRosenfieldたぶん彼は1つを構築しています。
Falken教授、2012年

ディクショナリはハッシュ可能ではないので、ディクショナリである{'fruit': 'banana'、 'color': 'blue'}をキーとして使用できないため、2番目の構文では質問できません。別の辞書。TypeError:unhashable type: 'dict'が発生します。
epeleg 2017

回答:


147

個人的に、私がpythonについて気に入っている点の1つは、タプルとディクトの組み合わせです。ここにあるのは事実上2d配列(x =フルーツの名前とy =色)であり、私は通常、少なくとも何かのような、numpyまたはデータベースがより適切ではない場合に、2d配列を実装するためのタプルのディクトのサポーターです。。要するに、私はあなたが良いアプローチを持っていると思います。

いくつかの追加の作業を行わずに、dictをdictのキーとして使用できないことに注意してください。これは、あまり良い解決策ではありません。

つまり、namedtuple()も検討する必要があります。その方法でこれを行うことができます:

>>> from collections import namedtuple
>>> Fruit = namedtuple("Fruit", ["name", "color"])
>>> f = Fruit(name="banana", color="red")
>>> print f
Fruit(name='banana', color='red')
>>> f.name
'banana'
>>> f.color
'red'

これで、フルーツカウント辞書を使用できます。

>>> fruitcount = {Fruit("banana", "red"):5}
>>> fruitcount[f]
5

その他のトリック:

>>> fruits = fruitcount.keys()
>>> fruits.sort()
>>> print fruits
[Fruit(name='apple', color='green'), 
 Fruit(name='apple', color='red'), 
 Fruit(name='banana', color='blue'), 
 Fruit(name='strawberry', color='blue')]
>>> fruits.sort(key=lambda x:x.color)
>>> print fruits
[Fruit(name='banana', color='blue'), 
 Fruit(name='strawberry', color='blue'), 
 Fruit(name='apple', color='green'), 
 Fruit(name='apple', color='red')]

chmulligをエコーし​​て、1つの果物のすべての色のリストを取得するには、キーをフィルタリングする必要があります。

bananas = [fruit for fruit in fruits if fruit.name=='banana']

#senderleあなたは別の回答へのコメントとして書き込みましたが、「私の直感は、データベースはOPのニーズに対して過剰であるということです。」; したがって、namedtupleサブクラスを作成することをお勧めします。しかし、データを処理する独自のツールを備えたマイクロデータベースでない場合、クラスのインスタンスは他に何ですか?
eyquem

それらからサブリストを抽出できname='banana'ますか?
NicoSchlömer、2011

2
chmulligが指摘したように、キー、つまりbananas = filter(lambda fruit: fruit.name=='banana', fruits)やをフィルターする必要がありますbananas = [fruit for fruit in fruits if fruit.name=='banana']。これは、ネストされたdictが潜在的に効率的になる1つの方法です。すべては、データの使用を計画している方法にかかっています。
センダーレ、2011

名前付きタプルにさらにキーを追加しないと物事が簡単になりますか?新しい属性を追加すると思いますcount
openrijal

18

あなたの最良のオプションは、あなたが持っているものをモデル化するための単純なデータ構造を作成することです。次に、これらのオブジェクトを単純なリストに格納し、任意の方法で並べ替え/取得できます。

この場合、次のクラスを使用します。

class Fruit:
    def __init__(self, name, color, quantity): 
        self.name = name
        self.color = color
        self.quantity = quantity

    def __str__(self):
        return "Name: %s, Color: %s, Quantity: %s" % \
     (self.name, self.color, self.quantity)

次に、次のように、「フルーツ」インスタンスを作成してリストに追加します。

fruit1 = Fruit("apple", "red", 12)
fruit2 = Fruit("pear", "green", 22)
fruit3 = Fruit("banana", "yellow", 32)
fruits = [fruit3, fruit2, fruit1] 

シンプルなリストのfruits方がはるかに簡単で、混乱が少なく、管理も簡単です。

いくつかの使用例:

以下のすべての出力は、特定のコードスニペットを実行した後の結果です。

for fruit in fruits:
    print fruit

ソートされていないリスト:

ディスプレイ:

Name: banana, Color: yellow, Quantity: 32
Name: pear, Color: green, Quantity: 22
Name: apple, Color: red, Quantity: 12

名前のアルファベット順に並べ替え:

fruits.sort(key=lambda x: x.name.lower())

ディスプレイ:

Name: apple, Color: red, Quantity: 12
Name: banana, Color: yellow, Quantity: 32
Name: pear, Color: green, Quantity: 22

数量で並べ替え:

fruits.sort(key=lambda x: x.quantity)

ディスプレイ:

Name: apple, Color: red, Quantity: 12
Name: pear, Color: green, Quantity: 22
Name: banana, Color: yellow, Quantity: 32

色==赤:

red_fruit = filter(lambda f: f.color == "red", fruits)

ディスプレイ:

Name: apple, Color: red, Quantity: 12

17

データベース、dict of dicts、辞書リストの辞書、名前付きタプル(サブクラスです)、sqlite、冗長...私は自分の目を信じていませんでした。ほかに何か ?

「タプルをキーとして持つ辞書は、この状況を処理する適切な方法ではないかもしれません。」

「私の直感は、データベースはOPのニーズに対して過剰であるということです。」

うん!思った

だから、私の意見では、タプルのリストで十分です:

from operator import itemgetter

li = [  ('banana',     'blue'   , 24) ,
        ('apple',      'green'  , 12) ,
        ('strawberry', 'blue'   , 16 ) ,
        ('banana',     'yellow' , 13) ,
        ('apple',      'gold'   , 3 ) ,
        ('pear',       'yellow' , 10) ,
        ('strawberry', 'orange' , 27) ,
        ('apple',      'blue'   , 21) ,
        ('apple',      'silver' , 0 ) ,
        ('strawberry', 'green'  , 4 ) ,
        ('banana',     'brown'  , 14) ,
        ('strawberry', 'yellow' , 31) ,
        ('apple',      'pink'   , 9 ) ,
        ('strawberry', 'gold'   , 0 ) ,
        ('pear',       'gold'   , 66) ,
        ('apple',      'yellow' , 9 ) ,
        ('pear',       'brown'  , 5 ) ,
        ('strawberry', 'pink'   , 8 ) ,
        ('apple',      'purple' , 7 ) ,
        ('pear',       'blue'   , 51) ,
        ('chesnut',    'yellow',  0 )   ]


print set( u[1] for u in li ),': all potential colors'
print set( c for f,c,n in li if n!=0),': all effective colors'
print [ c for f,c,n in li if f=='banana' ],': all potential colors of bananas'
print [ c for f,c,n in li if f=='banana' and n!=0],': all effective colors of bananas'
print

print set( u[0] for u in li ),': all potential fruits'
print set( f for f,c,n in li if n!=0),': all effective fruits'
print [ f for f,c,n in li if c=='yellow' ],': all potential fruits being yellow'
print [ f for f,c,n in li if c=='yellow' and n!=0],': all effective fruits being yellow'
print

print len(set( u[1] for u in li )),': number of all potential colors'
print len(set(c for f,c,n in li if n!=0)),': number of all effective colors'
print len( [c for f,c,n in li if f=='strawberry']),': number of potential colors of strawberry'
print len( [c for f,c,n in li if f=='strawberry' and n!=0]),': number of effective colors of strawberry'
print

# sorting li by name of fruit
print sorted(li),'  sorted li by name of fruit'
print

# sorting li by number 
print sorted(li, key = itemgetter(2)),'  sorted li by number'
print

# sorting li first by name of color and secondly by name of fruit
print sorted(li, key = itemgetter(1,0)),'  sorted li first by name of color and secondly by name of fruit'
print

結果

set(['blue', 'brown', 'gold', 'purple', 'yellow', 'pink', 'green', 'orange', 'silver']) : all potential colors
set(['blue', 'brown', 'gold', 'purple', 'yellow', 'pink', 'green', 'orange']) : all effective colors
['blue', 'yellow', 'brown'] : all potential colors of bananas
['blue', 'yellow', 'brown'] : all effective colors of bananas

set(['strawberry', 'chesnut', 'pear', 'banana', 'apple']) : all potential fruits
set(['strawberry', 'pear', 'banana', 'apple']) : all effective fruits
['banana', 'pear', 'strawberry', 'apple', 'chesnut'] : all potential fruits being yellow
['banana', 'pear', 'strawberry', 'apple'] : all effective fruits being yellow

9 : number of all potential colors
8 : number of all effective colors
6 : number of potential colors of strawberry
5 : number of effective colors of strawberry

[('apple', 'blue', 21), ('apple', 'gold', 3), ('apple', 'green', 12), ('apple', 'pink', 9), ('apple', 'purple', 7), ('apple', 'silver', 0), ('apple', 'yellow', 9), ('banana', 'blue', 24), ('banana', 'brown', 14), ('banana', 'yellow', 13), ('chesnut', 'yellow', 0), ('pear', 'blue', 51), ('pear', 'brown', 5), ('pear', 'gold', 66), ('pear', 'yellow', 10), ('strawberry', 'blue', 16), ('strawberry', 'gold', 0), ('strawberry', 'green', 4), ('strawberry', 'orange', 27), ('strawberry', 'pink', 8), ('strawberry', 'yellow', 31)]   sorted li by name of fruit

[('apple', 'silver', 0), ('strawberry', 'gold', 0), ('chesnut', 'yellow', 0), ('apple', 'gold', 3), ('strawberry', 'green', 4), ('pear', 'brown', 5), ('apple', 'purple', 7), ('strawberry', 'pink', 8), ('apple', 'pink', 9), ('apple', 'yellow', 9), ('pear', 'yellow', 10), ('apple', 'green', 12), ('banana', 'yellow', 13), ('banana', 'brown', 14), ('strawberry', 'blue', 16), ('apple', 'blue', 21), ('banana', 'blue', 24), ('strawberry', 'orange', 27), ('strawberry', 'yellow', 31), ('pear', 'blue', 51), ('pear', 'gold', 66)]   sorted li by number

[('apple', 'blue', 21), ('banana', 'blue', 24), ('pear', 'blue', 51), ('strawberry', 'blue', 16), ('banana', 'brown', 14), ('pear', 'brown', 5), ('apple', 'gold', 3), ('pear', 'gold', 66), ('strawberry', 'gold', 0), ('apple', 'green', 12), ('strawberry', 'green', 4), ('strawberry', 'orange', 27), ('apple', 'pink', 9), ('strawberry', 'pink', 8), ('apple', 'purple', 7), ('apple', 'silver', 0), ('apple', 'yellow', 9), ('banana', 'yellow', 13), ('chesnut', 'yellow', 0), ('pear', 'yellow', 10), ('strawberry', 'yellow', 31)]   sorted li first by name of color and secondly by name of fruit

1
こんにちは、私はあなたのソリューションが好きですが、操作の複雑さの問題には対処していません。すべての検索タイプは、リストのサイズで線形(O(n))です。一方、OPはいくつかのアクションを他のアクションよりも高速にしたいと思ったのは理にかなっています(たとえば、黄色のバナナの数を取得することは、O(1)で可能になると私が期待するものです。)
epeleg

13

この場合、辞書はおそらく使用すべきものではありません。より完全な機能を備えたライブラリは、より良い代替手段となります。おそらく実際のデータベースでしょう。最も簡単なのはsqliteです。ファイル名の代わりに文字列 ':memory:'を渡すことで、すべてをメモリに保持できます。

このパスを続行したい場合は、キーまたは値の追加の属性を使用して実行できます。ただし、辞書を別の辞書のキーにすることはできませんが、タプルはできます。ドキュメントは何が許されるかを説明しています。これは不変オブジェクトである必要があります。これには、文字列と数値のみを含む文字列、数値、およびタプル(およびこれらのタイプのみを再帰的に含むより多くのタプル...)が含まれます。

最初の例をd = {('apple', 'red') : 4}で実行できますが、必要なものを照会するのは非常に困難です。あなたはこのようなことをする必要があるでしょう:

#find all apples
apples = [d[key] for key in d.keys() if key[0] == 'apple']

#find all red items
red = [d[key] for key in d.keys() if key[1] == 'red']

#the red apple
redapples = d[('apple', 'red')]

4
より大きなスケールではデータベースが(明らかに!)進むための最良の方法であるため、私はこの回答に反対票を投じませんでした。しかし、私の直感は、データベースはOPのニーズに対して過剰であるということです。たぶんそれは反対投票を説明しますか?
sendle

4

キーをタプルとして使用する場合、指定された2番目のコンポーネントでキーをフィルターにかけ、それを並べ替えるだけです。

blue_fruit = sorted([k for k in data.keys() if k[1] == 'blue'])
for k in blue_fruit:
  print k[0], data[k] # prints 'banana 24', etc

コンポーネントが自然な順序を持っている場合、タプルは自然な順序を持っているので、ソートは機能します。

キーをかなり本格的なオブジェクトとして使用する場合は、でフィルタリングするだけk.color == 'blue'です。

dictsをキーとして実際に使用することはできませんが、のような最も単純なクラスを作成class Foo(object): passし、その場で属性を追加できます。

k = Foo()
k.color = 'blue'

これらのインスタンスはdictキーとして機能しますが、その可変性に注意してください!


3

エントリが他の辞書のリストである辞書を持つことができます:

fruit_dict = dict()
fruit_dict['banana'] = [{'yellow': 24}]
fruit_dict['apple'] = [{'red': 12}, {'green': 14}]
print fruit_dict

出力:

{'banana':[{'yellow':24}]、 'apple':[{'red':12}、{'green':14}]}

編集:eumiroが指摘したように、辞書の辞書を使用できます。

fruit_dict = dict()
fruit_dict['banana'] = {'yellow': 24}
fruit_dict['apple'] = {'red': 12, 'green': 14}
print fruit_dict

出力:

{'バナナ':{'黄色':24}、 'りんご':{'緑':14、 '赤':12}}


2
辞書のリストの辞書?多分辞書の辞書で十分でしょうか?
eumiro

@eumiro:ありがとう、そうです、そしてそれは私の最初のアイデアでした。しかし、元の例をコーディングしながら、それを辞書リストの辞書に変えました。dicts of dictsの例を追加しました。
GreenMatt

ネストされた辞書は混乱する傾向があります。私の答えを見てください
Cuga

@Cuga:私は、dictsの口述などが混乱する可能性があることに同意します。私は、@ Nicoの質問に答えるための例示的な例を提供しています。
GreenMatt 2011

申し訳ありません:私はあなたの解決策が間違っていることを意味するつもりはありませんでした。それは明らかに機能し、状況によっては理想的なものになる可能性があります。私は状況について私の見解を共有したかった。
CUGA

2

このタイプのデータは、トライのようなデータ構造から効率的に引き出されます。また、高速ソートも可能です。ただし、メモリ効率はそれほど高くない場合があります。

従来のトライでは、単語の各文字をノードとしてツリーに格納します。しかし、あなたの場合、「アルファベット」は異なります。文字ではなく文字列を格納しています。

次のようになります。

root:                Root
                     /|\
                    / | \
                   /  |  \     
fruit:       Banana Apple Strawberry
              / |      |     \
             /  |      |      \
color:     Blue Yellow Green  Blue
            /   |       |       \
           /    |       |        \
end:      24   100      12        0

このリンクを参照してください:Pythonでのトライ


2

2つのキーを個別に使用したいので、2つの選択肢があります。

  1. 2枚のとしてdictsと冗長データを格納{'banana' : {'blue' : 4, ...}, .... }して{'blue': {'banana':4, ...} ...}。その後、検索と並べ替えは簡単ですが、辞書を一緒に変更することを確認する必要があります。

  2. ただ1つの辞書に保存してから、それらを反復する関数を記述します。例:

    d = {'banana' : {'blue' : 4, 'yellow':6}, 'apple':{'red':1} }
    
    blueFruit = [(fruit,d[fruit]['blue']) if d[fruit].has_key('blue') for fruit in d.keys()]

私の回答のコードが正しい形式で表示されない理由を理解できません。編集して最後の2行をコードとしてマークしようとしましたが、機能しません!
highBandWidth

1
番号付きリストを作成し、パーサーがコード(4つのスペースをインデントしたもの)をそのリストの2番目のアイテムの続きとして解釈しています。コードをさらに4スペースインデントして合計8にすると、パーサーはコードをコードとして認識し、正しくフォーマットします。
センデル
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.