소개
Pandas는 인덱스 위치(Index position) 또는 인덱스 레이블(Index label) 두 가지 방식으로 데이터의 부분 집합을 선택할 수 있는 강력한 기능을 제공합니다. 이 글에서는 그중에서도 사전식 슬라이싱(lexicographical slicing)을 활용해 데이터의 일부를 선택하는 방법을 소개합니다.
데이터셋을 찾고 있다면 Kaggle(kaggle.com)에서 'movies dataset'을 검색해 보세요. 이 글에서는 Kaggle의 영화 데이터셋을 예제로 사용합니다.
실습 방법
먼저 예제에 필요한 컬럼만 골라서 영화 데이터셋을 불러옵니다.
import pandas as pd
import numpy as np
movies = pd.read_csv(
"https://raw.githubusercontent.com/sasankac/TestDataSet/master/movies_data.csv",
index_col="title",
usecols=["title", "budget", "vote_average", "vote_count"]
)
movies.sample(n=5)샘플 데이터는 아래와 같습니다.
| title | budget | vote_average | vote_count |
|---|---|---|---|
| Little Voice | 0 | 6.6 | 61 |
| Grown Ups 2 | 80000000 | 5.8 | 1155 |
| The Best Years of Our Lives | 2100000 | 7.6 | 143 |
| Tusk | 2800000 | 5.1 | 366 |
| Operation Chromite | 0 | 5.8 | 29 |
인덱스 정렬은 왜 중요할까?
인덱스가 문자열로 구성된 경우에는 반드시 정렬(sort)을 해두는 것을 권장합니다. 대용량 데이터셋을 다룰 때 인덱스가 정렬되어 있으면 성능 차이를 체감할 수 있습니다.
인덱스를 정렬하지 않으면 어떻게 될까?
코드가 영원히 실행되는 건 아니지만(물론 농담입니다), 인덱스 레이블이 정렬되어 있지 않으면 Pandas는 쿼리와 일치하는 값을 찾기 위해 모든 레이블을 하나씩 순회해야 합니다. 색인 페이지 없는 옥스포드 사전을 상상해 보세요. 원하는 단어를 찾으려면 처음부터 한 장씩 넘겨야 할 겁니다. 반면 정렬된 인덱스가 있으면 원하는 위치로 바로 점프할 수 있듯이, Pandas도 마찬가지로 빠르게 탐색할 수 있습니다.
먼저 현재 인덱스가 정렬되어 있는지 확인해 보겠습니다.
# 인덱스가 정렬되어 있는지 확인 movies.index.is_monotonic
False
결과가 False이므로 인덱스가 정렬되어 있지 않다는 것을 알 수 있습니다.
정렬되지 않은 상태에서 슬라이싱 시도
이제 'A'로 시작하는 영화 제목만 선택해 보려 합니다. SQL로 표현하면 다음과 같습니다.
select * from movies where title like 'A%'
Pandas에서는 다음과 같이 작성할 수 있습니다.
movies.loc["Aa":"Bb"]
그런데 실행하면 아래와 같은 에러가 발생합니다.
ValueError: index must be monotonic increasing or decreasing KeyError: 'Aa'
즉, 인덱스가 단조 증가(monotonic increasing)하거나 단조 감소(monotonic decreasing) 상태여야만 레이블 기반 슬라이싱이 가능하다는 의미입니다.
인덱스 정렬 후 사전식 슬라이싱
인덱스를 오름차순으로 정렬한 뒤 동일한 명령을 다시 실행하면, 정렬의 이점을 살린 사전식 슬라이싱을 사용할 수 있습니다.
movies = movies.sort_index() movies.index.is_monotonic
True
이제 데이터가 준비되었습니다. 'A'로 시작하는 영화 제목부터 'B'로 시작하는 제목까지 모두 선택해 보겠습니다.
movies.loc["A":"C"]
| title | budget | vote_average | vote_count |
|---|---|---|---|
| Abandon | 25000000 | 4.6 | 45 |
| Abandoned | 0 | 5.8 | 27 |
| Abduction | 35000000 | 5.6 | 961 |
| Aberdeen | 0 | 7.0 | 6 |
| About Last Night | 12500000 | 6.0 | 210 |
| ... | ... | ... | ... |
| Battle for the Planet of the Apes | 1700000 | 5.5 | 215 |
| Battle of the Year | 20000000 | 5.9 | 88 |
| Battle: Los Angeles | 70000000 | 5.5 | 1448 |
| Battlefield Earth | 44000000 | 3.0 | 255 |
| Battleship | 209000000 | 5.5 | 2114 |
292 rows × 3 columns
내림차순 정렬된 경우 주의점
아래 표처럼 특수문자나 소문자가 포함된 제목들은 유니코드 순서상 앞쪽에 배치됩니다.
| title | budget | vote_average | vote_count |
|---|---|---|---|
| Æon Flux | 62000000 | 5.4 | 703 |
| xXx: State of the Union | 60000000 | 4.7 | 549 |
| xXx | 70000000 | 5.8 | 1424 |
| eXistenZ | 15000000 | 6.7 | 475 |
| [REC]² | 5600000 | 6.4 | 489 |
만약 데이터가 내림차순으로 정렬되어 있는데 오름차순 범위(예: "Aa" → "Bb")로 슬라이싱하면 결과가 비어 있는(empty) DataFrame이 반환됩니다. 이 경우에는 범위의 시작과 끝을 반대로 지정해서 실행하면 됩니다.
movies.loc["Bb":"Aa"]
| title | budget | vote_average | vote_count |
|---|---|---|---|
| B-Girl | 0 | 5.5 | 7 |
| Ayurveda: Art of Being | 300000 | 5.5 | 3 |
| Away We Go | 17000000 | 6.7 | 189 |
| Awake | 86000000 | 6.3 | 395 |
| Avengers: Age of Ultron | 280000000 | 7.3 | 6767 |
| ... | ... | ... | ... |
| About Last Night | 12500000 | 6.0 | 210 |
| Aberdeen | 0 | 7.0 | 6 |
| Abduction | 35000000 | 5.6 | 961 |
| Abandoned | 0 | 5.8 | 27 |
| Abandon | 25000000 | 4.6 | 45 |
228 rows × 3 columns
정리
- Pandas의
.loc[]슬라이싱으로 문자열 인덱스를 사전식(lexicographical) 범위 조회할 수 있습니다. - 슬라이싱 전에 반드시
index.is_monotonic으로 정렬 여부를 확인하세요. - 정렬되지 않은 인덱스로 슬라이싱하면
ValueError: index must be monotonic increasing or decreasing에러가 발생합니다. sort_index()로 인덱스를 정렬하면 대용량 데이터에서도 빠른 조회가 가능합니다.- 내림차순 정렬된 데이터는 범위의 시작과 끝을 반대로 지정해야 올바른 결과를 얻을 수 있습니다.