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
이러한 값의 다른 컬렉션을 조정하는 것보다이 데이터를 모델링하기 위해 clsas를 정의하는 것이 가장 좋습니다
— Cuga

2
@AdamRosenfield는 아마도 그가 하나를 짓고있을 것입니다.
— Prof. Falken

사전은 해시 할 수 없기 때문에 두 번째 구문은 가능하지 않습니다. 사전 인 { 'fruit': 'banana', 'color': 'blue'}는 키로 사용할 수 없습니다. 다른 사전을 위해. TypeError : unhashable type : 'dict'가 발생합니다.
— epeleg

답변:


147

개인적으로 제가 파이썬에 대해 좋아하는 것 중 하나는 튜플-딕트 조합입니다. 여기에있는 것은 사실상 2d 배열 (x = 과일 이름 및 y = 색상)이며, 저는 일반적으로 적어도 비슷한 numpy것이나 데이터베이스가 더 적절하지 않은 경우 2d 배열을 구현하기위한 튜플 사전의 지지자입니다. . 요컨대, 좋은 접근 방식을 가지고 있다고 생각합니다.

추가 작업을하지 않고는 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 사전을 사용할 수 있습니다.

>>> 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')]

Echoing chmullig, 한 과일의 모든 색상 목록을 얻으려면 키를 필터링해야합니다.

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

#senderle 당신은 다른 답변에 대한 코멘트로 썼습니다. "하지만 내 직감은 데이터베이스가 OP의 요구에 비해 과도하다는 것입니다."; 따라서 namedtuple 하위 클래스를 만드는 것을 선호합니다. 그러나 데이터를 처리하는 자체 도구가있는 마이크로 데이터베이스가 아니라면 클래스의 인스턴스는 또 무엇입니까?
— eyquem

하위 목록을 추출 할 수 name='banana'있습니까?
— Nico Schlömer

2
chmullig가 지적했듯이 키, 즉 bananas = filter(lambda fruit: fruit.name=='banana', fruits)또는 bananas = [fruit for fruit in fruits if fruit.name=='banana']. 이것은 중첩 된 딕셔너리가 잠재적으로 더 효율적인 방법 중 하나입니다. 모든 것이 데이터 사용 계획에 달려 있습니다.
— senderle

명명 된 튜플에 더 많은 키를 추가하면 일이 더 쉬워지지 않을까요? 나는 새로운 속성을 추가 말하고 싶지만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)

그런 다음 다음과 같이 간단히 "Fruit"인스턴스를 생성하고 목록에 추가 할 수 있습니다.

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

데이터베이스, 사전 사전, 사전 목록 사전, 명명 된 튜플 (하위 클래스), 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의 요구에 과도하다는 것입니다. 아마도 그것이 반대표를 설명할까요?
— senderle

4

키를 튜플로 사용하면 주어진 두 번째 구성 요소로 키를 필터링하고 정렬하기 만하면됩니다.

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'.

딕셔너리를 키로 사용할 수는 없지만, 다음과 같은 가장 간단한 클래스를 만들고 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

산출:

{ 'banana': { 'yellow': 24}, 'apple': { 'green': 14, 'red': 12}}


2
사전 목록의 사전? 사전 사전이면 충분할까요?
— 음미로

@eumiro : 고마워, 네 말이 맞아, 그게 내 원래 생각이었다. 그러나 원래 예제를 코딩하는 동안 dict 목록의 dict로 전환했습니다. dicts 예제를 추가했습니다.
— GreenMatt

중첩 된 사전은 혼란스러운 경향이 있습니다. 내 대답을 참조하십시오
— Cuga

@Cuga : dicts의 dicts 등이 혼란 스러울 수 있다는 데 동의합니다. 나는 단지 @Nico의 질문에 대답하는 예시를 제공하고 있습니다.
— GreenMatt

나는 사과한다 : 나는 당신의 해결책이 틀렸다는 것을 암시하려는 것이 아닙니다. 그것은 명확하게 작동하며 어떤 상황에서는 이상적인 것이 될 수 있습니다. 나는 상황에 대한 나의 견해를 공유하고 싶었다.
— Cuga

2

이러한 유형의 데이터는 Trie와 유사한 데이터 구조에서 효율적으로 가져옵니다. 또한 빠른 정렬이 가능합니다. 하지만 메모리 효율성은 그리 크지 않을 수 있습니다.

전통적인 트라이는 단어의 각 문자를 트리의 노드로 저장합니다. 그러나 귀하의 경우 "알파벳"은 다릅니다. 문자 대신 문자열을 저장하고 있습니다.

다음과 같이 보일 수 있습니다.

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

이 링크를 참조하십시오 : python에서 trie


2

두 개의 키를 독립적으로 사용하려고하므로 두 가지 선택 사항이 있습니다.

  1. 두 개의 딕셔너리를 {'banana' : {'blue' : 4, ...}, .... }및 로 중복 저장합니다 {'blue': {'banana':4, ...} ...}. 그러면 검색 및 정렬이 쉽지만 사전을 함께 수정해야합니다.

  2. 하나의 사전 만 저장 한 다음 반복하는 함수를 작성하십시오. 예 :

    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()]

내 대답의 코드가 올바른 형식으로 표시되지 않는 이유를 알 수 없습니다. 마지막 두 줄을 코드로 편집하고 표시하려고 시도했지만 작동하지 않습니다!
— highBandWidth

1
번호가 매겨진 목록을 만들고 파서가 코드 (공백 4 개 들여 쓰기)를 해당 목록의 두 번째 항목의 연속으로 해석합니다. 총 8 개를 위해 코드를 4 칸 더 들여 쓰면 파서가 코드를 코드로 인식하고 올바르게 형식을 지정합니다.
— senderle
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.