데이터를 특정 순서대로 정렬하는 것은 프로그래밍에서 매우 자주 필요한 작업입니다. 파이썬은 배열의 요소를 정렬하기 위해 내장 함수 sorted()와 리스트 메서드 sort()를 제공하며, 더 복잡한 정렬 요구사항이 있을 때는 NumPy 라이브러리의 argsort()와 lexsort() 함수를 활용할 수 있습니다.
sorted(): 원본을 유지하는 정렬
sorted() 함수는 원본 배열을 수정하지 않고, 정렬된 새로운 배열을 반환합니다.
a = [9, 5, 3, 1, 12, 6]
b = sorted(a)
print('정렬된 배열 :', b)
print('원본 배열 :', a)
실행 결과:
정렬된 배열 : [1, 3, 5, 6, 9, 12]
원본 배열 : [9, 5, 3, 1, 12, 6]
list.sort(): 원본을 직접 변경하는 제자리 정렬
sort() 메서드는 전달된 리스트를 제자리(in-place)에서 직접 수정하여 정렬합니다. 따라서 아래 예시처럼 원본 배열 자체가 변경됩니다.
a = [9, 5, 3, 1, 12, 6]
print('원본 배열 :', a)
a.sort()
print('정렬된 배열 :', a)
실행 결과:
원본 배열 : [9, 5, 3, 1, 12, 6]
정렬된 배열 : [1, 3, 5, 6, 9, 12]
두 방식의 차이를 정리하면 다음과 같습니다. sorted()는 원본의 복사본을 만든 뒤 정렬하기 때문에, 복사 과정 없이 바로 정렬하는 sort()보다 속도가 느리고 메모리도 더 사용합니다. 반면, 원본 데이터를 보존해야 하는 경우에는 sorted()가 더 안전한 선택입니다.
NumPy로 구현하는 고급 정렬
NumPy는 과학 데이터 처리 분야에서 널리 사용되는 파이썬 라이브러리로, 다양한 고급 정렬 기능을 제공합니다. 아래 예제를 통해 살펴보겠습니다.
numpy.argsort(): 정렬된 값 대신 인덱스 반환
argsort() 함수는 정렬된 배열 자체가 아니라, 배열이 정렬되었을 때의 요소 인덱스를 반환합니다. 아래 예제에서는 배열의 각 요소와 인덱스를 먼저 출력한 뒤, argsort()를 적용해 정렬 순서에 해당하는 인덱스 배열을 얻습니다.
import numpy as np
x = np.array([9, 5, 3, 1, 12, 6])
print(x)
# 각 요소의 위치와 값 출력
for i in range(len(x)):
print('[', i, ']', x[i], end=' ')
print('\n')
# 정렬된 요소의 인덱스 출력
s = np.argsort(x)
print(s)
실행 결과:
[ 9 5 3 1 12 6]
[ 0 ] 9 [ 1 ] 5 [ 2 ] 3 [ 3 ] 1 [ 4 ] 12 [ 5 ] 6
[3 2 1 5 0 4]
결과 [3 2 1 5 0 4]는 '가장 작은 값은 인덱스 3에 있는 1이고, 그다음은 인덱스 2의 3, ...'이라는 의미입니다. 이렇게 얻은 인덱스 배열을 활용하면 x[s]처럼 간단히 정렬된 배열을 재구성할 수도 있습니다.
numpy.lexsort(): 여러 키를 이용한 다중 조건 정렬
lexsort() 함수는 두 개 이상의 배열을 정렬 키로 사용하는 다중 키 정렬에 사용됩니다. 예를 들어 먼저 A열을 기준으로 정렬하고, 값이 같은 경우 B열을 기준으로 추가 정렬하는 식입니다. 아래 예제에서는 A열과 B열에 해당하는 두 개의 배열을 준비한 뒤, lexsort()를 적용해 A열 우선, B열 차선의 정렬 결과를 인덱스 배열 형태로 얻습니다.
import numpy as np
colA = np.array([2, 5, 1, 8, 1]) # 첫 번째 열
colB = np.array([9, 0, 3, 2, 0]) # 두 번째 열
# colA 기준으로 정렬하고, 같은 값은 colB 기준으로 정렬
sorted_index = np.lexsort((colB, colA))
print(sorted_index)
# 정렬 결과를 (colA, colB) 쌍으로 출력
print([(colA[i], colB[i]) for i in sorted_index])
실행 결과:
[4 2 0 1 3]
[(1, 0), (1, 3), (2, 9), (5, 0), (8, 2)]
결과를 보면 colA에서 가장 작은 값 1은 인덱스 2와 4에 위치합니다. 그런데 결과에는 2가 아니라 4가 먼저 나타납니다. 이는 colA의 값이 동일할 때 colB의 값도 함께 고려되기 때문입니다. 즉, colB 값이 0인 인덱스 4가 3인 인덱스 2보다 앞에 오도록 정렬된 것입니다. 참고로 lexsort()에 전달하는 튜플에서는 마지막에 위치한 키(colA)가 가장 우선순위가 높은 주 정렬 기준이 됩니다.