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.