กรองตามคำบอกเพื่อให้มีเฉพาะบางคีย์หรือไม่


496

ฉันมีรายการdictที่มีทั้งรายการ ฉันสนใจเพียงบางส่วนเท่านั้น มีวิธีง่าย ๆ ในการตัดออกอื่น ๆ ทั้งหมดหรือไม่


การบอกว่าคีย์ประเภทใด (จำนวนเต็มสตริงวันที่วัตถุใด ๆ ) และทำให้ทราบว่ามีการทดสอบอย่างง่าย (สตริง, regex, รายชื่อสมาชิกหรือความไม่เท่าเทียมของตัวเลข) เพื่อตรวจสอบว่ามีคีย์ใดเข้าหรือออก หรืออื่น ๆ เราจำเป็นต้องเรียกใช้ฟังก์ชันใด ๆ เพื่อพิจารณาว่า
smci

@smci คีย์สตริง อย่าคิดว่ามันจะเกิดขึ้นกับฉันที่ฉันสามารถใช้สิ่งอื่นได้ ฉันได้รับการเข้ารหัสใน JS และ PHP มาเป็นเวลานาน ...
14116

คำตอบ:


656

การสร้าง dict ใหม่:

dict_you_want = { your_key: old_dict[your_key] for your_key in your_keys }

ใช้ความเข้าใจในพจนานุกรม

หากคุณใช้เวอร์ชันที่ไม่มีเวอร์ชัน (เช่น Python 2.6 และรุ่นก่อนหน้า) ให้สร้างdict((your_key, old_dict[your_key]) for ...)ขึ้นมา มันเหมือนกันแม้ว่าจะไม่เป็นที่น่าพอใจ

โปรดทราบว่าสิ่งนี้แตกต่างจากเวอร์ชันของ jnnnnn ที่มีประสิทธิภาพที่มั่นคง (ขึ้นอยู่กับจำนวนของ your_keys) สำหรับold_dicts ทุกขนาด ทั้งในเรื่องของความเร็วและความจำ เนื่องจากนี่เป็นนิพจน์ตัวสร้างจึงประมวลผลทีละรายการและจะไม่ตรวจสอบรายการทั้งหมดของ old_dict

การลบทุกอย่างในสถานที่:

unwanted = set(keys) - set(your_dict)
for unwanted_key in unwanted: del your_dict[unwanted_key]

8
"ใช้ความเข้าใจในพจนานุกรมหากคุณใช้รุ่นที่ไม่มีเวอร์ชัน" == เวอร์ชัน <= 2.6
getekha

8
โยน KeyError หากหนึ่งในคีย์ตัวกรองไม่ปรากฏใน old_dict ฉันขอแนะนำให้ {K: d [k] สำหรับ k ในตัวกรอง k งถ้า}
ปีเตอร์กิบสัน

1
@PeterGibson ใช่ถ้าเป็นส่วนหนึ่งของข้อกำหนดคุณต้องทำอะไรเกี่ยวกับมัน ไม่ว่าจะเป็นการวางกุญแจอย่างเงียบ ๆ การเพิ่มค่าเริ่มต้นหรืออย่างอื่นก็ขึ้นอยู่กับสิ่งที่คุณกำลังทำ มีกรณีการใช้งานมากมายที่แนวทางของคุณผิด นอกจากนี้ยังมีอีกหลายจุดที่ปุ่มหายไปในการold_dictระบุข้อผิดพลาดที่อื่นและในกรณีนั้นฉันชอบข้อผิดพลาดมากกับผลลัพธ์ที่ไม่ถูกต้อง

@delnan ยัง "ถ้า k ง" นอกจากนี้คุณช้าลงหาก d มีขนาดใหญ่ผมก็คิดว่ามันเป็นมูลค่าการกล่าวขวัญ
ปีเตอร์กิบสัน

7
@PeterGibson ไม่เช่นนั้นการค้นหาพจนานุกรมคือ O (1)

130

ความเข้าใจในประโยค dict ที่สง่างามกว่าเล็กน้อย:

foodict = {k: v for k, v in mydict.items() if k.startswith('foo')}

upvoted ฉันคิดว่าจะเพิ่มคำตอบที่คล้ายกับสิ่งนี้ แม้ว่าจะมีความอยากรู้อยากเห็นทำไม {k: v สำหรับ k, v ใน dict.items () ... } มากกว่า {k: dict [k] สำหรับ k ใน dict ... } มีความแตกต่างของประสิทธิภาพหรือไม่
Hart Simha

4
ตอบคำถามของฉันเอง {k: dict [k] สำหรับ k in dict ... } เร็วกว่าประมาณ 20-25% อย่างน้อยใน Python 2.7.6 มีพจนานุกรม 26 รายการ (timeit (... , setup = "d = {chr (x + 97): x + 1 สำหรับ x ในช่วง (26)} ")) ขึ้นอยู่กับจำนวนรายการที่จะถูกกรองออก (การกรองคีย์พยัญชนะจะเร็วกว่าการกรองคีย์ของเสียงสระเพราะคุณกำลังมองหา รายการน้อยลง) ความแตกต่างของประสิทธิภาพอาจมีความสำคัญน้อยลงเมื่อขนาดพจนานุกรมของคุณเพิ่มขึ้น
Hart Simha

5
อาจจะเป็นสิ่งที่สมบูรณ์แบบถ้าคุณใช้mydict.iteritems()แทน .items()สร้างรายการอื่น
Pat

64

นี่คือตัวอย่างใน python 2.6:

>>> a = {1:1, 2:2, 3:3}
>>> dict((key,value) for key, value in a.iteritems() if key == 1)
{1: 1}

ส่วนการกรองคือifคำสั่ง

วิธีนี้ช้ากว่าคำตอบของ delnan หากคุณต้องการเลือกปุ่มจำนวนมากเท่านั้น


11
ยกเว้นฉันอาจจะใช้if key in ('x','y','z')ฉันเดา
mpen

หากคุณรู้ว่าคุณต้องการใช้ปุ่มใดให้ใช้คำตอบของ delnan หากคุณต้องการทดสอบแต่ละคีย์ด้วยคำสั่ง if ให้ใช้คำตอบของ ransford
jnnnnn

1
วิธีนี้มีข้อดีอีกข้อหนึ่ง หากพจนานุกรมถูกส่งคืนจากการเรียกใช้ฟังก์ชันที่มีราคาแพง (เช่น / old_dict เป็นการเรียกใช้ฟังก์ชัน) โซลูชันนี้จะเรียกใช้ฟังก์ชันเพียงครั้งเดียว ในสภาพแวดล้อมที่จำเป็นการจัดเก็บพจนานุกรมที่ส่งคืนโดยฟังก์ชันในตัวแปรไม่ใช่เรื่องใหญ่ แต่ในสภาพแวดล้อมการทำงาน (เช่นในแลมบ์ดา) นี่คือการสังเกตที่สำคัญ
gae123


20

รหัส 1:

dict = { key: key * 10 for key in range(0, 100) }
d1 = {}
for key, value in dict.items():
    if key % 2 == 0:
        d1[key] = value

รหัส 2:

dict = { key: key * 10 for key in range(0, 100) }
d2 = {key: value for key, value in dict.items() if key % 2 == 0}

รหัส 3:

dict = { key: key * 10 for key in range(0, 100) }
d3 = { key: dict[key] for key in dict.keys() if key % 2 == 0}

ประสิทธิภาพของรหัสทั้งหมดจะถูกวัดด้วย timeit โดยใช้หมายเลข = 1,000 และรวบรวม 1,000 ครั้งสำหรับรหัสแต่ละชิ้น

ป้อนคำอธิบายรูปภาพที่นี่

สำหรับไพ ธ อน 3.6 ประสิทธิภาพของปุ่มกดตัวกรองสามวิธีเกือบเหมือนกัน สำหรับ python 2.7 code 3 นั้นเร็วกว่าเล็กน้อย


แค่อยากรู้คุณสร้างพล็อตนั้นจาก Python หรือไม่?
user5359531

1
ggplot2 ใน R - ส่วนหนึ่งของtidyverse
keithpjolley

18

แลมบ์ดาซับวันนี้ควรทำงาน:

dictfilt = lambda x, y: dict([ (i,x[i]) for i in x if i in set(y) ])

นี่คือตัวอย่าง:

my_dict = {"a":1,"b":2,"c":3,"d":4}
wanted_keys = ("c","d")

# run it
In [10]: dictfilt(my_dict, wanted_keys)
Out[10]: {'c': 3, 'd': 4}

มันเป็นความเข้าใจพื้นฐานของรายการที่ทำซ้ำคีย์ dict ของคุณ (i ใน x) และส่งออกรายการของ tuple (คีย์, ค่า) จับคู่ถ้าคีย์อยู่ในรายการคีย์ที่ต้องการ (y) Dict () ล้อมทุกสิ่งเพื่อส่งออกเป็นวัตถุ Dict


ควรใช้setสำหรับwanted_keysแต่อย่างอื่นดูดี
mpen

สิ่งนี้ทำให้ฉันมีพจนานุกรมว่างเปล่าหากพจนานุกรมต้นฉบับของฉันมีรายการแทนที่ค่า วิธีแก้ปัญหาใด ๆ
FaCoffee

@ Francesco คุณช่วยยกตัวอย่างได้ไหม ถ้าฉันวิ่ง: dictfilt({'x':['wefwef',52],'y':['iuefiuef','efefij'],'z':['oiejf','iejf']}, ('x','z'))มันกลับมา{'x': ['wefwef', 52], 'z': ['oiejf', 'iejf']}ตามที่ตั้งใจไว้
Jim

ฉันลองทำสิ่งนี้ด้วย: dict={'0':[1,3], '1':[0,2,4], '2':[1,4]}และผลลัพธ์ก็คือ{}ซึ่งฉันคิดว่าเป็นพจน์ว่างเปล่า
FaCoffee

สิ่งหนึ่งที่ "dict" เป็นคำที่สงวนไว้ดังนั้นคุณไม่ควรใช้มันเพื่อตั้งชื่อ dict กุญแจอะไรที่คุณพยายามจะดึงออกมา? ถ้าฉันวิ่ง: foo = {'0':[1,3], '1':[0,2,4], '2':[1,4]}; dictfilt(foo,('0','2'))ฉันจะได้รับ: {'0': [1, 3], '2': [1, 4]}ซึ่งเป็นผลลัพธ์ที่ตั้งใจ
Jim

14

ให้พจนานุกรมต้นฉบับของคุณorigและชุดรายการที่คุณสนใจkeys:

filtered = dict(zip(keys, [orig[k] for k in keys]))

ซึ่งไม่ดีเท่าคำตอบของ delnan แต่ควรใช้ได้ในทุกเวอร์ชั่นที่น่าสนใจ อย่างไรก็ตามมันเปราะบางต่อองค์ประกอบkeysที่มีอยู่ในพจนานุกรมต้นฉบับของคุณ


ทีนี้นี่เป็นรุ่นกระตือรือร้นของ "tuple generator version" ที่ผมเข้าใจ เข้ากันได้ดีมากแม้ว่าจะใช้นิพจน์เครื่องกำเนิดไฟฟ้าใน 2.4 ฤดูใบไม้ผลิ 2005 - อย่างจริงจังมีใครยังใช้สิ่งนี้อยู่บ้าง

1
ฉันไม่เห็นด้วย 2.3 ไม่ควรมีอยู่จริงอีกต่อไป อย่างไรก็ตามจากการสำรวจที่ล้าสมัยของการใช้งาน 2.3: moinmo.in/PollAboutRequiringPython24เวอร์ชั่นสั้น: RHEL4, SLES9, มาพร้อมกับ OS X 10.4
Kai

7

ตามคำตอบที่ยอมรับโดย delnan

เกิดอะไรขึ้นถ้าคีย์ที่คุณต้องการไม่อยู่ใน old_dict โซลูชัน delnan จะส่งข้อยกเว้น KeyError ที่คุณสามารถตรวจจับได้ หากนั่นไม่ใช่สิ่งที่คุณต้องการบางทีคุณอาจต้องการ:

  1. รวมเฉพาะกุญแจที่มีให้ทั้งใน old_dict และชุดที่ต้องการของคุณ

    old_dict = {'name':"Foobar", 'baz':42}
    wanted_keys = ['name', 'age']
    new_dict = {k: old_dict[k] for k in set(wanted_keys) & set(old_dict.keys())}
    
    >>> new_dict
    {'name': 'Foobar'}
  2. มีค่าเริ่มต้นสำหรับคีย์ที่ไม่ได้ตั้งค่าไว้ใน old_dict

    default = None
    new_dict = {k: old_dict[k] if k in old_dict else default for k in wanted_keys}
    
    >>> new_dict
    {'age': None, 'name': 'Foobar'}

คุณสามารถทำได้{k: old_dict.get(k, default) for k in ...}
Moberg

6

ฟังก์ชั่นนี้จะทำการหลอกลวง:

def include_keys(dictionary, keys):
    """Filters a dict by only including certain keys."""
    key_set = set(keys) & set(dictionary.keys())
    return {key: dictionary[key] for key in key_set}

เช่นเดียวกับเวอร์ชันของ delnan พจนานุกรมนี้ใช้ความเข้าใจในพจนานุกรมและมีประสิทธิภาพการทำงานที่มั่นคงสำหรับพจนานุกรมขนาดใหญ่ (ขึ้นอยู่กับจำนวนของคีย์ที่คุณอนุญาตเท่านั้นและไม่รวมถึงจำนวนคีย์ทั้งหมดในพจนานุกรม)

และเช่นเดียวกับรุ่น MyGGan อันนี้อนุญาตให้รายการคีย์ของคุณรวมคีย์ที่อาจไม่มีอยู่ในพจนานุกรม

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

def exclude_keys(dictionary, keys):
    """Filters a dict by excluding certain keys."""
    key_set = set(dictionary.keys()) - set(keys)
    return {key: dictionary[key] for key in key_set}

โปรดทราบว่าไม่เหมือนกับเวอร์ชันของ delnan การดำเนินการไม่ได้เกิดขึ้นดังนั้นประสิทธิภาพจึงสัมพันธ์กับจำนวนของคีย์ในพจนานุกรม อย่างไรก็ตามข้อดีของการทำเช่นนี้คือฟังก์ชั่นจะไม่แก้ไขพจนานุกรมที่ให้ไว้

แก้ไข:เพิ่มฟังก์ชั่นแยกต่างหากสำหรับการยกเว้นปุ่มบางอย่างจาก dict


คุณควรอนุญาตให้keysทำซ้ำได้ทุกประเภทเช่นชุดที่ยอมรับได้
mpen

อ่าดีโทรขอบคุณที่ชี้ให้เห็น ฉันจะทำให้การปรับปรุงนั้น
Ryan

ฉันสงสัยว่าคุณเก่งกว่าสองฟังก์ชั่นหรือไม่ หากคุณถามคน 10 คน "ไม่ได้invertหมายความว่ามีการkeysโต้แย้งหรือการkeysโต้แย้งนั้นถูกปฏิเสธ?" พวกเขาจะเห็นด้วยกี่คน?
skatenerd

Updated แจ้งให้เราทราบสิ่งที่คุณคิด.
Ryan

สิ่งนี้ดูเหมือนว่าจะไม่ทำงานหาก dict อินพุตมีรายการแทนที่ค่า ในกรณีนี้คุณจะได้รับข้อความเป็นโมฆะ วิธีแก้ปัญหาใด ๆ
FaCoffee

4

หากเราต้องการสร้างพจนานุกรมใหม่โดยลบกุญแจที่เลือกเราสามารถใช้ประโยชน์จากความเข้าใจในพจนานุกรม
เช่น:

d = {
'a' : 1,
'b' : 2,
'c' : 3
}
x = {key:d[key] for key in d.keys() - {'c', 'e'}} # Python 3
y = {key:d[key] for key in set(d.keys()) - {'c', 'e'}} # Python 2.*
# x is {'a': 1, 'b': 2}
# y is {'a': 1, 'b': 2}

เรียบร้อย ใช้งานได้เฉพาะใน Python 3 Python 2 บอกว่า "TypeError: ประเภทตัวถูกดำเนินการที่ไม่รองรับสำหรับ -: 'รายการ' และ 'ตั้ง'"
mpen

เพิ่มชุด (d.keys ()) สำหรับ Python 2 นี้ทำงานเมื่อฉันเรียกใช้
Srivastava

2

ตัวเลือกอื่น:

content = dict(k1='foo', k2='nope', k3='bar')
selection = ['k1', 'k3']
filtered = filter(lambda i: i[0] in selection, content.items())

แต่คุณจะได้รับlist(งูหลาม 2) หรือ iterator (งูใหญ่ 3) ส่งกลับโดยไม่ filter()dict


ห่อfilteredในdictและคุณได้รับกลับพจนานุกรม!
CMCDragonkai

1

แบบสั้น:

[s.pop(k) for k in list(s.keys()) if k not in keep]

เป็นที่สุดของคำตอบที่แนะนำเพื่อรักษาความกระชับที่เรามีการสร้างวัตถุที่ซ้ำกันไม่ว่าจะเป็นหรือlist dictหนึ่งนี้สร้างโยนไปแต่ลบคีย์ในต้นฉบับlistdict


0

นี่เป็นอีกวิธีการง่ายๆที่ใช้delในหนึ่งซับ:

for key in e_keys: del your_dict[key]

e_keysเป็นรายการของกุญแจที่จะยกเว้น มันจะอัพเดท dict ของคุณแทนที่จะให้ใหม่

ถ้าคุณต้องการเอาท์พุทดิจิตัลใหม่ให้ทำสำเนาของดิจิตัลก่อนที่จะลบ:

new_dict = your_dict.copy()           #Making copy of dict

for key in e_keys: del new_dict[key]

0

คุณสามารถใช้python-benedictมันเป็นคลาสย่อย dict

การติดตั้ง: pip install python-benedict

from benedict import benedict

dict_you_want = benedict(your_dict).subset(keys=['firstname', 'lastname', 'email'])

เป็นโอเพ่นซอร์สบน GitHub: https://github.com/fabiocaccamo/python-benedict


คำเตือน: ฉันเป็นผู้เขียนของห้องสมุดนี้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.