Filtre sadece belirli anahtarlar içeriyor mu?


496

Bir var dictgirdileri bir sürü vardır. Sadece birkaç tanesiyle ilgileniyorum. Diğerlerini budamanın kolay bir yolu var mı?


Hangi anahtarların (tamsayılar? Dizeler? Tarihler? Rasgele nesneler?) Ve dolayısıyla hangi anahtarların içeri veya dışarı olduğunu kontrol etmek için basit (dize, normal ifade, liste üyeliği veya sayısal eşitsizlik) testi olup olmadığını söylemek yararlıdır. Yoksa bunu belirlemek için rastgele işlev (ler) i çağırmamız gerekir.
smci

@smci Dize anahtarları. Başka bir şey kullanabileceğimin bile aklıma gelmediğini düşünmüyorum; Çok uzun zamandır JS ve PHP
kodluyorum

Yanıtlar:


656

Yeni bir diksiyon oluşturmak:

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

Sözlük anlamayı kullanır.

Bunlardan yoksun bir sürüm kullanıyorsanız (yani Python 2.6 ve öncesi), yapın dict((your_key, old_dict[your_key]) for ...). Aynı şey, daha çirkin olsa da.

Bunun, jnnnnn sürümünün aksine old_dict, herhangi bir boyutta sabit bir performansa (yalnızca anahtarlarınızın sayısına bağlı olarak) sahip olduğunu unutmayın . Hem hız hem de hafıza açısından. Bu bir üretici ifadesi olduğundan, her seferinde bir öğe işler ve tüm old_dict öğelerine bakmaz.

Her şeyi yerinde kaldırma:

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

8
"Onlardan yoksun bir sürüm kullanıyorsanız sözlük anlama kullanır" == sürüm <= 2.6
getekha

8
Disk belleği tuşlarından biri eski_dict içinde yoksa bir KeyError atar. D'de k ise filtrede k için {k: d [k] öneririm
Peter Gibson

1
@PeterGibson Evet, bu gereksinimlerin bir parçasıysa, bu konuda bir şeyler yapmanız gerekir . Anahtarları sessizce bırakıp bırakmamak, varsayılan bir değer eklemek veya başka bir şey yapmak ne yaptığınıza bağlıdır; yaklaşımınızın yanlış olduğu birçok kullanım durumu vardır. Eksik olan bir anahtarın old_dictbaşka bir yerde bir hatayı gösterdiği birçok şey var ve bu durumda sessizce yanlış sonuçlara bir hatayı tercih ederim.

@ delnan, ayrıca "d'de k" ilavesi d büyükse sizi yavaşlatır, bahsetmeye değer olduğunu düşündüm
Peter Gibson

7
@PeterGibson Olmaz, sözlük araması O (1) 'dir.

130

Biraz daha zarif dikseli anlama:

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

Upvoted. Buna benzer bir cevap eklemeyi düşünüyordum. Ancak meraktan, neden {k: d için k, v için dict.items () ...} yerine {k: dict [k] yerine d için ...} Performans farkı var?
Hart Simha

4
Kendi sorumu yanıtladı. Dict'teki k için {k: dict [k], yaklaşık 20-25 daha hızlıdır, en azından Python 2.7.6'da 26 öğeden oluşan bir sözlükle (timeit (..., setup = "d = {chr (x + 97): x (1) aralığında x için x + 1} ")), kaç öğenin filtrelendiğine bağlı olarak (ünsüz anahtarları filtrelemek ünlü anahtarları filtrelemekten daha hızlıdır çünkü daha az öğe). Sözlük boyutunuz büyüdükçe performans farkı çok daha az belirgin hale gelebilir.
Hart Simha

5
mydict.iteritems()Bunun yerine kullandıysanız muhtemelen aynı mükemmel olurdu . .items()başka bir liste oluşturur.
Pat

64

İşte python 2.6'daki bir örnek:

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

Filtreleme kısmı ififadedir.

Bu yöntem delnan'ın cevabından daha yavaştır, ancak çok fazla tuştan sadece birkaçını seçmek istiyorsanız.


11
dışında muhtemelen kullanacağımı if key in ('x','y','z')tahmin ediyorum.
Ağustos'ta

hangi anahtarları istediğinizi zaten biliyorsanız, delnan'ın cevabını kullanın. Her anahtarı bir if ifadesiyle test etmeniz gerekiyorsa, ransford'un yanıtını kullanın.
jnnnnn

1
Bu çözümün bir avantajı daha var. Sözlük pahalı bir işlev çağrısından döndürülürse (yani / old_dict bir işlev çağrısıysa) bu çözüm işlevi yalnızca bir kez çağırır. İşlev tarafından döndürülen sözlüğün bir değişkende saklanması zorunlu bir ortamda önemli bir şey değildir, ancak işlevsel bir ortamda (örneğin lambdada) bu önemli bir gözlemdir.
gae123


20

Kod 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

Kod 2:

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

Kod 3:

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

Tüm kod performansına bağlı parça sayısı = 1000 kullanılarak timeit ile ölçülür ve her bir kod parçası için 1000 kez toplanır.

resim açıklamasını buraya girin

Python 3.6 için, filtre dikme tuşlarının üç yolunun performansı neredeyse aynıdır. Python 2.7 için kod 3 biraz daha hızlıdır.


sadece merak ettim, Python'dan bu komployu yaptın mı?
user5359531

1
ggplot2 in R - tidyverse
keithpjolley

18

Bu bir astar lambda çalışmalıdır:

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

İşte bir örnek:

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 anahtarlarınız (i in x) üzerinde yinelenen temel bir liste kavramadır ve anahtar istediğiniz anahtar listenizde (y) yaşıyorsa bir demet (anahtar, değer) çifti listesi çıkarır. Bir dict (), bir dict nesnesi olarak çıktı almak için her şeyi sarar.


Bir setiçin kullanmalı wanted_keys, ancak aksi takdirde iyi görünüyor.
mpen

Orijinal sözlüğüm değer yerine liste içeriyorsa, bu bana boş bir sözlük verir. Herhangi bir geçici çözüm var mı?
FaCoffee

@Francesco, bir örnek verebilir misiniz? Eğer koşarsam:, istendiği dictfilt({'x':['wefwef',52],'y':['iuefiuef','efefij'],'z':['oiejf','iejf']}, ('x','z'))gibi geri döner {'x': ['wefwef', 52], 'z': ['oiejf', 'iejf']}.
Jim

Bunu ile denedim: dict={'0':[1,3], '1':[0,2,4], '2':[1,4]}ve sonuç, {}boş bir diksiyon olduğu varsayıldım.
FaCoffee

Bir şey, "dikt" ayrılmış bir kelimedir, bu yüzden bir dikteyi adlandırmak için kullanmamalısınız. Çıkarmaya çalıştığınız anahtarlar nelerdi? Eğer koşarsam:, foo = {'0':[1,3], '1':[0,2,4], '2':[1,4]}; dictfilt(foo,('0','2'))şunu elde ederim: {'0': [1, 3], '2': [1, 4]}amaçlanan sonuç
Jim

14

Orijinal sözlüğünüz origve ilgilendiğiniz girişler kümesi göz önüne alındığında keys:

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

ki bu delnan'ın yanıtı kadar hoş değil, ancak ilgilenilen her Python sürümünde çalışmalıdır. Bununla birlikte, keysorijinal sözlüğünüzdeki mevcut öğelerin her birine kırılgandır .


Peki, bu temelde benim dik kavrama benim "tuple jeneratör sürümü" istekli bir versiyonu. Gerçekten çok uyumlu, jeneratör ifadeleri 2.4, bahar 2005'te kullanılmasına rağmen - cidden, kimse hala kullanıyor mu?

1
Ben katılmıyorum; 2.3 artık gerçekten var olmamalı. Ancak, 2.3 kullanımının eski bir araştırması olarak: moinmo.in/PollAboutRequiringPython24 Kısa versiyon: RHEL4, SLES9, OS X 10.4
Kai

7

Delnan'ın kabul ettiği cevaba dayanarak.

İstediğiniz anahtarlardan biri eski_dict'ta değilse ne olur? Delnan çözümü, yakalayabileceğiniz bir KeyError istisnası atar. İhtiyacınız olan şey bu değilse belki de yapmak istersiniz:

  1. yalnızca hem eski_dikilde hem de aranan_anahtarlar kümenizde ısrar eden anahtarlar içerir.

    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 içinde ayarlanmamış anahtarlar için varsayılan bir değere sahiptir.

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

Ayrıca şunu da yapabilirsin{k: old_dict.get(k, default) for k in ...}
Moberg

6

Bu işlev hile yapacak:

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'ın sürümü gibi, bu da sözlük anlamayı kullanır ve büyük sözlükler için kararlı bir performansa sahiptir (yalnızca izin verdiğiniz anahtar sayısına bağlıdır, sözlükteki toplam anahtar sayısına bağlı değildir).

MyGGan'ın sürümü gibi, bu da anahtar listenizin sözlükte bulunmayan anahtarları içermesine izin verir.

Ve bonus olarak, orijinaldeki belirli tuşları hariç tutarak sözlük oluşturabileceğiniz tersi:

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'ın versiyonundan farklı olarak, işlemin yerinde yapılmadığından performansın sözlükteki anahtar sayısıyla ilişkili olduğunu unutmayın. Ancak bunun avantajı, işlevin sağlanan sözlüğü değiştirmeyecek olmasıdır.

Düzenle: Belirli anahtarları bir diktenden hariç tutmak için ayrı bir işlev eklendi.


Setin kabul ettiği keysgibi her türlü yinelemeye izin vermelisiniz .
mpen

Ah, iyi çağrı, bunu işaret ettiğiniz için teşekkürler. Bu güncellemeyi yapacağım.
Ryan

İki fonksiyonla daha iyi olup olmadığınızı merak ediyorum. Eğer 10 kişiye " argümanın korunduğunu mu yoksa argümanın reddedildiğini invertima ediyor mu?" keyskeys
skatenerd

Güncellenmiş. Ne düşündüğü söyle.
Ryan

Girilen dikte değerler yerine listeler varsa, bu çalışmıyor gibi görünüyor. Bu durumda geçersiz bir karar alırsınız. Herhangi bir geçici çözüm var mı?
FaCoffee

4

Seçilen anahtarlar kaldırılmış olarak yeni bir sözlük yapmak istiyorsak, sözlük anlama özelliğini kullanabiliriz
Örneğin:

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}

Temiz. Sadece Python 3'te çalışır. Python 2, "TypeError: - list 've' set 'için desteklenmeyen işlenen tür (ler)" diyor
1919'da

Python 2 için set (d.keys ()) eklendi. Bu, çalıştırdığımda çalışıyor.
Srivastava

2

Başka seçenek:

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

Ancak bir list(Python 2) veya döndürülen bir yineleyici (Python 3) alırsınız, a filter()değil dict.


Wrap filterediçinde dictve sözlüğünü geri almak!
CMCDragonkai

1

Kısa form:

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

Cevapların çoğunun önerdiği gibi, özlülüğü korumak için, listya a ya da yinelenen bir nesne yaratmalıyız dict. Bu bir oyuna sokma oluşturur listancak orijinalindeki anahtarları siler dict.


0

İşte delbir astarda kullanılan başka bir basit yöntem :

for key in e_keys: del your_dict[key]

e_keyshariç tutulacak tuşların listesidir. Size yenisini vermek yerine diktenizi güncelleyecektir.

Yeni bir çıktı diktesi istiyorsanız, silmeden önce diktinin bir kopyasını oluşturun:

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

for key in e_keys: del new_dict[key]

0

Kullanabilirsin python-benedict, bu dik bir alt sınıf.

Kurulum: pip install python-benedict

from benedict import benedict

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

GitHub'da açık kaynak: https://github.com/fabiocaccamo/python-benedict


Feragatname: Bu kütüphanenin yazarıyım.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.