Python: Tuples / พจนานุกรมเป็นคีย์เลือกจัดเรียง


105

สมมติว่าฉันมีผลไม้หลายสีเช่นกล้วยสีฟ้า 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,
    # ...
}

ฉันต้องการเรียกดูรายการผลไม้สีฟ้าทั้งหมดหรือกล้วยทุกสีหรือจัดเรียงพจนานุกรมตามชื่อผลไม้ มีวิธีทำอย่างไรให้สะอาด?

อาจเป็นไปได้ว่าพจนานุกรมที่มีทูเปิลเป็นคีย์ไม่ใช่วิธีที่เหมาะสมในการจัดการกับสถานการณ์นี้

ข้อเสนอแนะทั้งหมดยินดีต้อนรับ!


27
ดูเหมือนว่าคุณต้องการฐานข้อมูล ...
Adam Rosenfield

4
คุณควรกำหนด clsas เพื่อสร้างแบบจำลองข้อมูลนี้ดีที่สุดแทนที่จะพยายามประสานคอลเล็กชันต่างๆของค่าเหล่านี้
Cuga

2
@AdamRosenfield บางทีเขากำลังสร้าง
ศ. Falken

แค่อยากจะเพิ่มว่าพจนานุกรมนั้นไม่สามารถล้างได้ดังนั้นจึงไม่สามารถใช้ไวยากรณ์ที่สองที่คุณถามถึงได้เนื่องจาก {'fruit': 'banana', 'color': 'blue'} ซึ่งเป็นพจนานุกรมไม่สามารถใช้เป็นคีย์ได้ สำหรับพจนานุกรมอื่น มันจะทำให้เกิด TypeError: unhashable type: 'dict'
epeleg

คำตอบ:


147

โดยส่วนตัวแล้วสิ่งหนึ่งที่ฉันชอบเกี่ยวกับ python คือการรวม tuple-dict สิ่งที่คุณมีอยู่ที่นี่คืออาร์เรย์ 2d อย่างมีประสิทธิภาพ (โดยที่ x = ชื่อผลไม้และ y = color) และโดยทั่วไปฉันเป็นผู้สนับสนุนคำสั่งของ tuples สำหรับการใช้อาร์เรย์ 2d อย่างน้อยก็เมื่อมีบางอย่างเช่นnumpyหรือฐานข้อมูลไม่เหมาะสม . ในระยะสั้นฉันคิดว่าคุณมีแนวทางที่ดี

โปรดทราบว่าคุณไม่สามารถใช้คำสั่งเป็นกุญแจในการเขียนตามคำสั่งได้โดยไม่ต้องทำงานพิเศษนั่นจึงไม่ใช่วิธีแก้ปัญหาที่ดีนัก

ที่กล่าวว่าคุณควรพิจารณาnametuple ()ด้วย ด้วยวิธีนี้คุณสามารถทำได้:

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

การสะท้อน chmullig เพื่อให้ได้รายชื่อผลไม้ทุกสีคุณจะต้องกรองคีย์เช่น

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

#senderle คุณเขียนเป็นความคิดเห็นสำหรับคำตอบอื่น "แต่ความรู้สึกในใจของฉันคือฐานข้อมูลมีมากเกินไปสำหรับความต้องการของ OP;"; ดังนั้นคุณจึงต้องการสร้างคลาสย่อยที่มีชื่อทูเพิล แต่จะมีอินสแตนซ์ของคลาสอะไรอีกบ้างหากไม่ใช่ฐานข้อมูลขนาดเล็กที่มีเครื่องมือของตนเองในการประมวลผลข้อมูล
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']. นี่เป็นวิธีหนึ่งที่ทำให้คำสั่งที่ซ้อนกันมีประสิทธิภาพมากขึ้น ทั้งหมดนี้ขึ้นอยู่กับวิธีที่คุณวางแผนจะใช้ข้อมูล
ส่ง

จะไม่เพิ่มคีย์เพิ่มเติมใน tuple ที่มีชื่อทำให้สิ่งต่างๆง่ายขึ้น? ฉันจะบอกว่าเพิ่มแอตทริบิวต์ใหม่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 บางทีนั่นอาจอธิบายถึงการโหวตลดลง?
ส่ง

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'ด้วยปุ่มเป็นวัตถุค่อนข้างเต็มเปี่ยมคุณเพียงแค่กรองตาม

คุณไม่สามารถใช้ dicts เป็นคีย์ได้จริง ๆ แต่คุณสามารถสร้างคลาสที่ง่ายที่สุดเช่นclass Foo(object): passและเพิ่มคุณสมบัติใด ๆ ลงไปได้ทันที:

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

อินสแตนซ์เหล่านี้สามารถใช้เป็นคีย์คำสั่งได้ แต่ระวังการเปลี่ยนแปลง!


3

คุณสามารถมีพจนานุกรมที่รายการเป็นรายการพจนานุกรมอื่น ๆ :

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

เอาท์พุต:

{'กล้วย': [{'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: ขอบคุณคุณพูดถูกและนั่นเป็นความคิดดั้งเดิมของฉัน อย่างไรก็ตามฉันได้เปลี่ยนมันเป็นรายการของคำสั่งในขณะที่เขียนโค้ดตัวอย่างต้นฉบับ ฉันได้เพิ่มตัวอย่างการเขียนตามคำบอกแล้ว
GreenMatt

พจนานุกรมที่ซ้อนกันมักจะสับสน โปรดดูคำตอบของฉัน
Cuga

@Cuga: ฉันยอมรับว่าคำสั่งของคำสั่ง ฯลฯ อาจทำให้สับสนได้ ฉันแค่ยกตัวอย่างประกอบเพื่อตอบคำถามของ @ Nico ตามที่ถาม
GreenMatt

ฉันขอโทษ: ฉันไม่ได้หมายความว่าการแก้ปัญหาของคุณผิด มันใช้งานได้อย่างชัดเจนและในบางสถานการณ์อาจเป็นสิ่งที่ดีที่สุด ฉันต้องการแบ่งปันสถานการณ์ของฉัน
Cuga

2

ข้อมูลประเภทนี้ดึงมาจากโครงสร้างข้อมูลแบบ Trie ได้อย่างมีประสิทธิภาพ นอกจากนี้ยังช่วยให้สามารถจัดเรียงได้อย่างรวดเร็ว ประสิทธิภาพของหน่วยความจำอาจไม่ดีเท่าไหร่

Trie แบบดั้งเดิมเก็บตัวอักษรของคำแต่ละตัวเป็นโหนดในแผนภูมิ แต่ในกรณีของคุณ "ตัวอักษร" ของคุณต่างออกไป คุณกำลังจัดเก็บสตริงแทนอักขระ

อาจมีลักษณะดังนี้:

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

ดูลิงค์นี้: trie ใน python


2

คุณต้องการใช้สองปุ่มแยกกันดังนั้นคุณจึงมีสองทางเลือก:

  1. เก็บข้อมูลเกินความจำเป็นด้วยสอง dicts เป็นและ{'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 ช่องว่าง) เป็นความต่อเนื่องของรายการที่สองของรายการนั้น เยื้องรหัสอีก 4 ช่องว่างรวมเป็น 8 ช่องและตัวแยกวิเคราะห์จะจดจำรหัสเป็นรหัสและจัดรูปแบบให้ถูกต้อง
ส่ง
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.