根据另一个列表中的值排序列表?

Leg*_*end 317 python sorting

我有一个像这样的字符串列表:

X = ["a", "b", "c", "d", "e", "f", "g", "h", "i"]
Y = [ 0,   1,   1,    0,   1,   2,   2,   0,   1]
Run Code Online (Sandbox Code Playgroud)

使用Y中的值对X进行排序以获得以下输出的最短方法是什么?

["a", "d", "h", "b", "c", "e", "i", "f", "g"]
Run Code Online (Sandbox Code Playgroud)

具有相同"密钥"的元素的顺序无关紧要.我可以使用for构造,但我很好奇是否有更短的方法.有什么建议?

Wha*_*ang 399

最短的代码

[x for _,x in sorted(zip(Y,X))]
Run Code Online (Sandbox Code Playgroud)

例:

X = ["a", "b", "c", "d", "e", "f", "g", "h", "i"]
Y = [ 0,   1,   1,    0,   1,   2,   2,   0,   1]

Z = [x for _,x in sorted(zip(Y,X))]
print(Z)  # ["a", "d", "h", "b", "c", "e", "i", "f", "g"]
Run Code Online (Sandbox Code Playgroud)

一般来说

[x for _, x in sorted(zip(Y,X), key=lambda pair: pair[0])]
Run Code Online (Sandbox Code Playgroud)

解释:

  1. zip两个人list.
  2. 创建一个新的,list基于zip使用排序sorted().
  3. 使用列表解析从排序后的拉链中提取每对的第一个元素list.

有关如何设置\使用key参数以及sorted一般功能的更多信息,请查看此内容.


  • 这是正确的,但我会添加一个注释,如果你试图通过同一个数组对多个数组进行排序,这将不会按预期工作,因为用于排序的键是(y,x) ,而不仅仅是y.您应该使用[x for(y,x)in sorted(zip(Y,X),key = lambda pair:pair [0])] (109认同)
  • @Hatefiend 有趣,你能指出如何实现这一目标的参考吗? (2认同)

Ned*_*der 102

将两个列表压缩在一起,对其进行排序,然后获取所需的部分:

>>> yx = zip(Y, X)
>>> yx
[(0, 'a'), (1, 'b'), (1, 'c'), (0, 'd'), (1, 'e'), (2, 'f'), (2, 'g'), (0, 'h'), (1, 'i')]
>>> yx.sort()
>>> yx
[(0, 'a'), (0, 'd'), (0, 'h'), (1, 'b'), (1, 'c'), (1, 'e'), (1, 'i'), (2, 'f'), (2, 'g')]
>>> x_sorted = [x for y, x in yx]
>>> x_sorted
['a', 'd', 'h', 'b', 'c', 'e', 'i', 'f', 'g']
Run Code Online (Sandbox Code Playgroud)

将这些结合在一起得到:

[x for y, x in sorted(zip(Y, X))]
Run Code Online (Sandbox Code Playgroud)

  • 如果 `X` 是 `str` 的列表,这很好,但是如果 `X` 中的某些项目对可能没有定义 `<`,请注意,例如 - 如果其中一些是 `None` (2认同)
  • 您正在使用Python3。在Python 2中,zip产生了一个列表。现在,它产生一个可迭代的对象。`sorted(zip(...))`应该仍然可以使用,或者:`them = list(zip(...)); them.sort()` (2认同)

Tom*_*Tom 72

另外,如果你不介意使用numpy数组(或者实际上已经在处理numpy数组......),这是另一个不错的解决方案:

people = ['Jim', 'Pam', 'Micheal', 'Dwight']
ages = [27, 25, 4, 9]

import numpy
people = numpy.array(people)
ages = numpy.array(ages)
inds = ages.argsort()
sortedPeople = people[inds]
Run Code Online (Sandbox Code Playgroud)

我在这里找到了它:http: //scienceoss.com/sort-one-list-by-another-list/

  • 如果它们已经是 numpy 数组,那么它只是 `sortedArray1= array1[array2.argsort()]`。这也使得按 2D 数组的特定列对多个列表进行排序变得容易:例如,`sortedArray1= array1[array2[:,2].argsort()]` 按值对 array1(可能有多个列)进行排序在 array2 的第三列中。 (2认同)

sen*_*rle 36

对我来说最明显的解决方案是使用key关键字arg.

>>> X = ["a", "b", "c", "d", "e", "f", "g", "h", "i"]
>>> Y = [ 0,   1,   1,    0,   1,   2,   2,   0,   1]
>>> keydict = dict(zip(X, Y))
>>> X.sort(key=keydict.get)
>>> X
['a', 'd', 'h', 'b', 'c', 'e', 'i', 'f', 'g']
Run Code Online (Sandbox Code Playgroud)

请注意,如果您愿意,可以将其缩短为单行:

>>> X.sort(key=dict(zip(X, Y)).get)
Run Code Online (Sandbox Code Playgroud)

  • 这是否要求 X 中的值是唯一的? (5认同)

1-i*_*ijk 12

我喜欢有一个排序索引列表.这样,我可以按照与源列表相同的顺序对任何列表进行排序.一旦你有了一个排序索引列表,一个简单的列表理解就可以了:

X = ["a", "b", "c", "d", "e", "f", "g", "h", "i"]
Y = [ 0,   1,   1,    0,   1,   2,   2,   0,   1]

sorted_y_idx_list = sorted(range(len(Y)),key=lambda x:Y[x])
Xs = [X[i] for i in sorted_y_idx_list ]

print( "Xs:", Xs )
# prints: Xs: ["a", "d", "h", "b", "c", "e", "i", "f", "g"]
Run Code Online (Sandbox Code Playgroud)

请注意,也可以使用numpy argsort()获取已排序的索引列表.


TMC*_*TMC 10

另一种选择,结合了几个答案.

zip(*sorted(zip(Y,X)))[1]
Run Code Online (Sandbox Code Playgroud)

为了工作python3:

list(zip(*sorted(zip(B,A))))[1]
Run Code Online (Sandbox Code Playgroud)


nac*_*son 10

我实际上是来这里寻找按值匹配的列表对列表进行排序的。

list_a = ['foo', 'bar', 'baz']
list_b = ['baz', 'bar', 'foo']
sorted(list_b, key=lambda x: list_a.index(x))
# ['foo', 'bar', 'baz']
Run Code Online (Sandbox Code Playgroud)

  • 这是一个坏主意。`index` 将对 `list_a` 执行 _O(N)_ 搜索,从而产生 `O(N² log N)` 排序。 (2认同)
  • @Richard:键在排序之前计算一次;所以复杂度实际上是O(N^2)。 (2认同)

pyl*_*ang 9

more_itertools 有一个用于并行迭代可迭代对象的工具:

from more_itertools import sort_together


sort_together([Y, X])[1]
# ('a', 'd', 'h', 'b', 'c', 'e', 'i', 'f', 'g')
Run Code Online (Sandbox Code Playgroud)


riz*_*iza 5

zip,按第二列排序,返回第一列。

zip(*sorted(zip(X,Y), key=operator.itemgetter(1)))[0]
Run Code Online (Sandbox Code Playgroud)


Ame*_*ina 5

这是一个老问题,但我看到的一些答案实际上不起作用,因为zip不可编写脚本。其他答案没有费心import operator提供有关此模块及其优点的更多信息。

对于这个问题,至少有两个好的习语。从您提供的示例输入开始:

X = ["a", "b", "c", "d", "e", "f", "g", "h", "i"]
Y = [ 0,   1,   1,   0,   1,   2,   2,   0,   1 ]
Run Code Online (Sandbox Code Playgroud)

使用“装饰-排序-取消装饰”习惯用法

这也被称为Schwartzian_transform ,以R. Schwartz命名,他在 90 年代在 Perl 中推广了这种模式:

# Zip (decorate), sort and unzip (undecorate).
# Converting to list to script the output and extract X
list(zip(*(sorted(zip(Y,X)))))[1]                                                                                                                       
# Results in: ('a', 'd', 'h', 'b', 'c', 'e', 'i', 'f', 'g')
Run Code Online (Sandbox Code Playgroud)

请注意,在这种情况下Y, 和X是按字典顺序排序和比较的。也就是说,Y比较第一项(来自 );如果它们相同,则X比较第二项(来自 ),依此类推。这可能会产生不稳定的输出,除非您包含字典顺序的原始列表索引以保持重复项的原始顺序。

使用operator模块

这使您可以更直接地控制如何对输入进行排序,因此您只需说明排序所依据的特定键即可获得排序稳定性。请参阅此处的更多示例。

import operator    

# Sort by Y (1) and extract X [0]
list(zip(*sorted(zip(X,Y), key=operator.itemgetter(1))))[0]                                                                                                 
# Results in: ('a', 'd', 'h', 'b', 'c', 'e', 'i', 'f', 'g')
Run Code Online (Sandbox Code Playgroud)

  • 我认为在大多数情况下我只会使用 `lambda x: x[1]` 而不是 `operator.itemgetter(1)`,因为它更容易理解并且不需要额外的包。使用“operator.itemgetter”有优势吗? (3认同)