여러 속성으로 목록을 정렬 하시겠습니까?


456

목록 목록이 있습니다.

[[12, 'tall', 'blue', 1],
[2, 'short', 'red', 9],
[4, 'tall', 'blue', 13]]

하나의 요소를 기준으로 정렬하려면 키가 큰 요소를 말하면을 통해 할 수 있습니다 s = sorted(s, key = itemgetter(1)).

나는별로 정렬하고 싶었다면 모두 키 / 짧고 색상, 나는 각 요소에 대해 한 번, 일종의 두 번 할 수 있지만 빠른 방법은 무엇입니까?



8
목록 대신 튜플 을 사용하는 경우 python 순서는 실행할 때 왼쪽에서 오른쪽으로 항목별로 정렬합니다 sort. 즉 sorted([(4, 2), (0, 3), (0, 1)]) == [(0, 1), (0, 3), (4, 2)].
Mateen Ulhaq

답변:


771

키는 튜플을 반환하는 함수일 수 있습니다.

s = sorted(s, key = lambda x: (x[1], x[2]))

또는 다음을 사용하여 동일한 결과를 얻을 수 있습니다 itemgetter(더 빠르고 Python 함수 호출을 피함).

import operator
s = sorted(s, key = operator.itemgetter(1, 2))

그리고 여기서 sort사용 sorted하고 다시 할당 하는 대신 사용할 수 있습니다 .

s.sort(key = operator.itemgetter(1, 2))

20
완성도를 들어 timeit에서 : 나를 먼저 루프 및 루프 당 초 4.4 우리 당 6에게 우리를 준위한
브라이언 라슨에게

10
첫 번째 오름차순과 두 번째 오름차순을 정렬하는 방법이 있습니까? (두 속성이 모두 문자열이라고 가정하면 -정수를 추가 하는 것과 같은 핵은 없다 )
Martin Thoma

73
어떻게 적용 할 경우 약 revrse=True에만 x[1]이 방법을 사용할 수있다?
Amyth

28
@moose, @Amyth, 하나의 속성으로 만 되돌리려면 두 번째로 정렬 할 수 있습니다. 첫 번째는 두 번째 s = sorted(s, key = operator.itemgetter(2))로, 기본적으로는 s = sorted(s, key = operator.itemgetter(1), reverse=True)이상적이지 않지만 작동합니다.
tomcounsell

52
@Amyth 또는 다른 옵션 (키가 숫자 인 경우 역순으로 만들기 위해)을 곱할 수 있습니다 -1.
Serge

37

이것이 가장 파이썬적인 방법인지 확실하지 않습니다 ... 정수 값을 내림차순으로 정렬하고 알파벳 순서로 2를 정렬 해야하는 튜플 목록이 있습니다. 이것은 알파벳 정렬이 아닌 정수 정렬을 취소해야했습니다. 내 해결책은 다음과 같습니다. (시험 btw에서 즉시 정렬 된 기능을 '중첩'할 수 있다는 것을 알지 못했습니다)

a = [('Al', 2),('Bill', 1),('Carol', 2), ('Abel', 3), ('Zeke', 2), ('Chris', 1)]  
b = sorted(sorted(a, key = lambda x : x[0]), key = lambda x : x[1], reverse = True)  
print(b)  
[('Abel', 3), ('Al', 2), ('Carol', 2), ('Zeke', 2), ('Bill', 1), ('Chris', 1)]

13
2nd는 숫자이기 때문에 b = sorted(a, key = lambda x: (-x[1], x[0]))어떤 기준이 먼저 적용되는지 더 잘 보이는 것과 같이 작동합니다 . 효율성에 관해서는 확실하지 않습니다. 누가 시간을 내야합니다.
Andrei-Niculae Petre

5

당신이 사용할 수있는 것 같습니다 list대신 것 같습니다 tuple. 이것은 목록 / 튜플의 '매직 인덱스'대신 속성을 잡을 때 더 중요하다고 생각합니다.

필자의 경우 들어오는 키가 문자열 인 클래스의 여러 속성으로 정렬하고 싶었습니다. 다른 장소에서 다른 정렬이 필요했고 클라이언트가 상호 작용하고있는 부모 클래스에 대한 일반적인 기본 정렬을 원했습니다. 실제로 '필요한'경우 '정렬 키'를 재정의해야 할뿐만 아니라 클래스가 공유 할 수있는 목록으로 키를 저장할 수있는 방식으로

먼저 도우미 메서드를 정의했습니다.

def attr_sort(self, attrs=['someAttributeString']:
  '''helper to sort by the attributes named by strings of attrs in order'''
  return lambda k: [ getattr(k, attr) for attr in attrs ]

그런 다음 사용

# would defined elsewhere but showing here for consiseness
self.SortListA = ['attrA', 'attrB']
self.SortListB = ['attrC', 'attrA']
records = .... #list of my objects to sort
records.sort(key=self.attr_sort(attrs=self.SortListA))
# perhaps later nearby or in another function
more_records = .... #another list
more_records.sort(key=self.attr_sort(attrs=self.SortListB))

생성 된 람다 함수를 사용하여 목록을 정렬 object.attrA한 다음 제공된 문자열 이름에 해당하는 getter가 object.attrB있다고 가정 object합니다. 그리고 두 번째의 경우는 기준으로 정렬 할 object.attrC다음object.attrA .

또한 소비자, 단위 테스트에서 공유 할 외부 정렬 선택 항목을 노출하거나 잠재적으로 사용자가 api의 일부 작업에 대해 정렬 방법을 알려줄 수 있습니다. 백엔드 구현에 연결


잘 했어. 속성을 다른 순서로 정렬해야한다면 어떻게해야합니까? attrA가 오름차순으로 정렬되고 attrB가 내림차순으로 정렬되어야한다고 가정합니까? 이 위에 빠른 해결책이 있습니까? 감사!
mhn_namak

4

몇 년 동안 파티에 늦었지만 두 가지 기준으로 정렬 하고 사용 하고 싶습니다 reverse=True. 다른 사람이 방법을 알고 싶어하는 경우 기준 (기능)을 괄호로 묶을 수 있습니다.

s = sorted(my_list, key=lambda i: ( criteria_1(i), criteria_2(i) ), reverse=True)

1

한 가지 방법은 다음과 같습니다. 기본적으로 정렬 함수 목록을 가져 오기 위해 정렬 함수를 다시 작성합니다. 각 정렬 함수는 테스트하려는 속성을 비교하고 각 정렬 테스트에서 cmp 함수가 0이 아닌 리턴을 리턴하는지 확인합니다. 그렇다면 중단하고 반환 값을 보내십시오. Lambda 목록의 함수 중 Lambda를 호출하여 호출합니다.

다른 방법과 달리 이전 정렬과는 달리 데이터를 단일 패스로 전달한다는 이점이 있습니다. 또 다른 것은 그것이 제자리에 정렬되는 반면에, 분류는 사본을 만드는 것 같습니다.

각 함수가 그룹에 있고 점수 함수가있는 클래스 목록의 순위를 매기는 순위 함수를 작성하는 데 사용했지만 속성 목록을 추가 할 수 있습니다. 람다를 해커가 사용하여 세터를 호출하지만, 람다와 같은 점에 유의하십시오. 순위 부분은 일련의 목록에서 작동하지 않지만 정렬됩니다.

#First, here's  a pure list version
my_sortLambdaLst = [lambda x,y:cmp(x[0], y[0]), lambda x,y:cmp(x[1], y[1])]
def multi_attribute_sort(x,y):
    r = 0
    for l in my_sortLambdaLst:
        r = l(x,y)
        if r!=0: return r #keep looping till you see a difference
    return r

Lst = [(4, 2.0), (4, 0.01), (4, 0.9), (4, 0.999),(4, 0.2), (1, 2.0), (1, 0.01), (1, 0.9), (1, 0.999), (1, 0.2) ]
Lst.sort(lambda x,y:multi_attribute_sort(x,y)) #The Lambda of the Lambda
for rec in Lst: print str(rec)

다음은 객체 목록의 순위를 매기는 방법입니다

class probe:
    def __init__(self, group, score):
        self.group = group
        self.score = score
        self.rank =-1
    def set_rank(self, r):
        self.rank = r
    def __str__(self):
        return '\t'.join([str(self.group), str(self.score), str(self.rank)]) 


def RankLst(inLst, group_lambda= lambda x:x.group, sortLambdaLst = [lambda x,y:cmp(x.group, y.group), lambda x,y:cmp(x.score, y.score)], SetRank_Lambda = lambda x, rank:x.set_rank(rank)):
    #Inner function is the only way (I could think of) to pass the sortLambdaLst into a sort function
    def multi_attribute_sort(x,y):
        r = 0
        for l in sortLambdaLst:
            r = l(x,y)
            if r!=0: return r #keep looping till you see a difference
        return r

    inLst.sort(lambda x,y:multi_attribute_sort(x,y))
    #Now Rank your probes
    rank = 0
    last_group = group_lambda(inLst[0])
    for i in range(len(inLst)):
        rec = inLst[i]
        group = group_lambda(rec)
        if last_group == group: 
            rank+=1
        else:
            rank=1
            last_group = group
        SetRank_Lambda(inLst[i], rank) #This is pure evil!! The lambda purists are gnashing their teeth

Lst = [probe(4, 2.0), probe(4, 0.01), probe(4, 0.9), probe(4, 0.999), probe(4, 0.2), probe(1, 2.0), probe(1, 0.01), probe(1, 0.9), probe(1, 0.999), probe(1, 0.2) ]

RankLst(Lst, group_lambda= lambda x:x.group, sortLambdaLst = [lambda x,y:cmp(x.group, y.group), lambda x,y:cmp(x.score, y.score)], SetRank_Lambda = lambda x, rank:x.set_rank(rank))
print '\t'.join(['group', 'score', 'rank']) 
for r in Lst: print r
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.