是否可以以完全相同的方式对两个列表(相互引用)进行排序?

Err*_*404 109 python

好吧,这可能不是最明智的想法,但如果可能,我有点好奇.说我有两个清单:

list1 = [3,2,4,1, 1]
list2 = [three, two, four, one, one2]
Run Code Online (Sandbox Code Playgroud)

如果我运行list1.sort(),它会对它进行排序,[1,1,2,3,4]但有没有办法让list2保持同步(所以我可以说第4项属于'3')?我的问题是我有一个非常复杂的程序,可以正常使用列表,但我需要开始引用一些数据.我知道这对于词典来说是一个完美的情况,但我正在努力避免在我的处理中使用字典,因为我确实需要对键值进行排序(如果我必须使用字典,我知道如何使用它们).

基本上这个程序的本质是,数据以随机顺序出现(如上所述),我需要对其进行排序,处理然后发送结果(顺序无关紧要,但用户需要知道哪个结果属于哪个键).我想先将它放在字典中,然后对列表进行排序,但如果不维护订单,我就无法区分具有相同值的项目(在将结果传达给用户时可能会产生影响).理想情况下,一旦我得到列表,我宁愿想出一种方法来将两个列表排序在一起.这可能吗?

sen*_*rle 194

解决这个问题的一个经典方法是使用"decorate,sort,undecorate"这个习惯用法,这个使用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')
Run Code Online (Sandbox Code Playgroud)

这些当然不再是列表,但如果重要的话,这很容易解决:

>>> list1, list2 = (list(t) for t in zip(*sorted(zip(list1, list2))))
>>> list1
[1, 1, 2, 3, 4]
>>> list2
['one', 'one2', 'two', 'three', 'four']
Run Code Online (Sandbox Code Playgroud)

值得注意的是,上述内容可能会牺牲简洁的速度; 就地版本,占用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
Run Code Online (Sandbox Code Playgroud)

另一方面,对于较大的列表,单行版本可能更快:

>>> %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
Run Code Online (Sandbox Code Playgroud)

正如Quantum7指出的那样,JSF的建议还是要快一点,但它可能只会更快一些,因为Python在内部对所有基于密钥的排序使用完全相同的DSU惯用法.它刚好接近裸金属.(这显示了zip例程的优化程度!)

我认为zip基于-Based的方法更灵活,更具可读性,所以我更喜欢它.

  • 为了详细说明上述内容,`*`运算符执行[参数解包](http://docs.python.org/tutorial/controlflow.html#unpacking-argument-lists), (8认同)
  • 第三行中的星号代表什么? (6认同)
  • list1, list2 = zip(*sorted(zip(list1, list2))) 中的第一个和第二个 zip 执行不同的操作。* 使一切变得不同。 (2认同)
  • @ashu,从某种意义上说,是的!但从另一种意义上来说,它们几乎没有什么不同。`zip(*x)` 有一个有趣的属性,它是它自己的逆:`l = [(1, 2), (3, 4)]; list(zip(*zip(*l))) == l` 返回 `True`。它实际上是一个转置运算符。`zip()` 本身是相同的运算符,但假设您已手动解压缩输入序列。 (2认同)

jfs*_*jfs 24

您可以使用值作为键对索引进行排序:

indexes = range(len(list1))
indexes.sort(key=list1.__getitem__)
Run Code Online (Sandbox Code Playgroud)

获取给定排序索引的排序列表:

sorted_list1 = map(list1.__getitem__, indexes)
sorted_list2 = map(list2.__getitem__, indexes)
Run Code Online (Sandbox Code Playgroud)

在你的情况下,你不应该有list1,list2而是一个对的列表:

data = [(3, 'three'), (2, 'two'), (4, 'four'), (1, 'one'), (1, 'one2')]
Run Code Online (Sandbox Code Playgroud)

它很容易创造; 在Python中很容易排序:

data.sort() # sort using a pair as a key
Run Code Online (Sandbox Code Playgroud)

仅按第一个值排序:

data.sort(key=lambda pair: pair[0])
Run Code Online (Sandbox Code Playgroud)

  • python 3的indexs = list(range(len(len(list1)))) (2认同)
  • 或者,可以使用“sorted_list1 = [list1[i] for i inindexes]”代替“sorted_list1 = list(map(list1.__getitem__,indexes))”。 (2认同)

Dan*_*sen 16

在发现之前,我已经使用了发送者给出的答案很长一段时间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]
Run Code Online (Sandbox Code Playgroud)

我发现这个解决方案更直观,而且效果非常好.性能:

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
Run Code Online (Sandbox Code Playgroud)

即使np.argsort不是最快的,我发现它更容易使用.


Kar*_*tel 12

施瓦茨变换.内置的Python排序是稳定的,因此两者1不会导致问题.

>>> 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')]
Run Code Online (Sandbox Code Playgroud)

  • 但是,如果您发现需要这样做,那么您应该强烈重新考虑使用两个"并行"数据列表,而不是保留2元组(对)列表......或者甚至可能实际创建一个类. (2认同)

Hun*_*len 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']
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助


rob*_*ing 5

一种方法是通过对标识 [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']"
Run Code Online (Sandbox Code Playgroud)

请注意,我们可以迭代列表,甚至无需对它们进行排序:

list1_iter = (list1[i] for i in index)
Run Code Online (Sandbox Code Playgroud)