Załóżmy, że masz słownik taki jak:
{'a': 1,
'c': {'a': 2,
'b': {'x': 5,
'y' : 10}},
'd': [1, 2, 3]}
Jak byś spłaszczył to do czegoś takiego:
{'a': 1,
'c_a': 2,
'c_b_x': 5,
'c_b_y': 10,
'd': [1, 2, 3]}
python
dictionary
A Timmes
źródło
źródło
Odpowiedzi:
Zasadniczo w ten sam sposób, w jaki spłaszczyłbyś zagnieżdżoną listę, musisz po prostu wykonać dodatkową pracę, aby iterować dyktafon według klucza / wartości, tworząc nowe klucze dla nowego słownika i tworząc słownik w ostatnim kroku.
źródło
isinstance
ztry..except
bloku, to będzie pracować dla dowolnego odwzorowania, nawet jeśli nie jest pochodnądict
.collections.MutableMapping
aby był bardziej ogólny. Ale dla Pythona <2.6try..except
jest to prawdopodobnie najlepsza opcja.if isinstance(v, collections.MutableMapping):
naif v and isinstance(v, collections.MutableMapping):
new_key = parent_key + sep + k if parent_key else k
zakłada się, że klucze są zawsze łańcuchami, w przeciwnym razie wzrosnąTypeError: cannot concatenate 'str' and [other] objects
. Jednak możesz to naprawić, po prostu wymuszając metodęk
string (str(k)
) lub łącząc klucze w krotkę zamiast w łańcuch (krotki również mogą być kluczami dict).Istnieją dwie ważne kwestie, które należy wziąć pod uwagę przy oryginalnym plakacie:
{'a_b':{'c':1}, 'a':{'b_c':2}}
spowoduje to{'a_b_c':???}
. Poniższe rozwiązanie pozwala uniknąć problemu, zwracając iterowalne pary.joinedKey = '_'.join(*keys)
, będzie cię to kosztować O (N ^ 2) czasu pracy. Jeśli jednak chcesz to powiedziećnextKey = previousKey+'_'+thisKey
, masz czas O (N). Poniższe rozwiązanie pozwala ci zrobić jedno i drugie (ponieważ możesz po prostu połączyć wszystkie klucze, a następnie przetworzyć je ponownie).(Wydajność nie jest prawdopodobnie problemem, ale omówię drugi punkt na wypadek, gdyby ktokolwiek inny się tym przejmował: wdrażając to, istnieje wiele niebezpiecznych wyborów. Jeśli robisz to rekurencyjnie i dajesz i ponownie dajesz, lub cokolwiek równoważnego, co dotyka węzłów więcej niż raz (co jest dość łatwe do przypadkowego zrobienia), potencjalnie wykonujesz pracę O (N ^ 2) zamiast O (N). To dlatego, że być może obliczasz klucz,
a
aa_1
potema_1_i
..., a potem obliczasza
następniea_1
następniea_1_ii
..., ale tak naprawdę nie powinno mieć obliczyća_1
ponownie. Nawet jeśli nie są przeliczania go ponownie otrzymując go (podejście „poziom po poziomie”) jest tak źle. dobrym przykładem jest myśleć o występie na{1:{1:{1:{1:...(N times)...{1:SOME_LARGE_DICTIONARY_OF_SIZE_N}...}}}}
)Poniżej znajduje się funkcja, którą napisałem,
flattenDict(d, join=..., lift=...)
która może być dostosowana do wielu celów i może robić, co chcesz. Niestety, dość trudno jest stworzyć leniwą wersję tej funkcji bez ponoszenia powyższych kar za wydajność (wiele wbudowanych funkcji Pythona, takich jak chain.from_iterable, nie jest w rzeczywistości wydajnych, co zdałem sobie sprawę dopiero po szeroko zakrojonych testach trzech różnych wersji tego kodu, zanim zdecydowałem się na ten).Aby lepiej zrozumieć, co się dzieje, poniżej znajduje się diagram dla osób niezaznajomionych z
reduce
(po lewej), znanym również jako „złóż w lewo”. Czasami jest rysowany z wartością początkową zamiast k0 (nie jest częścią listy, przekazywana do funkcji). OtoJ
naszajoin
funkcja. Przetwarzamy wstępnie każdy k n zlift(k)
.W rzeczywistości jest to to samo, co
functools.reduce
, ale gdzie nasza funkcja robi to ze wszystkimi kluczowymi ścieżkami drzewa.Demonstracja (którą w innym przypadku umieściłbym w dokumentacji):
Występ:
... westchnij, nie myśl, że to moja wina ...
[nieważna notatka historyczna z powodu problemów z moderacją]
Odnośnie domniemanego duplikatu Flatten słownika słowników (głęboki na 2 poziomy) list w Pythonie :
Rozwiązanie tego pytania można zaimplementować w ramach tego przez działanie
sorted( sum(flatten(...),[]) )
. Odwrotna nie jest możliwe: ile jest prawdą, że wartość zflatten(...)
może być odzyskany z domniemanym podwójnie przez akumulator mapowania wyższego rzędu, to nie można odzyskać kluczy. (edytuj: Okazuje się również, że pytanie domniemanego powielonego właściciela jest zupełnie inne, ponieważ dotyczy tylko słowników o głębokości dokładnie 2 poziomów, chociaż jedna z odpowiedzi na tej stronie daje ogólne rozwiązanie.)źródło
A jeśli już używasz pand, możesz to zrobić w następujący
json_normalize()
sposób:Wynik:
źródło
Jeśli używasz,
pandas
istnieje funkcja ukryta wpandas.io.json._normalize
1 o nazwie,nested_to_record
która robi to dokładnie.1 W wersjach pandy
0.24.x
i starszym użyciupandas.io.json.normalize
(bez_
)źródło
from pandas.io.json._normalize import nested_to_record
. Zwróć uwagę na podkreślenie (_
) przednormalize
.0.25.x
, zaktualizowałem odpowiedź. :)Oto rodzaj „funkcjonalnej”, „jednowierszowej” implementacji. Jest rekurencyjny i oparty na wyrażeniu warunkowym i dyktowaniu.
Test:
źródło
('hgf',2)
2. klucza w twoich rzutach testowychTypeError
+
operator. W przypadku czegokolwiek innego będziesz musiał dostosowaćprefix + separator + k
się do odpowiedniego wywołania funkcji, aby skomponować obiekty.{'a_b':{'c':1}, 'a':{'b_c':2}}
{'name': 'Steven', 'children': [{'name': 'Jessica', 'children': []}, {'name': 'George', 'children': []}]}
Kod:
Wyniki:
Używam python3.2, aktualizacja dla twojej wersji pythona.
źródło
lkey=''
w definicji funkcji zamiast podczas wywoływania funkcji. Zobacz inne odpowiedzi w tym zakresie.Co powiesz na funkcjonalne i wydajne rozwiązanie w Pythonie 3.5?
To jest jeszcze bardziej wydajne:
W użyciu:
źródło
reduce
jest to świetne rozwiązanie, jeśli trzeba zmniejszyć liczbę słowników. Zaktualizowałem odpowiedź. Powinien wyglądać teraz trochę bardziej pytonicznie.Nie jest to ograniczone do słowników, ale do każdego typu odwzorowania, który implementuje .items (). Dalej jest szybszy, ponieważ unika warunku „jeśli”. Niemniej jednak kredyty trafiają do Imrana:
źródło
d
nie jestdict
niestandardowym typem mapowania, który nie jest implementowanyitems
, Twoja funkcja od razu zakończy się niepowodzeniem. Więc to nie działa dla każdego typu mapowania, ale tylko te, które implementująitems()
.items
? Byłbym ciekawy, żeby taki zobaczyć.Moje rozwiązanie dla Pythona 3.3 przy użyciu generatorów:
źródło
Prosta funkcja spłaszczania zagnieżdżonych słowników. Pythona 3, zastępuje
.iteritems()
się.items()
Pomysł / wymóg brzmiał: Uzyskaj płaskie słowniki bez przechowywania kluczy nadrzędnych.
Przykład użycia:
Przechowywanie kluczy rodzica również jest proste.
źródło
Wykorzystując rekursję, zachowując prostotę i czytelność dla człowieka:
Połączenie jest proste:
lub
jeśli chcemy zmienić domyślny separator.
Mały podział:
Kiedy funkcja jest wywoływana po raz pierwszy, jest wywoływana tylko przekazując to,
dictionary
co chcemy spłaszczyć.accumulator
Parametrem jest tutaj do rekursji wsparcia, które zobaczymy później. Więc tworzymy instancjęaccumulator
w pustym słowniku, w którym umieścimy wszystkie zagnieżdżone wartości z oryginałudictionary
.Podczas iteracji po wartościach słownika tworzymy klucz dla każdej wartości.
parent_key
Argumentem będzieNone
do pierwszego połączenia, podczas gdy dla każdego zagnieżdżonego słownika, będzie zawierać klucz wskazując na niego, więc poprzedzić ten klucz.W przypadku, gdy wartość wskazywana
v
przez kluczk
jest słownikiem, funkcja wywołuje samą siebie, przekazując zagnieżdżony słownik,accumulator
(który jest przekazywany przez odniesienie, więc wszystkie wprowadzone w nim zmiany są dokonywane w tej samej instancji) i kluczk
, abyśmy może skonstruować połączony klucz. Zwróć uwagę nacontinue
oświadczenie. Chcemy pominąć następną linię, znajdującą się pozaif
blokiem, aby zagnieżdżony słownik nie znalazł się waccumulator
kluczu underk
.Co więc zrobimy, jeśli wartość
v
nie jest słownikiem? Po prostu włóż go bez zmian doaccumulator
.Kiedy skończymy, po prostu zwracamy
accumulator
, pozostawiając oryginalnydictionary
argument nietknięty.UWAGA
Działa to tylko ze słownikami, które mają ciągi znaków jako klucze. Będzie działać z obiektami, które można mieszać, implementując tę
__repr__
metodę, ale przyniesie niepożądane wyniki.źródło
Jest to podobne do odpowiedzi imrana i ralu. Nie używa generatora, ale zamiast tego wykorzystuje rekursję z zamknięciem:
źródło
_flatten_dict
nigdy nie jest zwracana ani nie oczekuje się jej zwrotu. Zamiast tego może być nazywana podfunkcją lub funkcją zamkniętą .Rozwiązanie Davouda jest bardzo ładne, ale nie daje zadowalających wyników, gdy zagnieżdżony dykt zawiera również listy dykt, ale jego kod jest dostosowany do tego przypadku:
źródło
type([])
aby uniknąć wywołania funkcji dla każdego elementudict
.isinstance(v, list)
zamiast tegoPowyższe odpowiedzi działają naprawdę dobrze. Pomyślałem, że dodam niespłaszczoną funkcję, którą napisałem:
Uwaga: to nie uwzględnia znaku „_” już obecnego w kluczach, podobnie jak ich odpowiedniki spłaszczone.
źródło
Oto algorytm eleganckiej wymiany na miejscu. Testowane w Pythonie 2.7 i Pythonie 3.5. Używanie kropki jako separatora.
Przykład:
Wynik:
Opublikowałem ten kod tutaj wraz z
unflatten_json
funkcją dopasowania .źródło
Jeśli chcesz spłaszczyć zagnieżdżony słownik i chcesz mieć listę wszystkich unikalnych kluczy, oto rozwiązanie:
źródło
źródło
źródło
Myślałem o podklasie UserDict do automagicznego spłaszczania klawiszy.
Zalety polegające na tym, że klucze można dodawać w locie lub przy użyciu standardowej instrukcji dyktowania, bez zaskoczenia:
źródło
Korzystanie z generatorów:
źródło
type(i).__name__=='dict'
można by zastąpićtype(i) is dict
lub nawet lepiejisinstance(d, dict)
(lubMapping
/MutableMapping
).Używanie dict.popitem () w prostej rekursji podobnej do zagnieżdżonej listy:
źródło
Nie jest to dokładnie to, o co prosił OP, ale wielu ludzi przychodzi tutaj, szukając sposobów na spłaszczenie zagnieżdżonych danych JSON w świecie rzeczywistym, które mogą mieć zagnieżdżone obiekty json i tablice klucz-wartość oraz obiekty JSON wewnątrz tablic i tak dalej. JSON nie zawiera krotek, więc nie musimy się tym martwić.
Znalazłem implementację komentarza włączenia listy autorstwa @roneo do odpowiedzi zamieszczonej przez @Imran :
https://github.com/ScriptSmith/socialreaper/blob/master/socialreaper/tools.py#L8
Sprawdź to:
I to robi to, czego potrzebuję: rzucam na to skomplikowany plik json, a to dla mnie wyrównuje.
Wszystkie kredyty do https://github.com/ScriptSmith .
źródło
Właściwie napisałem niedawno pakiet o nazwie cherrypicker, aby poradzić sobie z tego rodzaju rzeczami, ponieważ musiałem to robić tak często!
Myślę, że poniższy kod dałby ci dokładnie to, czego szukasz:
Możesz zainstalować pakiet za pomocą:
... a więcej dokumentów i wskazówek znajdziesz na https://cherrypicker.readthedocs.io .
Inne metody mogą być szybsze, ale priorytetem tego pakietu jest, aby takie zadania łatwe . Jeśli jednak masz dużą listę obiektów do spłaszczenia, możesz również powiedzieć CherryPicker, aby używał przetwarzania równoległego w celu przyspieszenia działania.
źródło
Zawsze wolę uzyskiwać dostęp do
dict
obiektów przez.items()
, więc do spłaszczania dykt używam następującego generatora rekurencyjnegoflat_items(d)
. Jeśli chcesz miećdict
ponownie, po prostu zawiń to w ten sposób:flat = dict(flat_items(d))
źródło
Odmiana tych zagnieżdżonych słowników Flatten, kompresja kluczy z maksymalnym poziomem i niestandardową redukcją.
źródło
Jeśli nie masz nic przeciwko funkcjom rekurencyjnym, oto rozwiązanie. Pozwoliłem sobie również na uwzględnienie parametru wykluczenia na wypadek, gdyby istniała jedna lub więcej wartości, które chcesz zachować.
Kod:
Stosowanie:
Wynik:
źródło
Wypróbowałem niektóre rozwiązania na tej stronie - choć nie wszystkie - ale te, które próbowałem, nie poradziły sobie z zagnieżdżoną listą dykt.
Rozważ taki dykt:
Oto moje prowizoryczne rozwiązanie:
który produkuje:
Prowizoryczne rozwiązanie i nie jest doskonałe.
UWAGA:
nie zachowuje pustych dykt, takich jak
address: {}
para k / v.nie spłaszczy dykt w zagnieżdżonych krotkach - chociaż łatwo byłoby to dodać, wykorzystując fakt, że krotki Pythona działają podobnie do list.
źródło
Po prostu użyj
python-benedict
, jest to podklasa dict, która oferuje wiele funkcji, w tymflatten
metodę. Można go zainstalować za pomocą pip:pip install python-benedict
https://github.com/fabiocaccamo/python-benedict#flatten
źródło