给定并行列表,我如何对一个列表进行排序,同时以相同的方式对另一个列表进行排列(重新排列)?
- 2024-12-05 08:37:00
- admin 原创
- 150
问题描述:
假设我有:
list1 = [3, 2, 4, 1, 1]
list2 = ['three', 'two', 'four', 'one', 'one2']
调用list1.sort()
将对其进行排序,结果为[1, 1, 2, 3, 4]
。但是,我可以list2
同步重新排列 ,以获得这样的结果吗?
list1 = [1, 1, 2, 3, 4]
list2 = ['one', 'one2', 'two', 'three', 'four']
有时,人们会以不同的方式表述问题:给定两个列表,他们希望使用其中一个列表来确定另一个列表的排序顺序 - 即list2
按照 中相应值描述的顺序进行排序list1
。诀窍在于,这相当于对“键”值(list1
)进行排序,然后list2
以相同的方式重新排列。换句话说,正是这里描述的。然而,另一个问题的一些答案随后会丢弃“排序后的键”。
另请参阅:如何根据列表元素在另一个列表中出现的位置对列表进行排序? - 这是人们希望“基于”另一个列表对一个列表进行排序的另一种常见方式。在尝试关闭重复问题之前,请特别注意检查原始发帖人到底想要什么。一个关键线索:列表的长度是否需要相同?
解决方案 1:
解决这个问题的一个经典方法是使用“装饰、排序、去装饰”的习语,使用 python 的内置zip
函数尤其简单:
>>> list1 = [3,2,4,1, 1]
>>> list2 = ['three', 'two', 'four', 'one', 'one2']
>>> list1, list2 = zip(*sorted(zip(list1, list2)))
>>> list1
(1, 1, 2, 3, 4)
>>> list2
('one', 'one2', 'two', 'three', 'four')
这些当然不再是列表,但如果有关系的话,这很容易补救:
>>> list1, list2 = (list(t) for t in zip(*sorted(zip(list1, list2))))
>>> list1
[1, 1, 2, 3, 4]
>>> list2
['one', 'one2', 'two', 'three', 'four']
值得注意的是,上述代码可能会为了简洁而牺牲速度;在我的计算机上,就地版本(占用 3 行)对于小列表来说速度会稍微快一些:
>>> %timeit zip(*sorted(zip(list1, list2)))
100000 loops, best of 3: 3.3 us per loop
>>> %timeit tups = zip(list1, list2); tups.sort(); zip(*tups)
100000 loops, best of 3: 2.84 us per loop
另一方面,对于更大的列表,单行版本可能会更快:
>>> %timeit zip(*sorted(zip(list1, list2)))
100 loops, best of 3: 8.09 ms per loop
>>> %timeit tups = zip(list1, list2); tups.sort(); zip(*tups)
100 loops, best of 3: 8.51 ms per loop
正如 Quantum7 指出的那样,JSF 的建议速度仍然要快一些,但可能永远只会快一点点,因为 Python对所有基于键的排序在内部使用完全相同的 DSU 习语。它只是发生在更接近裸机的地方。(这显示了例程的优化程度zip
!)
我认为zip
基于的方法更灵活,可读性更强,所以我更喜欢它。
请注意,当的元素list1
相等时,此方法最终将比较的元素list2
。如果的元素list2
不支持比较,或者在比较时不产生布尔值(例如,如果list2
是 NumPy 数组列表),则此方法将失败,并且如果的元素list2
比较成本非常高,最好还是避免比较。
在这种情况下,您可以按照 jfs 的答案中的建议对索引进行排序,或者您可以为排序提供一个避免比较元素的关键函数list2
:
result1, result2 = zip(*sorted(zip(list1, list2), key=lambda x: x[0]))
此外,当输入为空时,作为转置的用法zip(*...)
会失败。如果您的输入可能为空,则必须单独处理这种情况。
解决方案 2:
您可以使用值作为键对索引进行排序:
indexes = range(len(list1))
indexes.sort(key=list1.__getitem__)
# Or on Python 3, where range does not return a list
indexes = sorted(range(len(list1)), key=list1.__getitem__)
要获取给定排序索引的排序列表:
sorted_list1 = map(list1.__getitem__, indexes)
sorted_list2 = map(list2.__getitem__, indexes)
# Python 3 version, converting map iterator to true list
sorted_list1 = list(map(list1.__getitem__, indexes))
sorted_list2 = list(map(list2.__getitem__, indexes))
就您而言,您不应该有list1
,list2
而应该有一对列表:
data = [(3, 'three'), (2, 'two'), (4, 'four'), (1, 'one'), (1, 'one2')]
它很容易创建;在 Python 中排序也很容易:
data.sort() # sort using a pair as a key
仅按第一个值排序:
data.sort(key=lambda pair: pair[0])
解决方案 3:
我使用 senderle 给出的答案很长时间,直到我发现np.argsort
。它的工作原理如下。
# idx works on np.array and not lists.
list1 = np.array([3,2,4,1])
list2 = np.array(["three","two","four","one"])
idx = np.argsort(list1)
list1 = np.array(list1)[idx]
list2 = np.array(list2)[idx]
我发现这个解决方案更直观,而且效果非常好。性能:
def sorting(l1, l2):
# l1 and l2 has to be numpy arrays
idx = np.argsort(l1)
return l1[idx], l2[idx]
# list1 and list2 are np.arrays here...
%timeit sorting(list1, list2)
100000 loops, best of 3: 3.53 us per loop
# This works best when the lists are NOT np.array
%timeit zip(*sorted(zip(list1, list2)))
100000 loops, best of 3: 2.41 us per loop
# 0.01us better for np.array (I think this is negligible)
%timeit tups = zip(list1, list2); tups.sort(); zip(*tups)
100000 loops, best for 3 loops: 1.96 us per loop
尽管np.argsort
它不是最快的,但我发现它更容易使用。
解决方案 4:
这可以使用 Perl 程序员所称的Schwartzian 变换(也称为装饰-排序-去装饰习语)来实现。内置的 Python 排序是稳定的,因此这两个1
s 不会引起问题。
>>> l1 = [3, 2, 4, 1, 1]
>>> l2 = ['three', 'two', 'four', 'one', 'second one']
>>> zip(*sorted(zip(l1, l2)))
[(1, 1, 2, 3, 4), ('one', 'second one', 'two', 'three', 'four')]
解决方案 5:
您可以使用zip()
和sort()
函数来实现这一点:
Python 2.6.5 (r265:79063, Jun 12 2010, 17:07:01)
[GCC 4.3.4 20090804 (release) 1] on cygwin
>>> list1 = [3,2,4,1,1]
>>> list2 = ['three', 'two', 'four', 'one', 'one2']
>>> zipped = zip(list1, list2)
>>> zipped.sort()
>>> slist1 = [i for (i, s) in zipped]
>>> slist1
[1, 1, 2, 3, 4]
>>> slist2 = [s for (i, s) in zipped]
>>> slist2
['one', 'one2', 'two', 'three', 'four']
希望这有帮助
解决方案 6:
一种方法是通过对标识 [0,1,2,..n] 进行排序来跟踪每个索引的去向
这适用于任意数量的列表。
然后将每个项目移到其位置。最好使用接头。
list1 = [3,2,4,1, 1]
list2 = ['three', 'two', 'four', 'one', 'one2']
index = list(range(len(list1)))
print(index)
'[0, 1, 2, 3, 4]'
index.sort(key = list1.__getitem__)
print(index)
'[3, 4, 1, 0, 2]'
list1[:] = [list1[i] for i in index]
list2[:] = [list2[i] for i in index]
print(list1)
print(list2)
'[1, 1, 2, 3, 4]'
"['one', 'one2', 'two', 'three', 'four']"
请注意,我们可以迭代列表,甚至无需对它们进行排序:
list1_iter = (list1[i] for i in index)
解决方案 7:
那么:
list1 = [3,2,4,1, 1]
list2 = ['three', 'two', 'four', 'one', 'one2']
sortedRes = sorted(zip(list1, list2), key=lambda x: x[0]) # use 0 or 1 depending on what you want to sort
>>> [(1, 'one'), (1, 'one2'), (2, 'two'), (3, 'three'), (4, 'four')]
解决方案 8:
如果您使用 numpy,则可以使用它np.argsort
获取已排序的索引并将这些索引应用于列表。这适用于您想要排序的任意数量的列表。
import numpy as np
arr1 = np.array([4,3,1,32,21])
arr2 = arr1 * 10
sorted_idxs = np.argsort(arr1)
print(sorted_idxs)
>>> array([2, 1, 0, 4, 3])
print(arr1[sorted_idxs])
>>> array([ 1, 3, 4, 21, 32])
print(arr2[sorted_idxs])
>>> array([ 10, 30, 40, 210, 320])
解决方案 9:
除非 list2 中有两个相同的值,否则您可以在 sorted() 方法中使用 key 参数。
代码如下:
sorted(list2, key = lambda x: list1[list2.index(x)])
它根据 list1 中的相应值对 list2 进行排序,但要确保在使用它时,list2 中没有两个值的计算结果相等,因为 list.index() 函数给出第一个值
解决方案 10:
根据@pylang对我刚刚关闭的重复问题的回答,必要的算法在流行的第三方库中实现more_itertools
,即sort_together。
因此:
from more_itertools import sort_together
list1 = [3, 2, 4, 1, 1]
list2 = ['three', 'two', 'four', 'one', 'one2']
list1, list2 = sort_together((list1, list2))
解决方案 11:
对另一个列表进行排序时保留字符串列表顺序的另一种方法如下:
list1 = [3,2,4,1, 1]
list2 = ['three', 'two', 'four', 'one', 'one2']
# sort on list1 while retaining order of string list
sorted_list1 = [y for _,y in sorted(zip(list1,list2),key=lambda x: x[0])]
sorted_list2 = sorted(list1)
print(sorted_list1)
print(sorted_list2)
输出
['one', 'one2', 'two', 'three', 'four']
[1, 1, 2, 3, 4]
解决方案 12:
如果您需要同步对 2 个以上的列表进行排序,我想建议一个解决方案:
def SortAndSyncList_Multi(ListToSort, *ListsToSync):
y = sorted(zip(ListToSort, zip(*ListsToSync)))
w = [n for n in zip(*y)]
return list(w[0]), tuple(list(a) for a in zip(*w[1]))
解决方案 13:
我想扩展 open jfs 的答案,它对我的问题非常有用:按第三个修饰列表对两个列表进行排序:
我们可以以任何方式创建我们的修饰列表,但在这种情况下,我们将从我们想要排序的两个原始列表之一的元素中创建它:
# say we have the following list and we want to sort both by the algorithms name
# (if we were to sort by the string_list, it would sort by the numerical
# value in the strings)
string_list = ["0.123 Algo. XYZ", "0.345 Algo. BCD", "0.987 Algo. ABC"]
dict_list = [{"dict_xyz": "XYZ"}, {"dict_bcd": "BCD"}, {"dict_abc": "ABC"}]
# thus we need to create the decorator list, which we can now use to sort
decorated = [text[6:] for text in string_list]
# decorated list to sort
>>> decorated
['Algo. XYZ', 'Algo. BCD', 'Algo. ABC']
现在我们可以应用jfs 的解决方案,按第三个对两个列表进行排序
# create and sort the list of indices
sorted_indices = list(range(len(string_list)))
sorted_indices.sort(key=decorated.__getitem__)
# map sorted indices to the two, original lists
sorted_stringList = list(map(string_list.__getitem__, sorted_indices))
sorted_dictList = list(map(dict_list.__getitem__, sorted_indices))
# output
>>> sorted_stringList
['0.987 Algo. ABC', '0.345 Algo. BCD', '0.123 Algo. XYZ']
>>> sorted_dictList
[{'dict_abc': 'ABC'}, {'dict_bcd': 'BCD'}, {'dict_xyz': 'XYZ'}]
解决方案 14:
newsource=[];newtarget=[]
for valueT in targetFiles:
for valueS in sourceFiles:
l1=len(valueS);l2=len(valueT);
j=0
while (j< l1):
if (str(valueT) == valueS[j:l1]) :
newsource.append(valueS)
newtarget.append(valueT)
j+=1
解决方案 15:
算法解决方案:
list1 = [3,2,4,1, 1]
list2 = ['three', 'two', 'four', 'one', 'one2']
lis = [(list1[i], list2[i]) for i in range(len(list1))]
list1.sort()
list2 = [x[1] for i in range(len(list1)) for x in lis if x[0] == i]
输出: ->
输出速度: 0.2s
>>>list1
>>>[1, 1, 2, 3, 4]
>>>list2
>>>['one', 'one2', 'two', 'three', 'four']
解决方案 16:
在 python 中对两个列表进行排序(按十进制值排序)
a = ['a','b','c','d','e','f']
b = ['0.23','80.00','5.01','6.58','1.38','79.06']
c=sorted(b,key=lambda x:float(x))
d=[]
for i in range(len(a)):
d.append(a[b.index(c[i])])
扫码咨询,免费领取项目管理大礼包!