【问题标题】:Sorting based on one of the list among Nested list in python基于python中嵌套列表中的列表之一进行排序
【发布时间】:2012-09-11 11:02:04
【问题描述】:

我有一个[[4,5,6],[2,3,1]] 的列表。现在我想根据list[1] 对列表进行排序,即输出应该是[[6,4,5],[1,2,3]]。所以基本上我是在对2,3,1 进行排序并保持list[0] 的顺序。

在搜索时,我得到了一个函数,它根据每个列表的第一个元素进行排序,但不是为此。另外我不想将列表重新创建为[[4,2],[5,3],[6,1]],然后使用该函数。

【问题讨论】:

  • 欢迎来到 Stack Overflow!我们鼓励您research your questions。如果您有 tried something already,请将其添加到问题中 - 如果没有,请先研究并尝试您的问题,然后再回来。
  • basically I am sorting 2,3,1 and maintaining the order of list[0] - 错误,[6,4,5]!= [4,5,6]?你是在your_list[1].sort()之后吗?
  • [4,5,6][6,4,5]是什么样的排序?
  • 我不认为你可以摆脱创建东西的开销 - 无论是通过 zip 还是 numpy。

标签: python list sorting nested


【解决方案1】:

由于[4, 5, 6][2, 3, 1] 有两个不同的用途,我将创建一个带有两个 参数的函数:要重新排序的列表,以及排序将决定顺序的列表。我只会返回重新排序的列表。

This answer 具有三种不同解决方案的时序,用于为排序创建排列列表。使用最快的选项给出了这个解决方案:

def pyargsort(seq):
    return sorted(range(len(seq)), key=seq.__getitem__)

def using_pyargsort(a, b):
    "Reorder the list a the same way as list b would be reordered by a normal sort"
    return [a[i] for i in pyargsort(b)]                     

print using_pyargsort([4, 5, 6], [2, 3, 1])    # [6, 4, 5]

pyargsort 方法的灵感来自 numpy argsort 方法,它做同样的事情要快得多。 Numpy 还具有高级索引操作,可以将数组用作索引,从而可以非常快速地重新排序数组。

因此,如果您对速度的需求很大,人们会认为这种 numpy 解决方案会更快:

import numpy as np

def using_numpy(a, b):
    "Reorder the list a the same way as list b would be reordered by a normal sort"
    return np.array(a)[np.argsort(b)].tolist()

print using_numpy([4, 5, 6], [2, 3, 1])     # [6, 4, 5]

但是,对于短列表(长度 a 和b 列表转换为array,然后在返回之前将结果转换回list。如果我们假设您在整个应用程序中使用 numpy 数组,这样我们就不需要来回转换,我们会得到以下解决方案:

def all_numpy(a, b):
    "Reorder array a the same way as array b would be reordered by a normal sort"
    return a[np.argsort(b)]

print all_numpy(np.array([4, 5, 6]), np.array([2, 3, 1]))    # array([6, 4, 5])

all_numpy 函数的执行速度比 using_pyargsort 函数快 10 倍。

以下对数图将这三个解决方案与其他答案中的两个替代解决方案进行了比较。参数是两个随机打乱的等长范围,并且函数都接收相同排序的列表。我只计算函数执行的时间。出于说明目的,我为每个 numpy 解决方案添加了一条额外的图表线,其中加载 numpy 的 60 毫秒开销被添加到时间中。

正如我们所见,全 numpy 解决方案比其他解决方案高出一个数量级。相比之下,从 python list 转换并返回会大大降低 using_numpy 解决方案的速度,但对于大型列表,它仍然优于纯 python。

对于大约 1'000'000 的列表长度,using_pyargsort 需要 2.0 秒,using_nympy + 开销仅为 1.3 秒,而all_numpy + 开销为 0.3 秒。

【讨论】:

  • 不需要为简单的任务安装第三方库,比如对列表进行排序...... numpy 不是标准 python 安装的一部分。
  • @l4mpi 再次阅读问题。这不是标准排序,而是以与第二项的标准排序相同的方式重新排序列表的第一项。此外,投反对票意味着“这个答案没有用”。如果有人已经使用 numpy,这个答案有用的,即使没有,它也提供了一个选项。您的反对票是不恰当的。
  • 好吧,我认为它是一种完全可行的方法。它也比 sorted/zip/unpacking 代码更容易阅读。此外,大多数 linux 发行版都将 numpy 作为标准,如果 OP 正在冒险进入这样的“与众不同”的东西 - 这是一个非常值得了解的选项......
  • 我明白他想做什么,在纯python中做起来仍然不是太难。您的回答(对于不熟悉该主题的人)给人的印象是 numpy 是标准库的一部分,并且正如您所说,仅对使用 numpy 或想要使用它的人有用(如果他全部就是这样)。
  • @l4mpi 我将编辑答案以明确 numpy 不是标准库的一部分。
【解决方案2】:

你描述的排序不是很容易完成。我能想到的唯一方法是使用zip 创建您说您不想创建的列表:

lst = [[4,5,6],[2,3,1]]
# key = operator.itemgetter(1) works too, and may be slightly faster ...
transpose_sort = sorted(zip(*lst),key = lambda x: x[1])
lst = zip(*transpose_sort)

这种限制有什么原因吗?

(另请注意,如果您真的想这样做,您可以在一行中完成所有操作:

lst = zip(*sorted(zip(*lst),key = lambda x: x[1]))

这也会产生一个元组列表。如果你真的想要一个列表,你可以map结果:

lst = map(list, lst)

或者列表推导也可以:

lst = [ list(x) for x in lst ]

【讨论】:

  • avoid map: [list(t) for t in zip(*sorted(zip(*[[4,5,6],[2,3,1]]),key = lambda x: x[1]))] 但我猜list(zip(*sorted(zip(*[[4,5,6],[2,3,1]]),key = lambda x: x[1]))[0]) 是他真正想要的。
  • @Alfe -- 我看不出有任何理由避免 map 支持列表组合(除了我认为它对 py2k 和 py3k 的工作方式相同)。
  • 对不起我的简短;我的意思是“避免使用地图”(以防万一,我通常会这样做)。
【解决方案3】:

如果第二个列表不包含重复项,您可以这样做:

l = [[4,5,6],[2,3,1]]   #the list
l1 = l[1][:]            #a copy of the to-be-sorted sublist
l[1].sort()             #sort the sublist
l[0] = [l[0][l1.index(x)] for x in l[1]] #order the first sublist accordingly

(由于这会保存子列表 l[1],因此如果您的输入列表很大,则可能是个坏主意)

【讨论】:

  • 如果 l[1] 中的数字出现多次,失败。
  • 我在答案中说明了这一点,无需反对... OP 没有指定他的列表是否包含重复项,如果确实如此,那么第一个列表的顺序无论如何都不是确定性的.例如。 [[1,2,3],[5,4,5]] 可以是 [[2,1,3],[4,5,5]][[2,3,1],[4,5,5]] ...
  • 不。如果值在 l[1] 中出现多次,则 l[0] 中的某些值将重复出现,而另一些则根本不会出现。那么您的结果不是输入的排序版本(例如,您为l = [[4,5,6],[2,2,1]] 返回[6, 4, 4]。由于提问者没有指定值不能在 l[1] 中多次出现,因此您不能假设这一点(或在如果您这样做,请至少大声指定)。
  • 你误解了我的评论......甚至没有指定如果有重复元素的正确顺序应该是什么,这将取决于排序约定(保持原始顺序与最少移动等)。而且我确实在我的答案中写了它,如果您认为它不够响亮,我可以将其编辑为全部大写:)
  • »如果存在重复,甚至没有指定元素的正确顺序应该如何,这将取决于排序约定« - 这不是我的意思。我的观点是,您的输出中缺少元素,其他元素重复。您不能将此归咎于排序规范未指定。如果你想让我相信提问者的问题可以通过“顺便说一句,第二个列表中的数字都只出现一次,保证”来自动修改,那么你还有很长的路要走。
【解决方案4】:

这个怎么样:

a = [[4,5,6],[2,3,1]]
[a[0][i] for i in sorted(range(len(a[1])), key=lambda x: a[1][x])]

它使用 numpy 的主要方式,无需使用 numpy 也无需 zip 文件。

使用 numpy 和 zipping 似乎都不是巨型结构最便宜的方式。不幸的是,.sort() 方法内置于 list 类型中,并使用硬连线访问列表中的元素(覆盖 __getitem__() 或类似的方法在这里没有任何效果)。

因此您可以实现自己的sort(),它根据一个值对两个或多个列表进行排序;这基本上就是 numpy 所做的。

或者您可以创建一个值列表以进行排序、排序,然后从中重新创建排序后的原始列表。

【讨论】:

  • 啊,我现在看到上面的 numpy 版本现在也被这个解决方案修改了(那里称为 pyargsort)。
猜你喜欢
  • 2010-09-21
  • 1970-01-01
  • 1970-01-01
  • 2019-02-24
  • 1970-01-01
  • 2013-02-06
  • 1970-01-01
相关资源
最近更新 更多