【问题标题】:Sort dictionary into list将字典排序到列表中
【发布时间】:2018-01-10 03:07:36
【问题描述】:

已经有很多关于排序字典的问题,但我找不到我的问题的正确答案。

我有字典 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.0, -2.0, 0.0, 4.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 3.5],有些给出[1, -2.0, 0, 1.0, 10, 3.5, 3, 4.0])是“扁平化列表”意味着非常 与您实际寻找的答案不同。如果您将最后一个问题更改为“有人知道如何将其转换为按键排序的值列表,缺失值用零填充吗?”,您会得到更一致的答案?
  • @SethMMorton 谢谢你,我已经编辑了问题。

标签: python list sorting dictionary


【解决方案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])

最后一种方法依赖于这样一个事实,即使keysvalues 的顺序是任意的,只要没有修改字典,它们仍然直接对应:

键和值以非随机的任意顺序迭代,随 Python 实现而变化,并且取决于字典的插入和删除历史。 如果键、值和项视图被迭代而没有对字典进行干预修改,则项的顺序将直接对应。

来源4.10.1. Dictionary view objects

【讨论】:

    【解决方案2】:

    您可以使用来自itertoolschain 尝试此操作:

    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()

      附:正如 @MSeifert 在 cmets 中正确指出的那样,这几乎肯定具有 O(n**2) 时间复杂度,而 list(chain(...)) 可能是线性摊销。

      【讨论】:

      • 请注意,这将(可能)具有O(n**2) 运行时行为。
      • @MSeifert:很好,谢谢。我已经在答案中添加了警告。
      【解决方案4】:

      另一种选择是使用yield from 语法introduced in 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)consider it a bug

      【讨论】:

      • 尽管它是一个有趣的替代方案 (+1):使用列表推导的副作用通常不被认为是 Pythonic。
      • @MSeifert 真的吗?我不认为这是对副作用使用列表理解。我的理解是 [(yield from tup) for tup in lst] 返回一个生成器表达式。这被传递给list(),它在生成器上重复调用next,并且每次都返回(yield from tup)。正因为如此,它似乎在使用实际的列表理解,而不仅仅是它的副作用。如果我错了,请纠正我?
      • 我不太确定,但列表理解实际上确实创建了一个 Nones 列表(即使您从未看到它们,因为它们最终附加到 StopIteration)而外部 list() 只是捕获了产生的值。但是我有seen people calling it a bug(但我不清楚哪个部分是“错误部分”)所以我绝对不确定是否应该将其作为官方支持的语法来提倡。
      • @MSeifert 啊,我明白了。谢谢。关于理解中yield from 的性质,我将在我的回答中添加一个警告。
      • 注意:这在 3.8 中不起作用,在 3.7 中已弃用
      【解决方案5】:

      你可以使用list-comprehension来实现你想要的,例如:

      如果您想为不可用的项目保留 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 而将被忽略。

      编辑:

      正如 Christian 所说,您可以将第二个选项更改为:

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

      这样可以避免两次调用v.get(i)

      【讨论】:

      • 您的第二个解决方案必须两次调用dict.get,因此效率低下。您可以改为简单地测试i 是否在v 的键中,然后添加v[i] 如果是:[v[i] for i in range(max(v.keys()) + 1) if i in v]。这种方法速度快了大约两倍。
      【解决方案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]
        

        【讨论】:

          猜你喜欢
          • 2013-02-19
          • 1970-01-01
          • 1970-01-01
          • 2020-04-30
          • 1970-01-01
          • 1970-01-01
          • 2011-09-23
          • 1970-01-01
          相关资源
          最近更新 更多