Python 정렬, sorted(): 리스트 정복의 지름길 mymaster, 2024년 06월 24일 “파이썬으로 데이터를 다루다 보면, 정렬은 피할 수 없는 숙명과 같죠?” 맞습니다. 프로그래밍의 세계에서 데이터 관리는 빼놓을 수 없는 과제이며, 그중에서도 정렬은 데이터 분석, 처리, 그리고 시각화 등 다양한 작업의 기반이 됩니다. 특히 방대한 데이터를 다루는 현대 프로그래밍 환경에서는 효율적인 정렬 알고리즘의 이해와 활용이 더욱 중요해졌습니다. 이 글에서는 Python의 강력한 정렬 도구인 sorted() 함수와 sort() 메서드를 파헤쳐 보고, 여러분의 데이터 정복 여정을 한 단계 업그레이드할 수 있는 노하우를 공유하고자 합니다. 복잡한 개념은 차근차근 설명하고, 실제 활용 가능한 예시를 통해 이해를 돕겠습니다. 자, 이제 sorted() 함수와 sort() 메서드를 완벽히 이해하고 Python 정렬 마스터로 거듭날 준비 되셨나요? 함께 떠나봅시다! 1. Python 정렬의 기초: sorted() 함수 sorted() 함수는 Python 내장 함수로, 다양한 자료형의 데이터를 정렬하여 새로운 리스트를 반환합니다. 리스트, 문자열, 튜플 등 반복 가능한(iterable) 객체라면 무엇이든 정렬 가능하다는 것이 큰 장점입니다. 1.1. sorted() 함수의 기본 사용법 sorted() 함수의 기본적인 사용법은 매우 간단합니다. 정렬하고자 하는 리스트를 함수의 인자로 전달하면, 정렬된 새로운 리스트를 반환합니다. 예를 들어, 다음과 같이 숫자로 이루어진 리스트를 sorted() 함수를 사용하여 오름차순으로 정렬해 보겠습니다. numbers = [3, 1, 4, 2, 5] sorted_numbers = sorted(numbers) print(sorted_numbers) # 출력: [1, 2, 3, 4, 5] 위 코드에서 sorted(numbers)는 정렬된 새로운 리스트를 sorted_numbers 변수에 할당합니다. sorted() 함수는 원본 리스트인 numbers에는 영향을 주지 않고 새로운 리스트를 생성하여 반환한다는 점에 유의해야 합니다. 1.2. sorted() 함수와 문자열 정렬 sorted() 함수는 숫자뿐만 아니라 문자열도 정렬할 수 있습니다. 문자열의 경우, 각 문자의 ASCII 코드 값을 기준으로 사전 순서대로 정렬됩니다. 예를 들어, 다음과 같이 문자열 리스트를 sorted() 함수를 사용하여 정렬해 보겠습니다. fruits = ['apple', 'banana', 'orange', 'grape'] sorted_fruits = sorted(fruits) print(sorted_fruits) # 출력: ['apple', 'banana', 'grape', 'orange'] 위 코드에서 sorted(fruits)는 문자열을 사전 순서대로 정렬하여 ['apple', 'banana', 'grape', 'orange']를 반환합니다. 1.3. sorted() 함수 활용: 내림차순 정렬 sorted() 함수는 기본적으로 오름차순으로 정렬하지만, reverse 매개 변수를 사용하여 내림차순 정렬도 가능합니다. numbers = [3, 1, 4, 2, 5] descending_numbers = sorted(numbers, reverse=True) print(descending_numbers) # 출력: [5, 4, 3, 2, 1] 위 코드에서 reverse=True를 설정하여 리스트를 내림차순으로 정렬합니다. 2. 정렬 기준 설정: key 매개 변수 활용 sorted() 함수의 진정한 강력함은 key 매개 변수를 통해 발휘됩니다. key 매개 변수에는 함수를 지정할 수 있으며, sorted() 함수는 이 함수의 반환 값을 기준으로 정렬을 수행합니다. 즉, 사용자 정의 함수를 통해 원하는 기준으로 정렬을 자유자재로 제어할 수 있습니다. 2.1. 함수를 이용한 정렬 기준 설정 예를 들어, 리스트 내 튜플 요소들의 두 번째 값을 기준으로 정렬하고 싶다고 가정해봅시다. 이 경우, 튜플의 두 번째 값을 반환하는 함수를 key 매개 변수에 전달하면 원하는 결과를 얻을 수 있습니다. data = [(1, 5), (3, 2), (2, 4), (4, 1)] def sort_by_second(item): return item[1] sorted_data = sorted(data, key=sort_by_second) print(sorted_data) # 출력: [(4, 1), (3, 2), (2, 4), (1, 5)] 위 코드에서 sort_by_second 함수는 튜플을 입력으로 받아 두 번째 값을 반환하는 함수입니다. sorted(data, key=sort_by_second)는 sort_by_second 함수의 반환 값을 기준으로 정렬하여 [(4, 1), (3, 2), (2, 4), (1, 5)]를 반환합니다. 2.2. lambda 함수를 이용한 간결한 표현 lambda 함수를 사용하면 key 매개 변수에 함수를 더욱 간결하게 전달할 수 있습니다. lambda 함수는 함수를 한 줄로 정의하는 방법으로, 코드의 가독성을 높이는 효과가 있습니다. 위의 예를 lambda 함수를 사용하여 다시 작성해 보겠습니다. data = [(1, 5), (3, 2), (2, 4), (4, 1)] sorted_data = sorted(data, key=lambda item: item[1]) print(sorted_data) # 출력: [(4, 1), (3, 2), (2, 4), (1, 5)] lambda item: item[1]는 익명 함수로, 튜플을 입력으로 받아 두 번째 값을 반환하는 함수와 동일한 역할을 합니다. lambda 함수를 사용하면 별도의 함수를 정의할 필요 없이 코드를 간결하게 유지할 수 있습니다. 2.3. 다양한 정렬 기준 적용: key 함수 활용 예시 key 매개 변수에 사용자 정의 함수를 전달하면 다양한 기준으로 정렬을 수행할 수 있습니다. 예를 들어, 문자열의 길이를 기준으로 정렬하거나, 절대값을 기준으로 숫자를 정렬하는 등의 작업이 가능합니다. 문자열 길이를 기준으로 정렬: words = ['apple', 'banana', 'cherry', 'date'] sorted_words = sorted(words, key=len) print(sorted_words) # 출력: ['date', 'apple', 'banana', 'cherry'] 위 코드에서 len 함수를 key 매개 변수에 전달하여 문자열의 길이를 기준으로 정렬합니다. 절대값을 기준으로 숫자 정렬: numbers = [-3, 1, -4, 2, -5] sorted_numbers = sorted(numbers, key=abs) print(sorted_numbers) # 출력: [1, 2, -3, -4, -5] 위 코드에서 abs 함수를 key 매개 변수에 전달하여 절대값을 기준으로 숫자를 정렬합니다. 3. 리스트 자체를 정렬하는 sort() 메서드 sorted() 함수와 유사하게 리스트를 정렬하는 기능을 제공하는 sort() 메서드는 리스트 객체 자체의 메서드입니다. sorted() 함수와 달리 sort() 메서드는 원본 리스트 자체를 수정하여 정렬하며, 정렬된 새로운 리스트를 반환하지 않습니다. 3.1. sort() 메서드의 기본 사용법 sort() 메서드는 sorted() 함수와 마찬가지로 기본적으로 오름차순으로 정렬하며, reverse 매개 변수를 사용하여 내림차순 정렬을 수행할 수 있습니다. numbers = [3, 1, 4, 2, 5] numbers.sort() # 원본 리스트를 직접 정렬 print(numbers) # 출력: [1, 2, 3, 4, 5] 위 코드에서 numbers.sort()는 원본 리스트인 numbers를 직접 수정하여 정렬합니다. 3.2. sort() 메서드와 key, reverse 매개 변수 활용 sort() 메서드 또한 sorted() 함수와 동일하게 key 매개 변수를 사용하여 정렬 기준을 변경할 수 있습니다. data = [(1, 5), (3, 2), (2, 4), (4, 1)] data.sort(key=lambda item: item[1]) # 튜플의 두 번째 값을 기준으로 정렬 print(data) # 출력: [(4, 1), (3, 2), (2, 4), (1, 5)] 위 코드에서 data.sort(key=lambda item: item[1])는 lambda 함수를 사용하여 튜플의 두 번째 값을 기준으로 리스트를 정렬합니다. 3.3. sort() 메서드 vs. sorted() 함수: 상황에 맞는 선택 sort() 메서드와 sorted() 함수는 모두 강력한 정렬 도구이지만, 몇 가지 중요한 차이점이 있습니다. 원본 리스트 수정 여부: sort() 메서드는 원본 리스트를 직접 수정하는 반면, sorted() 함수는 원본 리스트를 변경하지 않고 정렬된 새로운 리스트를 반환합니다. 반환 값: sort() 메서드는 반환 값이 없고 None을 반환하는 반면, sorted() 함수는 정렬된 새로운 리스트를 반환합니다. 적용 가능성: sort() 메서드는 리스트 객체에서만 사용 가능한 반면, sorted() 함수는 리스트, 문자열, 튜플 등 반복 가능한 모든 객체에 사용 가능합니다. 따라서, 원본 리스트를 유지하면서 정렬된 새로운 리스트가 필요한 경우 sorted() 함수를 사용하고, 원본 리스트를 직접 수정하고자 하는 경우 sort() 메서드를 사용하는 것이 효율적입니다. 4. 정렬 알고리즘의 세계: Timsort 알고리즘 Python의 sorted() 함수와 sort() 메서드는 Timsort라는 정렬 알고리즘을 사용합니다. Timsort 알고리즘은 2002년 Tim Peters가 개발한 알고리즘으로, Python 외에도 Java, Android platform 등 다양한 프로그래밍 언어 및 플랫폼에서 널리 사용되고 있습니다. 4.1. Timsort 알고리즘의 특징 Timsort 알고리즘은 실제 데이터에서 자주 나타나는 부분적으로 정렬된 부분 배열을 활용하여 정렬을 수행하는 것이 특징입니다. 이러한 특징을 통해 Timsort 알고리즘은 다음과 같은 장점을 제공합니다. 실제 데이터에 최적화: Timsort 알고리즘은 이미 정렬된 부분 배열을 활용하기 때문에, 완전히 무작위적인 데이터보다 실제 데이터에서 더욱 빠른 속도를 보여줍니다. 안정적인 정렬: Timsort 알고리즘은 안정적인 정렬 알고리즘으로, 동일한 값을 가진 요소들의 상대적인 순서가 정렬 후에도 유지됩니다. 다양한 상황에서 우수한 성능: Timsort 알고리즘은 최악의 경우에도 O(n log n)의 시간 복잡도를 가지며, 평균적으로 O(n log n)보다 빠른 속도를 보여줍니다. 이는 Timsort 알고리즘이 다양한 상황에서 효율적으로 동작할 수 있음을 의미합니다. 4.2. Timsort 알고리즘의 작동 방식: 간단히 알아보기 Timsort 알고리즘은 삽입 정렬과 병합 정렬의 장점을 결합한 알고리즘입니다. Run 생성: Timsort 알고리즘은 입력 리스트를 스캔하여 이미 정렬된 부분 배열인 “run”을 찾습니다. run의 최소 크기는 특정 조건에 따라 결정되며, 일반적으로 32 또는 64입니다. Run 병합: 생성된 run들은 병합 정렬을 사용하여 하나의 정렬된 배열로 병합됩니다. 병합 과정에서 Timsort 알고리즘은 인접한 run들을 효율적으로 병합하기 위해 다양한 최적화 기법을 사용합니다. Timsort 알고리즘은 복잡한 알고리즘이지만, 기본적인 작동 방식을 이해하면 Python의 sorted() 함수와 sort() 메서드가 다양한 상황에서 효율적으로 동작하는 이유를 파악할 수 있습니다. 5. 정렬, 더 깊이 알아보기 지금까지 Python의 sorted() 함수와 sort() 메서드를 사용하여 데이터를 정렬하는 방법을 알아보았습니다. 이제, 정렬과 관련된 몇 가지 추가적인 주제들을 살펴보겠습니다. 5.1. 안정적인 정렬과 불안정한 정렬 정렬 알고리즘은 크게 안정적인 정렬(stable sort)과 불안정한 정렬(unstable sort)으로 나눌 수 있습니다. 안정적인 정렬: 동일한 값을 가진 요소들의 상대적인 순서가 정렬 후에도 유지되는 정렬 알고리즘을 의미합니다. 즉, 원본 데이터에서 특정 기준으로 동일한 값을 가진 요소들이 특정 순서로 나타났다면, 정렬 후에도 해당 순서가 유지됩니다. 불안정한 정렬: 동일한 값을 가진 요소들의 상대적인 순서가 정렬 후에 변경될 수 있는 정렬 알고리즘을 의미합니다. Python의 sorted() 함수와 sort() 메서드는 Timsort 알고리즘을 사용하기 때문에 안정적인 정렬을 보장합니다. 5.2. 다양한 정렬 알고리즘 Timsort 알고리즘 외에도 다양한 정렬 알고리즘이 존재하며, 각 알고리즘은 고유한 장단점을 가지고 있습니다. 몇 가지 대표적인 정렬 알고리즘을 살펴보겠습니다. 버블 정렬 (Bubble Sort): 인접한 요소들을 비교하여 정렬하는 단순한 알고리즘입니다. 구현은 간단하지만, 효율성이 낮아 실제로는 거의 사용되지 않습니다. 삽입 정렬 (Insertion Sort): 정렬되지 않은 부분에서 요소를 하나씩 꺼내어 이미 정렬된 부분의 적절한 위치에 삽입하는 알고리즘입니다. 부분적으로 정렬된 데이터에 효율적입니다. 선택 정렬 (Selection Sort): 데이터를 순회하며 최솟값 또는 최댓값을 찾아 해당 위치의 요소와 교환하는 방식으로 정렬하는 알고리즘입니다. 데이터 이동 횟수가 적다는 장점이 있지만, 비교 횟수가 많아 효율성이 떨어집니다. 병합 정렬 (Merge Sort): 데이터를 작은 부분 집합으로 나누어 재귀적으로 정렬한 후 병합하는 알고리즘입니다. 안정적인 정렬 알고리즘이며, 평균적으로 O(n log n)의 시간 복잡도를 가집니다. 퀵 정렬 (Quick Sort): 피벗 요소를 기준으로 데이터를 분할하여 정렬하는 알고리즘입니다. 평균적으로 O(n log n)의 시간 복잡도를 가지지만, 최악의 경우 O(n^2)의 시간 복잡도를 가질 수 있습니다. 힙 정렬 (Heap Sort): 이진 힙(binary heap) 자료구조를 사용하여 정렬하는 알고리즘입니다. O(n log n)의 시간 복잡도를 보장하며, 추가적인 메모리 공간을 필요로 하지 않는다는 장점이 있습니다. 5.3. Python 정렬 라이브러리 Python은 sorted() 함수와 sort() 메서드 외에도 다양한 정렬 기능을 제공하는 라이브러리를 제공합니다. NumPy: 수치 데이터를 효율적으로 처리하기 위한 라이브러리로, numpy.sort() 함수를 통해 배열을 정렬할 수 있습니다. NumPy는 C로 구현되어 있어 빠른 속도를 자 목차 Toggle 1. Python 정렬의 기초: sorted() 함수1.1. sorted() 함수의 기본 사용법1.2. sorted() 함수와 문자열 정렬1.3. sorted() 함수 활용: 내림차순 정렬2. 정렬 기준 설정: key 매개 변수 활용2.1. 함수를 이용한 정렬 기준 설정2.2. lambda 함수를 이용한 간결한 표현2.3. 다양한 정렬 기준 적용: key 함수 활용 예시3. 리스트 자체를 정렬하는 sort() 메서드3.1. sort() 메서드의 기본 사용법3.2. sort() 메서드와 key, reverse 매개 변수 활용3.3. sort() 메서드 vs. sorted() 함수: 상황에 맞는 선택4. 정렬 알고리즘의 세계: Timsort 알고리즘4.1. Timsort 알고리즘의 특징4.2. Timsort 알고리즘의 작동 방식: 간단히 알아보기5. 정렬, 더 깊이 알아보기5.1. 안정적인 정렬과 불안정한 정렬5.2. 다양한 정렬 알고리즘5.3. Python 정렬 라이브러리 post