정확히 같은 방식으로 두 목록 (서로 참조)을 정렬하는 방법


139

두 가지 목록이 있다고 가정 해보십시오.

list1 = [3, 2, 4, 1, 1]
list2 = ['three', 'two', 'four', 'one', 'one2']

내가 실행하면 list1.sort()정렬 [1,1,2,3,4]하지만 list2동기화 하는 방법이 있습니까 (그래서 항목이에 4속 한다고 말할 수 'three'있습니까)? 따라서 예상되는 결과는 다음과 같습니다.

list1 = [1, 1, 2, 3, 4]
list2 = ['one', 'one2', 'two', 'three', 'four']

내 문제는 목록으로 잘 작동하는 꽤 복잡한 프로그램이 있지만 일부 데이터를 참조해야한다는 것입니다. 나는 이것이 사전에 대한 완벽한 상황이라는 것을 알고 있지만 키 값을 정렬해야하기 때문에 처리 과정에서 사전을 피하려고합니다 (사전을 사용해야하는 경우 사용 방법을 알고 있습니다).

기본적 으로이 프로그램의 본질은 데이터가 임의의 순서로 (위와 같이) 나옵니다. 데이터를 정렬하고 처리 한 다음 결과를 보내야합니다 (순서는 중요하지 않지만 사용자는 어떤 결과가 키). 먼저 사전에 넣은 다음 목록 하나를 정렬하는 방법에 대해 생각했지만 순서가 유지되지 않으면 동일한 값으로 항목을 구분하는 방법이 없습니다 (결과를 사용자에게 전달할 때 영향을 줄 수 있음). 이상적으로는 일단 목록을 얻으면 두 목록을 함께 정렬하는 방법을 찾는 것이 좋습니다. 이게 가능해?


list2의 변수가 list1의 정수를 가리 키지 않는다는 것을 지적해야합니다. 예를 들어 list1 [0] = 9와 같은 값을 변경하고 list2를 보면 list2 [0]은 여전히 ​​3입니다. 파이썬에서 정수를 사용하면 참조 / 포인터를 사용하지 않고 값을 복사합니다. list2 = list1 [:]
robert king

답변:


242

이 문제에 대한 한 가지 고전적인 접근 방식은 "장식, 정렬, 장식 해제"관용구를 사용하는 것입니다. 이는 파이썬의 내장 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의 제안 은 여전히 ​​더 빠르지 만 파이썬은 모든 키 기반 정렬에 대해 내부적으로 동일한 DSU 관용구를 사용하기 때문에 조금 더 빠를 것 입니다. 베어 메탈에 조금 더 가깝습니다. (이것은 zip루틴이 얼마나 최적화되었는지 보여줍니다 !)

나는 zip기반 접근 방식이 더 유연하고 약간 더 읽기 쉽다고 생각 하므로 선호합니다.


6
세 번째 줄의 별표는 무엇을 나타 냅니까?
Jeffrey

8
위의 내용을 자세히 설명하기 위해 *연산자는 인수 언 패킹을 수행합니다 .
senderle

1
JF Sebastian이 제안한 정렬 된 인덱스 / 맵 패러다임은 임의의 zip 솔루션 (10000 임의의 정수 목록 사용)보다 10 % 빠릅니다. % timeit index = range (len (l1)); index.sort (키 = l1 .__ getitem__); 지도 (l1 .__ getitem__, index); map (l2 .__ getitem__, index) 100 개의 루프, 루프 당 3 : 3.64ms (vs 9.17ms, 센더의 소심의 경우 9.07ms) 중
최고

1
list1, list2 = zip (* sorted (zip (list1, list2)))의 첫 번째와 두 번째 zip은 다른 작업을 수행합니다. *는 모든 차이를 만듭니다.
ashu

1
@ashu, 어떤 의미에서, 그렇습니다! 그러나 다른 의미에서는 전혀 다르지 않습니다. zip(*x)그것 자체의 역수라는 흥미로운 속성을 가지고 있습니다 : l = [(1, 2), (3, 4)]; list(zip(*zip(*l))) == lreturns True. 효과적으로 조옮김 연산자입니다. zip()자체는 동일한 연산자이지만 입력 시퀀스를 수동으로 압축 해제 한 것으로 가정합니다.
senderle

30

값을 키로 사용하여 색인을 정렬 할 수 있습니다.

indexes = range(len(list1))
indexes.sort(key=list1.__getitem__)

정렬 된 색인이 제공된 정렬 된 목록을 얻으려면 다음을 수행하십시오.

sorted_list1 = map(list1.__getitem__, indexes)
sorted_list2 = map(list2.__getitem__, indexes)

귀하의 경우에는 list1, list2대신 단일 쌍의 목록 이 있어야합니다 .

data = [(3, 'three'), (2, 'two'), (4, 'four'), (1, 'one'), (1, 'one2')]

작성하기 쉽습니다. 파이썬으로 정렬하는 것은 쉽습니다.

data.sort() # sort using a pair as a key

첫 번째 값으로 만 정렬하십시오.

data.sort(key=lambda pair: pair[0])

이것에 대한 멋진 점은 list1이 여러 다른 배열에 영향을 미치는 중요한 좌표 인 경우 인덱스를 유지하고 나중에 다른 것을 정렬 할 수 있다는 것입니다.
EL_DON

3
파이썬 3에 대한 인덱스 = list (range (len (list1)))
DonQuiKong

@DonQuiKong 파이썬 3에서이 코드를 사용 하려면 list() 주위 를 둘러 봐야합니다.map()
jfs

또는 대신에 sorted_list1 = list(map(list1.__getitem__, indexes))할 수 sorted_list1 = [list1[i] for i in indexes]있습니다.
나단

20

나는 센더 레가 보낸 답을 오랫동안 발견했다 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되지 가장 빠른 내가 사용하기 쉽게 찾을 수있다.


1
예제를 실행하는 동안 오류가 발생했습니다 : TypeError: only integer arrays with one element can be converted to an index(Python 2.7.6, numpy 1.8.2). 이 문제를 해결하려면 list1 및 list2를 numpy 배열로 선언해야합니다.
BenB

감사. 이것이 함수의 주석에 쓴 것이 아닙니까? 어쨌든 내부적 np.argsort으로 변환하려고 시도하지 않는 것은 어리석은 일이라고 생각합니다 np.array.
Daniel Thaagaard Andreasen

나는 작성된대로 실행되지 않기 때문에 첫 번째 코드 스 니펫을 언급했다 :)
BenB

numpy 배열에 할당 될 때 목록을 변환하여 수정했습니다. 댓글 주셔서 감사합니다 :)
Daniel Thaagaard Andreasen

이제 그들은 Numpy 배열로 두 번 변환됩니다;)
BenB

13

슈바르츠 식 변환 . 내장 파이썬 정렬은 안정적이므로 두 가지로 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')]

2
그러나이 작업을 수행해야하는 경우 2 개의 튜플 (쌍) 목록을 유지하거나 실제로 클래스를 작성하는 것과는 대조적으로 두 개의 "병렬"데이터 목록을 갖는 것이 좋습니다. .
Karl Knechtel

3

이건 어떤가요:

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')]

2

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']

도움이 되었기를 바랍니다


2

list2에 두 개의 동일한 값이 없으면 sorted () 메소드에서 키 인수를 사용할 수 있습니다.

코드는 다음과 같습니다.

sorted(list2, key = lambda x: list1[list2.index(x)]) 

list1의 해당 값에 따라 list2를 정렬하지만 list.index () 함수가 첫 번째 값을 제공하므로 list2의 두 값이 같은 것으로 평가되지 않아야합니다.


정렬은 작동하지만 어떤 조건에서는 다소 느립니다.
tyan

2

한 가지 방법은 ID [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)

1

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])

0

알고리즘 솔루션 :

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']

0

다른 목록을 기준으로 정렬 할 때 문자열 목록의 순서를 유지하는 또 다른 방법은 다음과 같습니다.

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]

0

나는 개방 확대하고자하는 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'}]

편집 : 이봐, 내가 이것에 대해 블록 게시물을 만들었습니다, 당신이 그것을 느끼면 그것을 확인하십시오 :) 🐍🐍🐍


-1
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

2
몇 줄의 설명이 도움이 될 것입니다
saiedmomen

@saiedmomen stackoverflow.com/questions/53829160/… 을 참조하여 게시했습니다. 여기서 대상 문자열은 소스 문자열에서 검색됩니다.
user10340258
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.