Сортировка словаря в список

Уже есть много вопросов по сортировке словарей, но я не могу найти правильный ответ на мой вопрос.

У меня есть словарь v:

v = {3:4.0, 1:-2.0, 10:3.5, 0:1.0}

Мы должны превратить словарь v в отсортированный список.

lijst(v) = [1.0, -2.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 3.5]

Я пробовал работать с этим кодом:

def lijst(x):
    return sorted(x.items(), key=lambda x: x[1])

Это список, который я получаю:

lijst(v) = [(1, -2.0), (0, 1.0), (10, 3.5), (3, 4.0)]

Кто-нибудь знает, как преобразовать это в список значений, отсортированных по порядку их ключа, с отсутствующими значениями, заполненными нулем?

Ответы

Ответ 1

Просто используйте itertools.chain.from_iterable, чтобы сгладить ваш результат (список кортежей):

>>> import itertools

>>> list(itertools.chain.from_iterable([(1, -2.0), (0, 1.0), (10, 3.5), (3, 4.0)]))
[1, -2.0, 0, 1.0, 10, 3.5, 3, 4.0]

Если я неправильно понял ваш первоначальный запрос, и словарь представляет собой "разреженный вектор" (где ключи являются индексами), вы можете просто заполнить список, содержащий только нули:

>>> res = [0.0]*(max(v)+1)       # create a dummy list containing only zeros
>>> for idx, val in v.items():   # populate the requested indices
...     res[idx] = val 
>>> res
[1.0, -2.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 3.5]

Или, если у вас есть NumPy, вы также можете избежать for -loop:

>>> import numpy as np

>>> arr = np.zeros(max(v)+1)
>>> arr[list(v.keys())] = list(v.values())
>>> arr
array([ 1. , -2. ,  0. ,  4. ,  0. ,  0. ,  0. ,  0. ,  0. ,  0. ,  3.5])

Последний подход основан на том факте, что, хотя порядок keys и values произволен, они тем не менее непосредственно соответствуют, пока нет модификации словаря:

Ключи и значения повторяются в произвольном порядке, который является неслучайным, варьируется в зависимости от реализаций Python и зависит от истории вложений в словари и удаления. Если ключи, значения и представления элементов повторяются без каких-либо промежуточных изменений в словаре, порядок элементов будет напрямую соответствовать.

Источник 4.10.1. Объекты просмотра словаря

Ответ 2

Вы можете попробовать это, используя chain от itertools:

from itertools import chain

v = {3:4.0, 1:-2.0, 10:3.5, 0:1.0}

final_output = list(chain(*sorted(v.items(), key=lambda x: x[1])))

Вывод:

[1, -2.0, 0, 1.0, 10, 3.5, 3, 4.0]

Ответ 3

Один из способов конкатенировать пары (ключ, значение) - с помощью sum() с начальным значением:

>>> sum(sorted(v.items(), key=lambda x:x[1]), ())
(1, -2.0, 0, 1.0, 10, 3.5, 3, 4.0)

Возвращает кортеж. Передайте его list(), если вам действительно нужен список.

P.S. Как справедливо отметили @MSeifert в комментариях, это почти наверняка имеет временную сложность O (n ** 2), тогда как list(chain(...)), скорее всего, амортизируется линейным.

Ответ 4

Другой вариант - использовать синтаксис yield from представленный в Python 3.3:

>>> lst = [(1, -2.0), (0, 1.0), (10, 3.5), (3, 4.0)]
>>> list([(yield from tup) for tup in lst])
[1, -2.0, 0, 1.0, 10, 3.5, 3, 4.0]
>>> 

Предостережение. Обратите внимание, что использование yield from таким образом внутри понимания списка может не быть "официальным синтаксисом", а некоторые (включая Guido) считают ошибка.

Ответ 5

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

если вы хотите сохранить держатели 0.0 для предметов, которые недоступны:

[v.get(i, 0.0) for i in range(max(v.keys())+1)]

выход:

[1.0, -2.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 3.5]

Если вы не хотите, чтобы владельцы мест 0.0 могли использовать:

[v.get(i) for i in range(max(v.keys())+1) if v.get(i) is not None]

выход:

[1.0, -2.0, 4.0, 3.5]

Объяснение:

когда вы используете range(), он будет генерировать отсортированный список, поэтому вам не придется беспокоиться о сортировке, тогда он попытается получить элементы из словаря в соответствии с этим списком. В первом примере, если ключ не существует, возвращается 0.0, а во втором примере None будет возвращен и будет проигнорирован из-за if-statement в выражении.

EDIT:

Как упоминалось в христианстве, вы можете изменить второй вариант для большей эффективности:

[v[i] for i in range(max(v.keys())+1) if i in v]

Это позволит избежать вызова v.get(i) дважды.

Ответ 6

Это не является строго ответом на вопрос, а скорее пытается понять, чего вы, возможно, пытаетесь достичь. Если вы пытаетесь реализовать разреженные векторы, прежде чем тратить время на новую реализацию, вы можете захотеть заглянуть в scipy.sparse.

Например:

from scipy.sparse import dok_matrix
v = {3:4.0, 1:-2.0, 10:3.5, 0:1.0}
m = dok_matrix((11,1))
m.update(v)

Преимущество разреженных матриц состоит в том, что (в зависимости от доли ненулевых элементов) они могут занимать меньше памяти и/или допускать более быстрые вычисления.

Ответ 7

v = {3:4.0, 1:-2.0, 10:3.5, 0:1.0}
print sorted(v.values())

Результат

[-2.0, 1.0, 3.5, 4.0]