Сортировка Dict по значению в списке вложенных Dict

#python #dictionary

#python #словарь

Вопрос:

Это сводило меня с ума в течение нескольких дней, я видел несколько вопросов о сортировке Dicts по значению, но это простые структуры, а моя сложная.

Мой ключ Dict верхнего уровня — это хэш, ключ sub Dict — это последовательность #, а значение sub Dict — это список. Последнее значение в этом — это число, по которому я хочу отсортировать Dict верхнего уровня. Размер Dict может быть довольно большим, но вот пример:

  {'16741b673a418af3812f6d43ea3f7daf': 
    {1: [0, '16741b673a418af3812f6d43ea3f7daf', 'data-01', 1132],
     2: [1, '16741b673a418af3812f6d43ea3f7daf', 'data-02', 1132],
     3: [2, '16741b673a418af3812f6d43ea3f7daf', 'data-03', 1132]},

 'cbef6de99cc2b9739c824db6d0246093':
    {4: [0, 'cbef6de99cc2b9739c824db6d0246093', 'data-04', 55296],
     5: [1, 'cbef6de99cc2b9739c824db6d0246093', 'data-05', 55296],
     6: [1, 'cbef6de99cc2b9739c824db6d0246093', 'data-06', 55296],
     7: [2, 'cbef6de99cc2b9739c824db6d0246093', 'data-07', 55296]},
 'a1e0f7ccdd8d38cb5ae00cdac71b6724':
    {8: [0, 'a1e0f7ccdd8d38cb5ae00cdac71b6724', 'data-08', 20125],
     9: [1, 'a1e0f7ccdd8d38cb5ae00cdac71b6724', 'data-09', 20125],
    10: [1, 'a1e0f7ccdd8d38cb5ae00cdac71b6724', 'data-10', 20125]}}
  

Этот код даст мне значение, которое я ищу, но только для первой итерации, затем я получаю KeyError: 1

 for item1 in mydict.items():
    print(item1[1][1][3])

item1[1] returns subkey 1's list
item1[2] returns subkey 2's list
item1[3] returns subkey 3's list
item1[1][1][3] returns subkey 1's "value"
  

Я хочу иметь возможность сортировать dict вперед и назад по этому значению. Я видел:

 sorted(data.items(), key=lambda x:x[1])
  

Я не могу понять, как применить это к моей проблеме, обычно мои попытки заканчиваются KeyError: 1 или IndexError: string вне диапазона.

Чего мне не хватает? Как я могу ссылаться на это значение для lamba? Это то, что мне нужно сделать?

Я бы предпочел не использовать решение, включающее Pandas. Я пытаюсь сделать это быстрым / эффективным, поскольку данные могут быть довольно большими (в настоящее время 10 000 подразделов)

Редактировать:

Вывод будет выглядеть так же, но отсортирован по последнему значению в списке:

  {'16741b673a418af3812f6d43ea3f7daf': 
    {1: [0, '16741b673a418af3812f6d43ea3f7daf', 'data-01', 1132],
     2: [1, '16741b673a418af3812f6d43ea3f7daf', 'data-02', 1132],
     3: [2, '16741b673a418af3812f6d43ea3f7daf', 'data-03', 1132]},

 'a1e0f7ccdd8d38cb5ae00cdac71b6724':
    {8: [0, 'a1e0f7ccdd8d38cb5ae00cdac71b6724', 'data-08', 20125],
     9: [1, 'a1e0f7ccdd8d38cb5ae00cdac71b6724', 'data-09', 20125],
    10: [1, 'a1e0f7ccdd8d38cb5ae00cdac71b6724', 'data-10', 20125]},

 'cbef6de99cc2b9739c824db6d0246093':
    {4: [0, 'cbef6de99cc2b9739c824db6d0246093', 'data-04', 55296],
     5: [1, 'cbef6de99cc2b9739c824db6d0246093', 'data-05', 55296],
     6: [1, 'cbef6de99cc2b9739c824db6d0246093', 'data-06', 55296],
     7: [2, 'cbef6de99cc2b9739c824db6d0246093', 'data-07', 55296]}}
  

Комментарии:

1. каждый хэш-ключ имеет одинаковое значение int в последнем элементе значений ключа?

2. Покажите нам, как будет выглядеть результат

3. Вы не можете отсортировать dict . Они не отсортированы по определению. Использовать collections.OrderedDict . Какой результат вы ожидаете для этого конкретного примера? Также for key1 in mydict.items(): вводит в заблуждение.

4. Хорошо, не удается отсортировать dict, как я могу добиться того, чего я хочу, воссоздать новый dict, упорядоченный по этому значению? Как?

5. @CristiFati — начиная с Python 3.7 (или 3.6, если используется реализация CPython), дикты учитывают порядок вставки.

Ответ №1:

Ваш вопрос немного неясен, я понимаю, что у вас есть {k1: {k2: [v1, v2, v3, v4]}} , вы хотите отсортировать каждую запись верхнего уровня v4 , которая должна быть одинаковой в каждом списке (так что не имеет значения, что мы выбираем). Однако вложенные записи ( k2 ) не являются постоянными между записями верхнего уровня.

Получить v4 из вложенной записи легко ( [3] или [-1] ) проблема заключается в получении произвольного значения dict второго уровня. next(iter(d.values())) следует сделать: выполнить итерацию вложенных значений (списков) и получить первое значение из итератора. Не то, чтобы это вызывало ошибку, если вложенная запись пуста (ключ верхнего уровня сопоставляется с пустым dict).

Так sorted(data.items(), key=lambda e: next(iter(e[1].values()))[-1]) должно работать:

 [('16741b673a418af3812f6d43ea3f7daf',
  {1: [0, '16741b673a418af3812f6d43ea3f7daf', 'data-01', 1132],
   2: [1, '16741b673a418af3812f6d43ea3f7daf', 'data-02', 1132],
   3: [2, '16741b673a418af3812f6d43ea3f7daf', 'data-03', 1132]}),
 ('a1e0f7ccdd8d38cb5ae00cdac71b6724',
  {8: [0, 'a1e0f7ccdd8d38cb5ae00cdac71b6724', 'data-08', 20125],
   9: [1, 'a1e0f7ccdd8d38cb5ae00cdac71b6724', 'data-09', 20125],
   10: [1, 'a1e0f7ccdd8d38cb5ae00cdac71b6724', 'data-10', 20125]}),
 ('cbef6de99cc2b9739c824db6d0246093',
  {4: [0, 'cbef6de99cc2b9739c824db6d0246093', 'data-04', 55296],
   5: [1, 'cbef6de99cc2b9739c824db6d0246093', 'data-05', 55296],
   6: [1, 'cbef6de99cc2b9739c824db6d0246093', 'data-06', 55296],
   7: [2, 'cbef6de99cc2b9739c824db6d0246093', 'data-07', 55296]})]
  

Имейте в виду, что это вернет список (key, value) кортежей, а не словарь. Вам нужно будет отправить его обратно dict (в идеале OrderedDict , возможно, обычный dict в Python 3.6 или более поздний), чтобы сохранить порядок:

 {'16741b673a418af3812f6d43ea3f7daf': 
   {1: [0, '16741b673a418af3812f6d43ea3f7daf', 'data-01', 1132],
    2: [1, '16741b673a418af3812f6d43ea3f7daf', 'data-02', 1132],
    3: [2, '16741b673a418af3812f6d43ea3f7daf', 'data-03', 1132]},
 'a1e0f7ccdd8d38cb5ae00cdac71b6724': 
   {8: [0, 'a1e0f7ccdd8d38cb5ae00cdac71b6724', 'data-08', 20125],
    9: [1, 'a1e0f7ccdd8d38cb5ae00cdac71b6724', 'data-09', 20125],
    10: [1, 'a1e0f7ccdd8d38cb5ae00cdac71b6724', 'data-10', 20125]},
 'cbef6de99cc2b9739c824db6d0246093': {
    4: [0, 'cbef6de99cc2b9739c824db6d0246093', 'data-04', 55296],
    5: [1, 'cbef6de99cc2b9739c824db6d0246093', 'data-05', 55296],
    6: [1, 'cbef6de99cc2b9739c824db6d0246093', 'data-06', 55296],
    7: [2, 'cbef6de99cc2b9739c824db6d0246093', 'data-07', 55296]}}
  

Комментарии:

1. Я думаю, вы понимаете, чего я пытаюсь достичь. Мне нравится простота вашего ответа, позвольте мне адаптировать его в мое приложение и посмотреть, что произойдет.

2. @sdproto — имейте в виду, что при этом вы будете сортировать только с учетом первого списка каждого подраздела dict, вам придется сначала отсортировать подразделы (или вложенную сортировку e[1].values() ), если последнее значение ваших списков может отличаться в одной и той же «группе».

3. Спасибо, последнее значение остается постоянным. Я понимаю, что мне нужно реструктурировать свой список и уменьшить значения dupe, но пока все в порядке.

4. @zwer да, я специально указал на это предположение (все списки имеют одинаковый v4, по которому мы сортируем) в первом абзаце моего ответа

5. @Masklinn — Список выглядит великолепно, все выглядит правильно. Извините, мне потребовалось немного времени для реализации, отличное решение. Это все, что мне нужно для преобразования обратно в dict? newdict = коллекции. OrderedDict(сортировка(MyDict.items(), ключ= лямбда e: next(итерация(e[1].значения()))[-1]))

Ответ №2:

Вот уродливый (и довольно неэффективный) вариант. Он сочетает в себе понимание dict, сортировку и получение значения dict, соответствующего 1-му из ключей (по (уродливому) d[list(d.keys())[0]] ):

 >>> data.keys()
dict_keys(['16741b673a418af3812f6d43ea3f7daf', 'cbef6de99cc2b9739c824db6d0246093', 'a1e0f7ccdd8d38cb5ae00cdac71b6724'])
>>> data_sorted = {k: v for k, v in sorted(data.items(), key=lambda x: x[1][list(x[1].keys())[0]][3])}
>>> data_sorted.keys()
dict_keys(['16741b673a418af3812f6d43ea3f7daf', 'a1e0f7ccdd8d38cb5ae00cdac71b6724', 'cbef6de99cc2b9739c824db6d0246093'])
  

У вас item1[1][1][3] ошибка ключа, потому что 1 (2) существует только в вложенном словаре '16741b673a418af3812f6d43ea3f7daf' .

Ваш dict содержит огромное количество дубликатов. Его можно было бы урезать до чего-то вроде (что также значительно упрощает выражение сортировки):

 >>> data = {("16741b673a418af3812f6d43ea3f7daf", 1132): ["data-01", "data-02", "data-03"],
...         ("cbef6de99cc2b9739c824db6d0246093", 55296): ["data-04", "data-05", "data-06", "data-07"],
...         ("a1e0f7ccdd8d38cb5ae00cdac71b6724", 20125): ["data-08", "data-09", "data-10"]}
>>>
>>> {k: v for k, v in sorted(data.items(), key=lambda x: x[0][1])}
  

Комментарии:

1. Спасибо, мне придется попробовать, спасибо за объяснение. Я понимаю, что существует много дублирования, мне было немного неловко публиковать это, но я намерен это исправить. Мне просто трудно понять, как ссылаться на разные ключи / значения на разных уровнях, поскольку на самом деле у них нет имен. Для меня становится поздно, так что завтра.

2. Смущаться не за что. Как я показал на примере, чтобы получить значение dict , получите его ключи, выберите один и получите соответствующие ему значения. Ключи имеют «имена», даже если они не являются строками (в ваших внутренних dict -ах они являются int -ами). Внутренние значения dict представляют собой списки, поэтому концепция ключа там не имеет смысла, на элементы ссылаются по индексу.

3. Мне нравится предложенная вами новая структура, единственное, мне нужен второй ключ как своего рода перекрестная ссылка, чтобы я мог обновить / удалить этот конкретный элемент. Мне нужно будет еще немного подумать, но вы дали мне отличную отправную точку, спасибо.

4. Да, легко преобразовать ваш первоначальный словарь (тот, который содержит 10 тыс. элементов) в новый формат и работать над ним. Также тот, кто создает словарь, также должен быть изменен, чтобы с этого момента соблюдать новый формат.