Misalkan Anda memiliki kamus seperti:
{'a': 1,
'c': {'a': 2,
'b': {'x': 5,
'y' : 10}},
'd': [1, 2, 3]}
Bagaimana Anda akan meratakannya menjadi seperti:
{'a': 1,
'c_a': 2,
'c_b_x': 5,
'c_b_y': 10,
'd': [1, 2, 3]}
python
dictionary
A Timmes
sumber
sumber
Jawaban:
Pada dasarnya dengan cara yang sama Anda akan meratakan daftar bersarang, Anda hanya perlu melakukan pekerjaan ekstra untuk iterate dict dengan kunci / nilai, membuat kunci baru untuk kamus baru Anda dan membuat kamus pada langkah terakhir.
sumber
isinstance
dengantry..except
blok, ini akan berfungsi untuk pemetaan apa pun, bahkan jika itu bukan berasal daridict
.collections.MutableMapping
agar lebih generik. Tetapi untuk Python <2.6,try..except
mungkin merupakan pilihan terbaik.if isinstance(v, collections.MutableMapping):
keif v and isinstance(v, collections.MutableMapping):
new_key = parent_key + sep + k if parent_key else k
mengasumsikan bahwa kunci selalu berupa string, jika tidak maka akan dinaikkanTypeError: cannot concatenate 'str' and [other] objects
. Namun, Anda bisa memperbaikinya hanya dengan memaksak
ke string (str(k)
), atau menyatukan kunci menjadi tuple dan bukannya string (tuple juga bisa menjadi kunci dict).Ada dua pertimbangan besar yang perlu dipertimbangkan poster asli:
{'a_b':{'c':1}, 'a':{'b_c':2}}
akan menghasilkan{'a_b_c':???}
. Solusi di bawah ini menghindari masalah dengan mengembalikan pasangan yang dapat diubah.joinedKey = '_'.join(*keys)
, itu akan menghabiskan waktu O (N ^ 2). Namun jika Anda mau mengatakannextKey = previousKey+'_'+thisKey
, itu membuat Anda O (N) waktu. Solusi di bawah ini memungkinkan Anda melakukan keduanya (karena Anda hanya bisa menggabungkan semua kunci, lalu mempostingnya).(Kinerja sepertinya bukan masalah, tapi saya akan menguraikan poin kedua jika ada orang yang peduli: Dalam menerapkan ini, ada banyak pilihan berbahaya. Jika Anda melakukan ini secara rekursif dan menghasilkan dan menghasilkan kembali, atau apa pun yang setara yang menyentuh node lebih dari sekali (yang cukup mudah untuk sengaja melakukan), Anda melakukan berpotensi O (N ^ 2) pekerjaan daripada O (N). Hal ini karena mungkin Anda menghitung kunci
a
kemudiana_1
kemudiana_1_i
..., dan kemudian menghitunga
kemudiana_1
kemudiana_1_ii
..., tapi benar-benar Anda tidak harus menghitunga_1
lagi. Bahkan jika Anda tidak menghitung ulang itu, re-menghasilkan itu (pendekatan 'tingkat-by-level') adalah sama buruknya. misalnya A yang baik adalah untuk memikirkan kinerja pada{1:{1:{1:{1:...(N times)...{1:SOME_LARGE_DICTIONARY_OF_SIZE_N}...}}}}
)Di bawah ini adalah fungsi yang saya tulis
flattenDict(d, join=..., lift=...)
yang dapat disesuaikan dengan banyak tujuan dan dapat melakukan apa yang Anda inginkan. Sayangnya itu cukup sulit untuk membuat versi malas dari fungsi ini tanpa menimbulkan hukuman kinerja di atas (banyak python builtins seperti chain.from_iterable sebenarnya tidak efisien, yang saya baru sadari setelah pengujian ekstensif dari tiga versi berbeda dari kode ini sebelum memutuskan yang ini).Untuk lebih memahami apa yang terjadi, di bawah ini adalah diagram untuk mereka yang tidak terbiasa dengan
reduce
(kiri), atau dikenal sebagai "flip kiri". Terkadang digambar dengan nilai awal menggantikan k0 (bukan bagian dari daftar, diteruskan ke fungsi). Di sini,J
adalahjoin
fungsi kita . Kami memproses setiap k n denganlift(k)
.Ini sebenarnya sama dengan
functools.reduce
, tetapi di mana fungsi kami melakukan ini untuk semua jalur kunci dari pohon.Demonstrasi (yang seharusnya saya masukkan ke dalam docstring):
Kinerja:
... huh, jangan pikir itu salahku ...
[catatan sejarah yang tidak penting karena masalah moderasi]
Mengenai dugaan duplikat Flatten, kamus kamus (sedalam 2 level) dari daftar dengan Python :
Solusi pertanyaan itu dapat diimplementasikan dalam hal ini dengan melakukan
sorted( sum(flatten(...),[]) )
. Sebaliknya tidak mungkin: meskipun benar bahwa nilai-nilai dariflatten(...)
dapat pulih dari dugaan duplikat dengan pemetaan tingkat tinggi akumulator, seseorang tidak dapat memulihkan kunci. (sunting: Juga ternyata pertanyaan pemilik duplikat dugaan itu benar-benar berbeda, karena hanya membahas kamus dengan kedalaman 2 tingkat, meskipun salah satu jawaban di halaman itu memberikan solusi umum.)sumber
Atau jika Anda sudah menggunakan panda, Anda dapat melakukannya dengan
json_normalize()
seperti:Keluaran:
sumber
Jika Anda menggunakan
pandas
ada fungsi tersembunyi dipandas.io.json._normalize
1 disebutnested_to_record
yang melakukan ini dengan tepat.1 Dalam versi panda
0.24.x
dan penggunaan yang lebih lamapandas.io.json.normalize
(tanpa_
)sumber
from pandas.io.json._normalize import nested_to_record
. Perhatikan garis bawah (_
) sebelumnyanormalize
.0.25.x
, saya sudah memperbarui jawabannya. :)Berikut adalah semacam implementasi "fungsional", "satu-liner". Itu adalah rekursif, dan berdasarkan pada ekspresi kondisional dan pemahaman dict.
Uji:
sumber
('hgf',2)
kunci 2 dalam lemparan tes AndaTypeError
+
operator. Untuk hal lain, Anda harus beradaptasiprefix + separator + k
dengan pemanggilan fungsi yang sesuai untuk menyusun objek.{'a_b':{'c':1}, 'a':{'b_c':2}}
{'name': 'Steven', 'children': [{'name': 'Jessica', 'children': []}, {'name': 'George', 'children': []}]}
Kode:
Hasil:
Saya menggunakan python3.2, perbarui untuk versi python Anda.
sumber
lkey=''
dalam definisi fungsi Anda alih-alih saat memanggil fungsi. Lihat jawaban lain dalam hal ini.Bagaimana dengan solusi fungsional dan performan di Python3.5?
Ini bahkan lebih berkinerja:
Digunakan:
sumber
reduce
sangat bagus jika Anda perlu mengurangi kamus. Saya memperbarui jawabannya. Seharusnya terlihat sedikit lebih pythonic sekarang.Ini tidak terbatas pada kamus, tetapi setiap tipe pemetaan yang mengimplementasikan .items (). Lebih lanjut lebih cepat karena menghindari kondisi jika. Namun demikian kredit jatuh ke Imran:
sumber
d
bukandict
tipe pemetaan khusus yang tidak diterapkanitems
, fungsi Anda akan gagal saat itu juga. Jadi, itu tidak bekerja untuk setiap jenis pemetaan tetapi hanya yang menerapkanitems()
.items
? Saya ingin tahu melihatnya.Solusi Python 3.3 saya menggunakan generator:
sumber
Fungsi sederhana untuk meratakan kamus bersarang. Untuk Python 3, ganti
.iteritems()
dengan.items()
Gagasan / persyaratannya adalah: Dapatkan kamus datar tanpa kunci induk.
Contoh penggunaan:
Menjaga kunci induk juga sederhana.
sumber
Memanfaatkan rekursi, menjaganya tetap sederhana dan mudah dibaca manusia:
Panggilan itu sederhana:
atau
jika kita ingin mengubah pemisah default.
Sedikit gangguan:
Ketika fungsi pertama kali dipanggil, itu disebut hanya melewati
dictionary
kita ingin meratakan. Theaccumulator
parameter di sini untuk dukungan rekursi, yang kita lihat nanti. Jadi, kami instantiateaccumulator
ke kamus kosong di mana kami akan meletakkan semua nilai yang bersarang dari aslinyadictionary
.Saat kami mengulangi nilai kamus, kami membuat kunci untuk setiap nilai. The
parent_key
Argumen akanNone
untuk panggilan pertama, sedangkan untuk setiap kamus bersarang, itu akan berisi kunci menunjuk ke sana, jadi kami tambahkan kunci itu.Dalam hal nilai
v
yangk
ditunjuk oleh kuncinya adalah kamus, fungsinya memanggil dirinya sendiri, melewati kamus bersarang,accumulator
(yang diteruskan dengan referensi, jadi semua perubahan yang dilakukan untuk itu dilakukan pada contoh yang sama) dan kuncik
sehingga kita dapat membuat kunci gabungan. Perhatikancontinue
pernyataan itu. Kami ingin melewati baris berikutnya, di luarif
blok, sehingga kamus bersarang tidak berakhir di tombolaccumulator
bawahk
.Jadi, apa yang kita lakukan seandainya nilainya
v
bukan kamus? Masukkan saja tidak berubah di dalamaccumulator
.Setelah kami selesai kami hanya mengembalikan
accumulator
, meninggalkandictionary
argumen asli tidak tersentuh.CATATAN
Ini hanya akan berfungsi dengan kamus yang memiliki string sebagai kunci. Ini akan bekerja dengan objek hash yang mengimplementasikan
__repr__
metode, tetapi akan menghasilkan hasil yang tidak diinginkan.sumber
Ini mirip dengan jawaban imran dan ralu. Itu tidak menggunakan generator, tetapi mempekerjakan rekursi dengan penutupan:
sumber
_flatten_dict
tidak pernah dikembalikan, juga tidak diharapkan akan pernah dikembalikan. Ini mungkin bisa disebut sebagai subfungsi atau fungsi tertutup sebagai gantinya.Solusi Davoud sangat bagus tetapi tidak memberikan hasil yang memuaskan ketika dict bersarang juga berisi daftar dict, tetapi kodenya diadaptasi untuk kasus tersebut:
sumber
type([])
untuk menghindari panggilan fungsi untuk setiap itemdict
.isinstance(v, list)
sebagai gantinyaJawaban di atas bekerja dengan sangat baik. Hanya berpikir saya akan menambahkan fungsi tidak rata yang saya tulis:
Catatan: Ini tidak memperhitungkan '_' yang sudah ada di kunci, sama seperti rekan-rekan rata.
sumber
Berikut adalah algoritma untuk penggantian yang elegan dan di tempat. Diuji dengan Python 2.7 dan Python 3.5. Menggunakan karakter titik sebagai pemisah.
Contoh:
Keluaran:
Saya menerbitkan kode ini di sini bersama dengan
unflatten_json
fungsi yang cocok .sumber
Jika Anda ingin flat bersarang kamus dan ingin semua daftar kunci unik maka di sini adalah solusinya:
sumber
sumber
sumber
Saya sedang memikirkan subkelas UserDict untuk secara otomatis menyamakan kunci.
Keuntungan bahwa kunci dapat ditambahkan dengan cepat, atau menggunakan instanciation standar, tanpa kejutan:
sumber
Menggunakan generator:
sumber
type(i).__name__=='dict'
dapat diganti dengantype(i) is dict
atau mungkin lebih baikisinstance(d, dict)
(atauMapping
/MutableMapping
).Menggunakan dict.popitem () dalam rekursi seperti daftar-langsung:
sumber
Tidak persis apa yang diminta OP, tetapi banyak orang datang ke sini mencari cara untuk meratakan data JSON bersarang dunia nyata yang dapat memiliki objek json dan array bernilai-nilai kunci dan objek json di dalam array dan sebagainya. JSON tidak termasuk tupel, jadi kami tidak perlu khawatir.
Saya menemukan implementasi dari daftar-inklusi komentar oleh @roneo ke jawaban yang diposting oleh @Imran :
https://github.com/ScriptSmith/socialreaper/blob/master/socialreaper/tools.py#L8
Menguji:
Annd yang melakukan pekerjaan yang perlu saya lakukan: Saya membuang json yang rumit ini dan meratakannya untuk saya.
Semua kredit ke https://github.com/ScriptSmith .
sumber
Saya benar-benar menulis paket yang disebut cherrypicker baru-baru ini untuk menangani hal-hal yang persis seperti ini karena saya harus sering melakukannya!
Saya pikir kode berikut akan memberi Anda apa yang Anda cari:
Anda dapat menginstal paket dengan:
... dan ada lebih banyak dokumen dan panduan di https://cherrypicker.readthedocs.io .
Metode lain mungkin lebih cepat, tetapi prioritas paket ini adalah untuk membuat tugas-tugas seperti itu mudah . Jika Anda memiliki daftar objek yang besar untuk diratakan, Anda juga dapat memberitahu CherryPicker untuk menggunakan pemrosesan paralel untuk mempercepat.
sumber
Saya selalu lebih suka mengakses
dict
objek melalui.items()
, jadi untuk mendatarkan dicts saya menggunakan generator rekursif berikutflat_items(d)
. Jika Anda ingin memilikinyadict
lagi, cukup bungkus seperti ini:flat = dict(flat_items(d))
sumber
Variasi kamus bersarang rata ini, mengompres kunci dengan max_level dan peredam khusus.
sumber
Jika Anda tidak keberatan dengan fungsi rekursif, berikut ini solusinya. Saya juga telah mengambil kebebasan untuk memasukkan pengecualian parameter- jika ada satu atau lebih nilai yang ingin Anda pertahankan.
Kode:
Pemakaian:
Keluaran:
sumber
Saya mencoba beberapa solusi di halaman ini - meskipun tidak semua - tetapi yang saya coba gagal menangani daftar dict yang bersarang.
Pertimbangkan dict seperti ini:
Inilah solusi darurat saya:
yang menghasilkan:
Solusi sementara dan itu tidak sempurna.
CATATAN:
itu tidak menyimpan dikte kosong seperti pasangan
address: {}
k / v.itu tidak akan meratakan dicts di tuple bersarang - meskipun akan mudah untuk menambahkan menggunakan fakta bahwa tuple python bertindak mirip dengan daftar.
sumber
Cukup gunakan
python-benedict
, ini adalah subclass dict yang menawarkan banyak fitur, termasukflatten
metode. Mungkin untuk menginstalnya menggunakan pip:pip install python-benedict
https://github.com/fabiocaccamo/python-benedict#flatten
sumber