Сортировка словаря в список
Уже есть много вопросов по сортировке словарей, но я не могу найти правильный ответ на мой вопрос.
У меня есть словарь 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]