본문 바로가기
Python

파이썬 정렬 함수 sorted()에 대한 개념과 활용!

by 산코디 2024. 5. 20.

파이썬에서 sorted() 함수는 정렬을 도와주는 함수중 하나다. 프로그래밍에서 정렬은 없어서는 안 될 중요한 기능 중 하나다. 어떤 언어에서든지 필요한 기능이긴 한데, 파이썬의 sorted() 함수는 어떤 특징과 성능이 있는지 자세히 살펴보자.



sorted() 함수의 개념

sorted() 함수는 반복 가능한 객체(리스트, 튜플 등)를 기준으로 해당 객체를 정렬한 새로운 객체를 반환한다. 새로운 객체를 반환하기 때문에 원본 데이터를 변경하지 않는 특징이 있다.


원본 데이터
sorted() 함수는 정렬된 새로운 리스트를 반환하기 때문에 원본 데이터를 변경하지 않는다.

정렬
기본적으로는 오름차순으로 정렬되며, reverse=True 인자를 사용하면 내림차순 정렬로 반환한다.

정렬 기준
key 인자를 활용하여 사용자가 지정한 기준에 따라 정렬할 수 있으며, 이를 통해 복잡한 데이터 구조에서 특정 속성이나 함수를 기준으로 정렬할 수 있다.

데이터 유형
sorted() 함수는 다양한 데이터 유형을 지원하는데, 숫자, 문자, 객체 등 다양하게 정렬할 수 있다.

시간 복잡도
sorted() 함수는 일반적으로 Timesort 알고리즘을 사용하여 빠른 정렬을 제공한다. 이 알고리즘은 최선의 경우 O(nlogn)의 시간 복잡도를 가지며, 이미 정렬된 경우에는 더 빠르게 처리한다.


이렇게 함수의 간단한 특징들을 정리해 보았고, 생각보다 다양한 데이터 유형을 기준으로 정렬할 수 있어서 활용도가 높다고 할 수 있다. 또한 빠른 성능을 낼 수 있는 알고리즘을 사용하기 때문에 사용하는 데에 크게 무리가 없어 보인다. 성능에 대해서는 밑에서 더 자세히 정리하였다.




sorted() 함수의 활용

sorted() 함수를 사용하기 위해 다양한 활용 사례들을 통해서 배워보는 것이 좋다. 기본 오름차순 정렬부터 내림차순 정렬, key 설정을 통해 특정 값으로 정렬 등 간단하고 다양한 예제를 통해서 살펴보자.


숫자 리스트 정렬 (오름차순)

numbers = [3, 1, 4, 1, 5, 9, 2, 6, 5]
sorted_numbers = sorted(numbers)
print(sorted_numbers)  
# 출력: [1, 1, 2, 3, 4, 5, 5, 6, 9]

위의 코드는 숫자로 구성된 배열 객체다. 이 배열을 기준으로 sorted() 함수를 사용해 오름차순 정렬을 처리하도록 하였다. 결과는 숫자의 순서에 맞게 오름차순 정렬이 되는 것을 확인할 수 있다.


숫자 리스트 정렬 (내림차순)

numbers = [5, 2, 8, 1, 6]
sorted_numbers_desc = sorted(numbers, reverse=True)
print(sorted_numbers_desc)  
# 출력: [8, 6, 5, 2, 1]

위의 코드와 유사한데, 이번에는 내림차순 정렬을 할 수 있도록 코드를 작성하였다. 위의 코드에서 sorted() 함수에 두 번째 인자로 reverse=True 를 설정하였는데, 이 설정이 내림차순을 처리할 수 있는 설정이다. 이렇게 설정 하나로 간단하게 내림차순 정렬을 할 수 있다.


문자열 리스트 정렬 (오름차순)

fruits = ["apple", "banana", "orange", "kiwi", "grape"]
sorted_fruits = sorted(fruits)
print(sorted_fruits)  
# 출력: ['apple', 'banana', 'grape', 'kiwi', 'orange']

위의 코드는 과일 문자를 기준으로 구성된 배열 객체다. 숫자와 마찬가지로 문자열 역시 문자의 순서에 맞게 정렬을 할 수 있다. 과일의 이름 순서대로 오름차순이 되는 것을 확인할 수 있다

딕셔너리 정렬 (내림차순)

scores = {'Alice': 85, 'Bobby': 70, 'Charlie': 90, 'Dave': 80}
sorted_scores = sorted(scores.items(), key=lambda x: x[1], reverse=True)
print(sorted_scores)  
# 출력: [('Charlie', 90), ('Alice', 85), ('Dave', 80), ('Bobby', 70)]

위의 코드는 이름(key)과 점수(value)를 기준으로 구성된 딕셔너리 객체다. 이 딕셔너리 객체를 기준으로 정렬 처리를 할 때, 위와 같이 key값을 점수로 지정하고, 정렬 유형을 내림차순이 될 수 있도록 reverse=True를 설정하였다.
key값을 지정할 때에는 lambda를 사용하였는데,
lambda x: x[1] 를 보게 되면 요소들을 순회하면서 각 요소에 접근하여 x[1]은 점수에 해당하는 값이기 때문에 점수를 기준으로 내림차순 정렬을 하게 된다.


일부 문자 추출하여 정렬 (오름차순)

words = ["hello5", "world3", "python2", "apple4", "banana1"]

sorted_words_by_last_number = sorted(words, key=lambda x: int(x[-1]))
print(sorted_words_by_last_number)  
# 출력: ['banana1', 'python2', 'world3', 'apple4', 'hello5']

위의 코드는 일반 문자열로 구성된 리스트 객체를 기준으로 정렬하는 코드다. 그런데 여기서 조금 다른 것은 문자열에 숫자가 포함되어 있고, 이 숫자값을 기준으로 정렬 처리하는 예제다. 그래서 정렬에 필요한 key값을 key=lambda x: int(x[-1])와 같이 설정하였다. 각 요소의 마지막 문자만 추출하여 그 값을 기준으로 정렬을 할 수 있다.


이렇게 다양한 사례들을 통해서 sorted() 함수를 활용하는 방법을 살펴보았다. 다른 언어에 비해서 파이썬은 생각보다 더 간결하게 정렬 처리가 가능한 것 같다.



sorted() 함수의 성능

sorted() 함수는 파이썬의 표준 라이브러리에 포함된 내장 함수이며, 효율적인 정렬 알고리즘을 사용하고 있다. 아래 자세한 내용을들 통해서 살펴보자.


Timsort 알고리즘
파이썬의 sorted() 함수는 기본적으로 Timsort 알고리즘을 사용한다. Timsort 알고리즘은 병합 정렬(merge sort)과 삽입 정렬(insertion sort)을 결합한 하이브리드 정렬 알고리즘으로, 안정적이면서도 효율적인 알고리즘이라고 할 수 있다.

시간 복잡도
Timsort 알고리즘은 최선의 경우 O(n)의 선형 시간 복잡도를 가지며, 최악의 경우에도 O(nlogn)의 시간 복잡도를 가진다.

O(n)
리스트의 크기가 n일 때, 요소를 모두 모두 확인하는 경우 O(n)의 시간 소요
O(nlogn)
입력 크기가 n일 때, log(n)의 횟수만큼 분할하고 병합하기 때문에 더 빠른 속도 제공

시간 복잡도에 대한 개념이 어렵지만 결과적으로 두 시간 복잡도는 모두 데이터의 크기가 크더라도 빠르게 처리할 수 있다는 의미를 가진다.

메모리
Timsort 알고리즘은 추가적인 메모리를 사용하여 안정적인 정렬을 보장한다. 대부분의 경우, 입력 데이터 크기의 상수 배만큼의 메모리를 추가로 필요한데, 이러한 메모리 사용량은 입력 데이터의 크기와 상관없이 고정되어 있으며, 일반적으로는 큰 문제가 되지 않는다.

최적화
CPython에서는 sorted() 함수가 C 언어로 구현되어 있어서 빠른 속도를 제공한다. 또한, 데이터의 크기가 작은 겅우에는 퀵 정렬과 삽입 정렬과 같은 간단한 정렬 알고리즘을 사용하여 성능을 최적화한다.

속도
Timsort 알고리즘은 대부분의 상황에서 빠른 속도를 제공한다. 특히 이미 정렬된 데이터나 부분 정렬된 데이터에 대해서는 매우 효율적으로 작동한다. 또한, 작은 크기의 데이터에 대해서도 빠른 정렬 알고리즘을 사용하여 성능을 최적화한다.


정리한 바와 같이 sorted() 함수는 생각보다 더 효율적이고 안정적인 함수라고 할 수 있다. 메모리 효율성, 속도, 시간 복잡도 등 여러 방면에서 뛰어난 함수이므로 신뢰하고 사용하면 될 것 같다.



sorted()와 list.sort() 비교

파이썬에서 보통 정렬 처리를 하고자 한다면 함수에 객체를 전달하여 처리하는 방법과 list.sort()와 같이 객체 뒤에서 호출하여 바로 변경이 가능한 처리 방법이 있다. 두 방식은 유사하지만 다른 부분이 있다.

반환
sorted() 함수는 원본 객체를 변경하지 않고 새로운 객체를 생성하여 반환한다.

numbers = [3, 1, 4, 1, 5]
sorted_numbers = sorted(numbers)  # 새로운 리스트 반환

print(sorted_numbers)  
# 출력: [1, 1, 3, 4, 5]
print(numbers)  
# 출력: [3, 1, 4, 1, 5] (원본 리스트 변경되지 않음)

위와 같이 샤로운 객체를 반환하고, 원본 객체는 변경하지 않는다.


list.sort()는 원본 객체를 기준으로 정렬을 하고, 반환 값은 없다.

numbers = [3, 1, 4, 1, 5]
numbers.sort()  
# 원본 리스트를 제자리에서 정렬
print(numbers)  
# 출력: [1, 1, 3, 4, 5] (원본 리스트 변경됨)

위와 같이 새로 객체를 생성하지 않고 원본 객체 자체에서 정렬을 하게 된다.


성능
sorted() 함수는 새로운 객체를 생성하고 반환하기 때문에 원본 데이터는 변경하지 않는다. 그렇기 때문에 메모리 사용량이 상대적으로 더 많을 수 있다.
list.sort() 방식은 원본 리스트를 제자리에서 정렬하므로 메모리 사용량이 상대적으로 더 적다. 그러나 원본 데이터를 변경하기 때문에 이 부분을 유의해서 사용해야 한다.


sorted() 함수와 list.sort() 방식은 사용법과 성능(메모리 사용량)에서는 차이가 있지만, 내부적으로는 동일한 Timsort 정렬 알고리즘을 사용한다. 그렇기 때문에 상황에 따라서 원본 데이터가 변경되지 않아야 하는 경우에는 sorted()를 사용하고, 원본 데이터 자체를 정렬하고자 할 때에는 list.sort()와 같이 사용하면 된다.



마무리

처음 언급한 것과 같이 프로그래밍에서 정렬은 매우 중요하다. 객체가 정렬되지 않은 것은 사용자가 데이터를 다룰 때 가독성이 떨어지고, 비효율적이다. 그래서 정렬을 사용하는 것은 좋다고 생각한다. 추가로 정렬을 공부하고 실제 적용하기 전에 정렬의 성능이나 알고리즘에 대해서 한 번쯤은 확인하고 사용하는 것이 좋을 것 같다.


경제 지표 서비스
https://econoflow.co.kr

 

Economy Flow

 

econoflow.co.kr

 

반응형