다른 목록의 값을 기준으로 목록을 정렬 하시겠습니까?


369

다음과 같은 문자열 목록이 있습니다.

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

다음 출력을 얻기 위해 Y의 값을 사용하여 X를 정렬하는 가장 짧은 방법은 무엇입니까?

["a", "d", "h", "b", "c", "e", "i", "f", "g"]

동일한 "키"를 갖는 요소의 순서는 중요하지 않습니다. 나는 for구조 의 사용에 의지 할 수 있지만 더 짧은 방법이 있는지 궁금합니다. 어떤 제안?


zip (* sorted (zip (X, Y), key = lambda pair : pair [0]))는 X 값으로 정렬 된 정렬 된 X와 Y를 모두 리턴하므로 riza의 답은 데이터를 플로팅 할 때 유용 할 수 있습니다.
jojo

답변:


479

최단 코드

[x for _,x in sorted(zip(Y,X))]

예:

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

일반적으로 말하면

[x for _, x in sorted(zip(Y,X), key=lambda pair: pair[0])]

설명 :

  1. ziplists.
  2. using list에 따라 새로운 정렬을 만듭니다 .zipsorted()
  3. 리스트 이해를 사용 하여 sorted, zipped에서 각 쌍의 첫 번째 요소를 추출 하십시오 list.

key파라미터와 sorted일반적인 기능 을 설정 / 사용하는 방법에 대한 자세한 내용 은 다음을 참조 하십시오 .



117
이것은 정확하지만 동일한 배열로 여러 배열을 정렬하려고하면 정렬하는 데 사용되는 키가 (y, x)이므로 예상대로 작동하지 않습니다. y뿐만 아니라 대신 [(y, x)에 대해 정렬 된 (zip (Y, X), 키 =

1
좋은 해결책! 그러나 다음과 같아야합니다. 목록은 쌍의 첫 번째 요소와 관련하여 정렬되며 이해력은 쌍의 '두 번째'요소를 추출합니다.
MasterControlProgram

이 솔루션은 스토리지 측면에서 좋지 않습니다. 적절한 정렬이 가능할 때마다 선호됩니다.
Hatefiend

107

두 목록을 압축하여 정렬 한 다음 원하는 부분을 가져갑니다.

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

이것을 결합하여 얻으십시오 :

[x for y, x in sorted(zip(Y, X))]

1
경우에 괜찮 X의 목록입니다 str,하지만 조심 가능성이있는 경우 <에 일부 항목 쌍에 대해 정의되지 않은 X, 예를 들어,이 - 그들 중 일부는라면None
존 라 Rooy

1
zip 객체에 대해 sort를 사용하려고하면 AttributeError: 'zip' object has no attribute 'sort'지금 당장 얻는 것입니다.
Ash Upadhyay

2
Python 3을 사용하고 있습니다. Python 2에서 zip은 목록을 생성했습니다. 이제 반복 가능한 객체를 생성합니다. sorted(zip(...))여전히 작동해야합니다. them = list(zip(...)); them.sort()
Ned Batchelder

77

또한 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]

나는 그것을 여기에서 발견했다 : http://scienceoss.com/sort-one-list-by-another-list/


1
더 큰 배열 / 벡터의 경우 numpy가있는이 솔루션이 유리합니다!
MasterControlProgram

1
그것들이 이미 numpy 배열이라면 간단 sortedArray1= array1[array2.argsort()]합니다. 또한 2D 배열의 특정 열을 기준으로 여러 목록을 쉽게 정렬 할 sortedArray1= array1[array2[:,2].argsort()]수 있습니다 . 예 를 들어 array1 (여러 열이있을 수 있음)을 array2의 세 번째 열 값으로 정렬 할 수 있습니다.
Aaron Bramson

40

나에게 가장 확실한 해결책은 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']

다음에 관심이 있다면 이것을 한 줄짜리로 줄이십시오.

>>> X.sort(key=dict(zip(X, Y)).get)

2
X의 값이 unqiue 여야합니까?
Jack Peng

15

실제로 값이 일치하는 목록을 기준으로 목록을 정렬하려고했습니다.

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

1
이 연기자입니까?
AFP_555

실마리 없음. 찾은 내용을 다시보고하십시오.
nackjicholson

1
이것은 나쁜 생각입니다. 정렬 결과 O (N) 검색을 index수행합니다 . list_aO(N² log N)
Richard

감사합니다. 성능이 중요 할 때이 작업을 수행하지 마십시오!
nackjicholson

15

more_itertools iterable을 병렬로 정렬하는 도구가 있습니다.

주어진

from more_itertools import sort_together


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

데모

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

13

나는 정렬 된 인덱스리스트를 좋아한다. 이렇게하면 소스 목록과 동일한 순서로 목록을 정렬 할 수 있습니다. 정렬 된 인덱스 목록이 있으면 간단한 목록 이해가 트릭을 수행합니다.

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

정렬 된 인덱스 목록은을 사용하여 얻을 수도 있습니다 numpy.argsort().


12

또 다른 대안은 여러 답변을 결합한 것입니다.

zip(*sorted(zip(Y,X)))[1]

python3에서 작동하려면 :

list(zip(*sorted(zip(B,A))))[1]

7

zip은 두 번째 열을 기준으로 정렬하여 첫 번째 열을 반환합니다.

zip(*sorted(zip(X,Y), key=operator.itemgetter(1)))[0]

참고 : key = operator.itemgetter (1)는 중복 문제를 해결합니다.
Keith

zip은 첨자가 아닙니다 ... 실제로 사용해야합니다list(zip(*sorted(zip(X,Y), key=operator.itemgetter(1))))[0]
raphael

@Keith 중복 문제는 무엇입니까?
Josh

일치하는 항목이 두 개 이상인 경우 첫 번째 항목을 가져옵니다
Keith

3

빠른 원 라이너.

list_a = [5,4,3,2,1]
list_b = [1,1.5,1.75,2,3,3.5,3.75,4,5]

목록 a를 목록과 일치 시키려고한다고 가정하십시오. b.

orderedList =  sorted(list_a, key=lambda x: list_b.index(x))

작은 목록을 큰 값으로 주문해야 할 때 유용합니다. 큰 목록에 작은 목록의 모든 값이 포함되어 있다고 가정하면 수행 할 수 있습니다.


이것은 OP의 질문을 해결하지 못합니다. 당신은 샘플 목록 X과 그것을 시도 했습니까 Y?
Aryeh Leib Taurog

이것은 나쁜 생각입니다. 정렬 결과 O (N) 검색을 index수행합니다 . list_bO(N² log N)
Richard

1

당신은을 만들 수 pandas Series와 같은 기본 목록을 사용 data하고 다른 목록을 index인덱스로 그저 다음과 :

import pandas as pd
pd.Series(data=X,index=Y).sort_index().tolist()

산출:

['a', 'd', 'h', 'b', 'c', 'e', 'i', 'f', 'g']

1

정렬 된 목록 (python3)을 모두 얻으려면 Whatangs가 대답합니다.

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

Zx, Zy = zip(*[(x, y) for x, y in sorted(zip(Y, X))])

print(list(Zx))  # [0, 0, 0, 1, 1, 1, 1, 2, 2]
print(list(Zy))  # ['a', 'd', 'h', 'b', 'c', 'e', 'i', 'f', 'g']

Zx와 Zy는 튜플이라는 것을 기억하십시오. 더 좋은 방법이 있다면 방황하고 있습니다.

경고 : 빈 목록으로 실행하면 충돌이 발생합니다.


1

@Whatang의 답변에서 영감을 얻은 다른 하나를 기준으로 두 개 이상의 목록을 정렬하는보다 일반적인 기능을 만들었습니다.

def parallel_sort(*lists):
    """
    Sorts the given lists, based on the first one.
    :param lists: lists to be sorted

    :return: a tuple containing the sorted lists
    """

    # Create the initially empty lists to later store the sorted items
    sorted_lists = tuple([] for _ in range(len(lists)))

    # Unpack the lists, sort them, zip them and iterate over them
    for t in sorted(zip(*lists)):
        # list items are now sorted based on the first list
        for i, item in enumerate(t):    # for each item...
            sorted_lists[i].append(item)  # ...store it in the appropriate list

    return sorted_lists

0
list1 = ['a','b','c','d','e','f','g','h','i']
list2 = [0,1,1,0,1,2,2,0,1]

output=[]
cur_loclist = []

고유 한 값을 얻으려면 list2

list_set = set(list2)

에서 색인 위치를 찾으려면 list2

list_str = ''.join(str(s) for s in list2)

색인 위치는 다음을 list2사용하여 추적됩니다.cur_loclist

[0, 3, 7, 1, 2, 4, 8, 5, 6]

for i in list_set:
cur_loc = list_str.find(str(i))

while cur_loc >= 0:
    cur_loclist.append(cur_loc)
    cur_loc = list_str.find(str(i),cur_loc+1)

print(cur_loclist)

for i in range(0,len(cur_loclist)):
output.append(list1[cur_loclist[i]])
print(output)

0

이것은 오래된 질문이지만 zip스크립트로 표시 할 수 없기 때문에 게시 된 답변 중 일부가 실제로 작동하지 않습니다 . 다른 답변은 import operator이 모듈과 그 이점에 대한 자세한 정보를 제공 하지 않았습니다 .

이 문제에 대해 두 가지 이상의 좋은 관용구가 있습니다. 제공 한 예제 입력으로 시작하십시오.

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

" 장식-정렬-장식 "관용구 사용

이것은 90 년대에 Perl에서이 패턴을 대중화 한 R. Schwartz에 이어 Schwartzian_transform 이라고도 합니다.

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

이 경우 참고 YX분류와 사전 식 비교된다. 즉,에서 첫 번째 항목 Y이 비교됩니다. 동일한 경우 두 번째 항목 (from 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')
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.