Предположим, у вас есть словарь, как:
{'a': 1,
'c': {'a': 2,
'b': {'x': 5,
'y' : 10}},
'd': [1, 2, 3]}
Как бы вы пошли на то, чтобы сгладить это в нечто вроде:
{'a': 1,
'c_a': 2,
'c_b_x': 5,
'c_b_y': 10,
'd': [1, 2, 3]}
python
dictionary
Тиммес
источник
источник
Ответы:
По сути, точно так же, как вы бы выровняли вложенный список, вам просто нужно проделать дополнительную работу для итерации dict по ключу / значению, создания новых ключей для вашего нового словаря и создания словаря на последнем этапе.
источник
isinstance
сtry..except
блоком, это будет работать для любого отображения, даже если он не является производным отdict
.collections.MutableMapping
чтобы сделать его более общим. Но для Python <2.6 это,try..except
вероятно, лучший вариант.if isinstance(v, collections.MutableMapping):
наif v and isinstance(v, collections.MutableMapping):
new_key = parent_key + sep + k if parent_key else k
предполагается, что ключи всегда являются строками, в противном случае он будет повышатьсяTypeError: cannot concatenate 'str' and [other] objects
. Тем не менее, вы можете исправить это, просто вызвавk
string (str(k)
) или объединяя ключи в кортеж вместо строки (кортежи также могут быть ключами dict).Есть два больших соображения, которые должен учитывать оригинальный постер:
{'a_b':{'c':1}, 'a':{'b_c':2}}
приведет к{'a_b_c':???}
. Приведенное ниже решение уклоняется от проблемы, возвращая итерацию пар.joinedKey = '_'.join(*keys)
, что это будет стоить вам O (N ^ 2) времени выполнения. Однако, если вы готовы сказатьnextKey = previousKey+'_'+thisKey
, это дает вам O (N) время. Приведенное ниже решение позволяет вам использовать оба способа (поскольку вы можете просто объединить все ключи, а затем обработать их).(Производительность вряд ли является проблемой, но я остановлюсь на втором пункте на случай, если кого-то еще это волнует: при реализации этого существует множество опасных вариантов. Если вы делаете это рекурсивно и получаете и повторно приносите, или что-нибудь эквивалентное, что касается узлы более одного раза (что довольно легко сделать случайно), вы выполняете потенциально O (N ^ 2) работу, а не O (N). Это потому, что, возможно, вы вычисляете ключ,
a
аa_1
затемa_1_i
..., а затем вычисляетеa
тоa_1
тогдаa_1_ii
..., но на самом деле вы не должны рассчитыватьa_1
снова. Даже если вы не пересчитывая его, повторно уступающее это ( «уровня по-уровень» подходом) так же плохо. хороший пример может служить думать о спектакле{1:{1:{1:{1:...(N times)...{1:SOME_LARGE_DICTIONARY_OF_SIZE_N}...}}}}
)Ниже приведена функция, которую я написал,
flattenDict(d, join=..., lift=...)
которая может быть адаптирована для многих целей и может делать то, что вы хотите. К сожалению, довольно трудно сделать ленивую версию этой функции, не неся вышеупомянутые потери производительности (многие встроенные функции Python, такие как chain.from_iterable, на самом деле не эффективны, что я понял только после всестороннего тестирования трех различных версий этого кода, прежде чем остановиться на вот этот).Чтобы лучше понять, что происходит, ниже приведена схема для тех, кто не знаком с
reduce
(слева), иначе называемый «сложить влево». Иногда он рисуется с начальным значением вместо k0 (не является частью списка, переданного в функцию). ЗдесьJ
нашаjoin
функция. Мы препроцессируем каждый k n сlift(k)
.Фактически это то же самое, что
functools.reduce
и наша функция, которая делает это со всеми путями дерева.Демонстрация (которую я бы положил в строку документации):
Производительность:
... вздох, не думай, что это моя вина ...
[неважная историческая справка из-за проблем с модерацией]
Относительно предполагаемого дубликата Flatten словарь словарей (2 уровня глубиной) списков в Python :
Решение этого вопроса может быть реализовано с помощью этого
sorted( sum(flatten(...),[]) )
. Обратное невозможно: в то время как это верно , что значения изflatten(...)
могут быть извлечены из предполагаемого дубликата путем сопоставления аккумулятора более высокого порядка, не может восстановить ключи. (Отредактируйте также: оказывается, что вопрос о предполагаемом дубликате владельца совершенно другой, поскольку он имеет дело только со словарями ровно 2-уровневого уровня, хотя один из ответов на этой странице дает общее решение.)источник
Или, если вы уже используете панд, вы можете сделать это
json_normalize()
так:Вывод:
источник
Если вы используете
pandas
есть функция спрятана вpandas.io.json._normalize
1 называется ,nested_to_record
которая делает это точно.1 В версиях для панд
0.24.x
и старых использованияpandas.io.json.normalize
(без_
)источник
from pandas.io.json._normalize import nested_to_record
. Обратите внимание на подчеркивание (_
) раньшеnormalize
.0.25.x
, я обновил ответ. :)Это своего рода «функциональная», «однострочная» реализация. Он рекурсивен и основан на условном выражении и понимании слова.
Тест:
источник
('hgf',2)
2-го ключа в ваших тестовых броскахTypeError
+
оператор. Для всего остального вам нужно будет приспособитьсяprefix + separator + k
к соответствующему вызову функции для составления объектов.{'a_b':{'c':1}, 'a':{'b_c':2}}
{'name': 'Steven', 'children': [{'name': 'Jessica', 'children': []}, {'name': 'George', 'children': []}]}
Код:
Полученные результаты:
Я использую python3.2, обновление для вашей версии python.
источник
lkey=''
в вашем определении функции, а не при вызове функции. Смотрите другие ответы на этот счет.Как насчет функционального и производительного решения в Python3.5?
Это еще более производительно:
В использовании:
источник
reduce
это здорово, если вам нужно уменьшить словари. Я обновил ответ. Теперь должен выглядеть немного более питоническим.Это не ограничивается словарями, но каждым типом отображения, который реализует .items (). Далее это быстрее, так как избегает условия if. Тем не менее кредиты идут в Имран:
источник
d
это неdict
пользовательский тип отображения, который не реализуетitems
, ваша функция сразу же потерпит неудачу. Таким образом, он работает не для каждого типа отображения, а только для тех, которые реализуютitems()
.items
? Мне было бы любопытно увидеть один.My Python 3.3 Solution с использованием генераторов:
источник
Использование рекурсии, простота и удобочитаемость:
Звонить просто:
или
если мы хотим изменить разделитель по умолчанию.
Небольшая поломка:
Когда функция вызывается впервые, она вызывается только в том случае, если
dictionary
мы хотим сплющить.accumulator
Параметр здесь для поддержки рекурсии, которую мы увидим позже. Итак, мы создаем экземплярaccumulator
пустого словаря, в который мы поместим все вложенные значения из оригиналаdictionary
.Перебирая значения словаря, мы создаем ключ для каждого значения.
parent_key
Аргумент будетNone
для первого вызова, в то время как для каждого вложенного словаря, он будет содержать ключ , указывающий на него, так что мы предварять этот ключ.В случае, если значение,
v
наk
которое указывает ключ, является словарем, функция вызывает себя, передавая вложенный словарь,accumulator
(который передается по ссылке, поэтому все изменения, сделанные в нем, выполняются в одном и том же экземпляре) и ключ,k
так что мы может построить связанный ключ. Обратите внимание наcontinue
утверждение. Мы хотим пропустить следующую строку внеif
блока, чтобы вложенный словарь не попадал вaccumulator
нижнюю клавишуk
.Итак, что нам делать, если значение
v
не словарь? Просто поместите это без изменений вaccumulator
.Как только мы закончим, мы просто возвращаем
accumulator
, оставляя исходныйdictionary
аргумент без изменений.НОТА
Это будет работать только со словарями, которые имеют строки в качестве ключей. Он будет работать с хешируемыми объектами, реализующими
__repr__
метод, но даст нежелательные результаты.источник
Простая функция для выравнивания вложенных словарей. Для Python 3 заменить
.iteritems()
на.items()
Идея / требование было: получить плоские словари без сохранения родительских ключей.
Пример использования:
Хранить родительские ключи также просто.
источник
Это похоже на ответ Имрана и Ралу. Он не использует генератор, но вместо этого использует рекурсию с замыканием:
источник
_flatten_dict
никогда не возвращается и не ожидается. Возможно, вместо этого его можно назвать подфункцией или вложенной функцией .Решение Давуда очень приятно, но не дает удовлетворительных результатов, когда вложенный диктат также содержит списки диктов, но его код должен быть адаптирован для этого случая:
источник
type([])
чтобы избежать вызова функции для каждого элементаdict
.isinstance(v, list)
вместо этогоОтветы выше работают очень хорошо. Просто подумал, что я добавлю функцию unlatten, которую я написал:
Примечание: это не учитывает '_', уже присутствующее в ключах, во многом как сглаженные аналоги.
источник
Вот алгоритм для элегантной замены на месте. Протестировано с Python 2.7 и Python 3.5. Использование символа точки в качестве разделителя.
Пример:
Вывод:
Я опубликовал этот код здесь вместе с
unflatten_json
функцией соответствия .источник
Если вы хотите создать вложенный словарь и получить список всех уникальных ключей, то вот решение:
источник
источник
источник
Я думал о подклассе UserDict для автоматического выравнивания ключей.
Преимущества в том, что ключи могут быть добавлены на лету, или с использованием стандартной инстанции dict, без удивления:
источник
Использование генераторов:
источник
type(i).__name__=='dict'
может быть замененtype(i) is dict
или, возможно, даже лучшеisinstance(d, dict)
(илиMapping
/MutableMapping
).Использование dict.popitem () в простой рекурсии типа вложенного списка:
источник
Не совсем то, о чем спрашивал ОП, но многие приходят сюда в поисках способов сглаживания реальных JSON-данных, которые могут содержать вложенные объекты-значения json и массивы, а также объекты json внутри массивов и так далее. JSON не включает в себя кортежи, поэтому нам не нужно беспокоиться о них.
Я нашел реализацию комментария включения в список @roneo к ответу, опубликованному @Imran :
https://github.com/ScriptSmith/socialreaper/blob/master/socialreaper/tools.py#L8
Попробуй это:
И это делает ту работу, в которой я нуждаюсь: я добавляю любой сложный json в это, и это выравнивает это для меня.
Все кредиты для https://github.com/ScriptSmith .
источник
На самом деле я недавно написал пакет под названием cherrypicker, чтобы разобраться с такими вещами, поскольку мне приходилось делать это так часто!
Я думаю, что следующий код даст вам именно то, что вы ищете:
Вы можете установить пакет с помощью:
... а также другие документы и рекомендации на https://cherrypicker.readthedocs.io .
Другие методы могут быть быстрее, но приоритет этого пакета , чтобы сделать такие задачи легко . Если у вас есть большой список объектов, которые нужно сгладить, вы также можете указать CherryPicker использовать параллельную обработку для ускорения процесса.
источник
Я всегда предпочитаю доступ к
dict
объектам через.items()
, поэтому для сглаживания диктов я использую следующий рекурсивный генераторflat_items(d)
. Если вам нравится иметьdict
снова, просто оберните это так:flat = dict(flat_items(d))
источник
Вариант этого Flatten - вложенные словари, сжатие ключей с max_level и пользовательский редуктор.
источник
Если вы не возражаете против рекурсивных функций, вот решение. Я также позволил себе включить параметр исключения, если есть одно или несколько значений, которые вы хотите сохранить.
Код:
Использование:
Вывод:
источник
Я попробовал некоторые из решений на этой странице - хотя и не все - но те, которые я попробовал, не смогли обработать вложенный список dict.
Рассмотрим что-то вроде этого:
Вот мое временное решение:
который производит:
Временное решение, и оно не идеально.
НОТА:
он не хранит пустых кодов, таких как
address: {}
пара k / v.это не сгладит разногласия во вложенных кортежах - хотя было бы легко добавить, используя тот факт, что кортежи python действуют подобно спискам.
источник
Просто используйте
python-benedict
, это подкласс dict, который предлагает множество функций, включаяflatten
метод. Его можно установить с помощью pip:pip install python-benedict
https://github.com/fabiocaccamo/python-benedict#flatten
источник