【问题标题】:Sort list by nested tuple values按嵌套元组值排序列表
【发布时间】:2011-05-28 16:13:24
【问题描述】:

有没有比编写一个提取嵌套元组值的 itemgetter 替代方案更好的方法来按嵌套元组值对列表进行排序:

def deep_get(*idx):
  def g(t):
      for i in idx: t = t[i]
      return t
  return g

>>> l = [((2,1), 1),((1,3), 1),((3,6), 1),((4,5), 2)]
>>> sorted(l, key=deep_get(0,0))
[((1, 3), 1), ((2, 1), 1), ((3, 6), 1), ((4, 5), 2)]
>>> sorted(l, key=deep_get(0,1))
[((2, 1), 1), ((1, 3), 1), ((4, 5), 2), ((3, 6), 1)]

我考虑过使用 compose,但标准库中没有:

sorted(l, key=compose(itemgetter(1), itemgetter(0))

我在库中遗漏了什么可以使这段代码更好看的东西吗?

实现应该可以合理地处理 100k 个项目。

上下文:我想对直方图项目的字典进行排序。键是元组 (a,b),值是计数。最后,项目应按计数降序、a 和 b 排序。另一种方法是将元组展平,直接使用itemgetter,但是这样会生成很多元组。

【问题讨论】:

  • 我不知道。你的方法很好,因为它是恕我直言。
  • "实现应该可以合理地处理 100k 个项目。" -- 这行是不必要的;所有使用 sort 的实现都可以合理地处理 100k 个项目
  • @ninjagecko 如果您对 3 个项目或 100k 或 1T 进行排序,实现会有所不同。

标签: python sorting tuples


【解决方案1】:

是的,您可以使用key=lambda x: x[0][1]

【讨论】:

  • itemgetter(0)lambda x: x[0] 快吗? compose(itemgetter(1), itemgetter(0))lambda x: x[0][1]deep_get 是否具有相同的性能特征?
  • lambda 几乎肯定会比所有这些都快,但由于排序原因,它仍然是O(N log(N)),所以我不会太担心它;可能有更好的东西需要优化
  • 我认为 itemgetter 会比 lambda 更快,因为它是用 C 编写的。为什么你认为 lambda 更快?
  • @utdmr 一切都通过 C,但你仍在切换到 python;如果您的大部分计算将在 C 中完成,并且如果 C 通过避免开销而具有某种主要优势,您只能期望加速。此外,compose 是用 lambda 实现的(实际上与函数相同),因此您不会保存任何内容。欢迎您自己测试。您会发现compose 方法的运行速度慢了 50%。 deep_get 但是我希望运行的时间大致相同(实际上确实如此)。您可以随时使用dis.dis 来查看代码编译成的内容。
  • @Sven 是的,这就是为什么我说“(和函数一样的东西,真的)”来抢占这个讨论=)因为types.FunctionType==types.LambdaTypedef f(x):return x; dis.dis(f)dis.dis(lambda x:x) 产生相同的结果操作码(如果您使用 *args,**kw 调用它们。
【解决方案2】:

鉴于您拥有的数据结构,您的方法非常好。

另一种方法是使用另一种结构。

如果您想要速度,那么分解标准 NumPy 是您的最佳选择。它的工作是有效地处理大型数组。它甚至为像你这样的数组提供了一些不错的排序例程。以下是您如何根据计数编写排序,然后再编写 (a, b):

>>> arr = numpy.array([((2,1), 1),((1,3), 1),((3,6), 1),((4,5), 2)],
                  dtype=[('pos', [('a', int), ('b', int)]), ('count', int)])
>>> print numpy.sort(arr, order=['count', 'pos'])
[((1, 3), 1) ((2, 1), 1) ((3, 6), 1) ((4, 5), 2)]

这非常快(它是用 C 实现的)。

如果您想坚持使用标准 Python,包含 (count, a, b) 元组的列表将自动按照您想要的方式由 Python 进行排序(对元组使用字典顺序)。

【讨论】:

    【解决方案3】:

    这可能是您的方法的一个更快的版本:

    l = [((2,1), 1), ((1,3), 1), ((3,6), 1), ((4,5), 2)]
    
    def deep_get(*idx):
        def g(t):
            return reduce(lambda t, i: t[i], idx, t)
        return g
    
    >>> sorted(l, key=deep_get(0,1))
    [((2, 1), 1), ((1, 3), 1), ((4, 5), 2), ((3, 6), 1)]
    

    可以简写为:

    def deep_get(*idx):
        return lambda t: reduce(lambda t, i: t[i], idx, t)
    

    甚至只是简单地写出来:

    sorted(l, key=lambda t: reduce(lambda t, i: t[i], (0,1), t))
    

    【讨论】:

      【解决方案4】:

      我比较了两个类似的解决方案。第一个使用简单的 lambda:

      def sort_one(d):
          result = d.items()
          result.sort(key=lambda x: (-x[1], x[0]))
          return result
      

      注意x[1] 上的减号,因为您希望排序按计数递减。

      第二个利用了 Python 中的sort 是稳定的这一事实。首先,我们按(a, b)(升序)排序。然后我们按count排序,降序:

      def sort_two(d):
          result = d.items()
          result.sort()
          result.sort(key=itemgetter(1), reverse=True)
          return result
      

      第一个速度提高了 10-20%(在小型和大型数据集上),并且在我的 Q6600(使用一个核心)上完成 100k 个项目都在 0.5 秒内完成。所以避免创建元组似乎没有多大帮助。

      【讨论】:

        猜你喜欢
        • 2013-12-02
        • 2020-09-08
        • 2021-03-08
        • 1970-01-01
        • 2011-04-19
        • 2014-08-03
        • 1970-01-01
        • 1970-01-01
        • 2021-05-17
        相关资源
        最近更新 更多