이 글은 Python으로 데이터 집합에서 가장 큰 값이나 가장 작은 값을 찾고 싶은 개발자를 위해 준비했습니다. 단순히 하나의 값만 필요한 경우부터 여러 개의 상위·하위 항목이 필요한 경우, 그리고 DataFrame을 다룰 때까지 상황별로 사용할 수 있는 방법들을 예제와 함께 소개하고, 마지막에 어떤 방법이 가장 효율적인지 정리해 드리겠습니다.
방법 1 – 리스트 슬라이싱(Slice) 접근
만약 단순히 가장 작은 값 하나 또는 가장 큰 값 하나(N=1)만 찾고 있다면, min()과 max()를 사용하는 것이 가장 빠릅니다.
먼저 임의의 정수 리스트를 생성해 보겠습니다.
import random # 1~9 범위의 정수로 무작위 리스트 생성 random_list = random.sample(range(1, 10), 9) random_list
실행 결과
[2, 4, 5, 1, 7, 9, 6, 8, 3]
가장 작은 값과 가장 큰 값 찾기 (N=1)
# 가장 작은 숫자 찾기 (N=1) min(random_list)
실행 결과
1
# 가장 큰 숫자 찾기 (N=1) max(random_list)
실행 결과
9
가장 작은 값 3개와 가장 큰 값 3개 찾기 (N=3)
반대로 N이 컬렉션 전체 크기와 비슷한 수준이라면, 먼저 정렬한 뒤 슬라이스로 N개만 잘라내는 것이 일반적으로 더 빠릅니다.
# 슬라이싱으로 가장 작은 값 3개 구하기 (N=3) sorted(random_list)[:3]
실행 결과
[1, 2, 3]
# 슬라이싱으로 가장 큰 값 3개 구하기 (N=3) sorted(random_list)[-3:]
실행 결과
[7, 8, 9]
방법 2 – heapq 모듈 활용
heapq 모듈에는 nlargest()와 nsmallest()라는 두 함수가 있으며, 이를 사용하면 가장 큰 N개 또는 가장 작은 N개의 항목을 손쉽게 구할 수 있습니다.
import heapq import random random_list = random.sample(range(1, 10), 9) # 가장 작은 항목 3개 (N=3) heapq.nsmallest(3, random_list)
실행 결과
[1, 2, 3]
# 가장 큰 항목 3개 (N=3) heapq.nlargest(3, random_list)
실행 결과
[9, 8, 7]
조금 더 복잡한 데이터를 다룰 때는 key 매개변수를 활용할 수 있습니다.
import heapq
grandslams = [
{'name': 'Roger Federer', 'titles': 20},
{'name': 'Rafael Nadal', 'titles': 19},
{'name': 'Novak Djokovic', 'titles': 17},
{'name': 'Andy Murray', 'titles': 3},
]
# 타이틀 수가 적은 선수 3명 (N=3)
less = heapq.nsmallest(3, grandslams, key=lambda s: s['titles'])
less
실행 결과
[{'name': 'Andy Murray', 'titles': 3},
{'name': 'Novak Djokovic', 'titles': 17},
{'name': 'Rafael Nadal', 'titles': 19}]
# 타이틀 수가 많은 선수 3명 (N=3) more = heapq.nlargest(3, grandslams, key=lambda s: s['titles']) more
실행 결과
[{'name': 'Roger Federer', 'titles': 20},
{'name': 'Rafael Nadal', 'titles': 19},
{'name': 'Novak Djokovic', 'titles': 17}]
DataFrame에서 가장 큰 값과 작은 값 N개 찾기
현실의 데이터 세상은 CSV 파일로 가득합니다. 따라서 Python 개발을 하다 보면 언젠가는 CSV와 DataFrame을 다루게 될 가능성이 매우 높습니다. 여기서는 DataFrame에서 가장 큰 값 또는 작은 값 N개를 찾는 두 가지 방법을 소개합니다.
첫 번째 방법은 sort_values() 메서드로 값을 정렬한 뒤, head() 메서드로 원하는 개수만큼 가져오는 것입니다.
import pandas as pd import io # 샘플 데이터 정의 data = ''' player,titles Djokovic,17 Nadal,19 Federer,20 Murray,3 ''' throwaway_storage = io.StringIO(data) df = pd.read_csv(throwaway_storage, index_col='player')
# 가장 작은 값 3개 (N=3)
df.sort_values('titles').head(3)
실행 결과
titles player Murray 3 Djokovic 17 Nadal 19
# 가장 큰 값 3개 (N=3)
df.sort_values('titles', ascending=False).head(3)
실행 결과
titles player Federer 20 Nadal 19 Djokovic 17
행을 정렬한 후 .head()를 호출하는 대신, DataFrame에서 직접 제공하는 .nsmallest()와 .nlargest() 메서드를 호출할 수도 있습니다. 코드가 더 간결하고 의도도 명확하게 드러납니다.
df.nsmallest(3, columns='titles')
실행 결과
titles player Murray 3 Djokovic 17 Nadal 19
df.nlargest(3, columns='titles')
실행 결과
titles player Federer 20 Nadal 19 Djokovic 17
마무리: 어떤 방법을 선택해야 할까?
찾으려는 항목의 개수가 전체 데이터에 비해 상대적으로 적다면 nlargest()와 nsmallest() 함수가 가장 적합합니다.
단순히 가장 작은 값 하나 또는 가장 큰 값 하나(N=1)만 필요하다면 min()과 max()를 사용하는 것이 더 빠릅니다.
마찬가지로 N이 컬렉션 전체 크기와 비슷하다면, 먼저 정렬한 뒤 슬라이스를 잘라내는 것이 일반적으로 더 빠릅니다.
결론적으로 Python의 nlargest()와 nsmallest() 구현은 상황에 맞게 동작 방식을 유연하게 조정하며, 이러한 최적화 중 일부를 자동으로 수행해 줍니다. 따라서 특별한 이유가 없다면 heapq의 함수들을 우선 사용하고, 성능이 중요한 경우에는 위의 가이드라인에 따라 상황에 맞는 방법을 선택하는 것이 좋습니다.