이미지 로딩 중...
AI Generated
2025. 11. 14. · 2 Views
Polars 데이터 선택 및 필터링 완벽 가이드
Polars를 사용하여 데이터를 효율적으로 선택하고 필터링하는 방법을 배웁니다. select, filter, with_columns 등 실무에서 가장 많이 사용하는 필수 기능들을 초급자도 쉽게 이해할 수 있도록 실전 예제와 함께 소개합니다.
목차
- select - 원하는 컬럼만 골라내기
- filter - 조건에 맞는 행만 추출하기
- with_columns - 새로운 컬럼 추가하기
- sort - 데이터 정렬하기
- group_by와 agg - 그룹별 집계하기
- head와 tail - 일부 행만 보기
- unique와 n_unique - 고유값 다루기
- join - 데이터 결합하기
- is_null과 fill_null - 결측치 다루기
- cast - 데이터 타입 변환하기
1. select - 원하는 컬럼만 골라내기
시작하며
여러분이 수백 개의 컬럼이 있는 거대한 데이터셋을 다룰 때, 실제로 필요한 것은 그 중 몇 개뿐인 상황을 겪어본 적 있나요? 예를 들어, 고객 데이터에서 이름과 구매 금액만 필요한데 100개가 넘는 컬럼이 모두 로딩되어 메모리를 잡아먹는 경우입니다.
이런 문제는 실제 데이터 분석 현장에서 매일같이 발생합니다. 불필요한 컬럼들이 메모리를 차지하고 처리 속도를 느리게 만들 뿐만 아니라, 코드의 가독성까지 떨어뜨립니다.
바로 이럴 때 필요한 것이 select입니다. Polars의 select는 필요한 컬럼만 깔끔하게 선택하여 성능과 가독성을 동시에 향상시켜줍니다.
개요
간단히 말해서, select는 DataFrame에서 원하는 컬럼만 선택하는 기능입니다. Pandas에서도 컬럼 선택이 가능하지만, Polars의 select는 lazy evaluation과 결합되어 훨씬 효율적입니다.
대용량 데이터를 다룰 때, 쿼리 최적화를 자동으로 수행하여 실제 필요한 컬럼만 읽어옵니다. 예를 들어, CSV 파일에서 특정 컬럼만 선택하면 해당 컬럼만 파싱하여 메모리 사용량을 크게 줄일 수 있습니다.
기존 Pandas에서는 df[['col1', 'col2']] 형식으로 선택했다면, Polars에서는 df.select(pl.col('col1'), pl.col('col2'))처럼 표현식 기반으로 작성합니다. select의 핵심 특징은 첫째, 표현식을 사용하여 컬럼 선택과 동시에 변환이 가능하고, 둘째, 와일드카드나 정규식으로 다중 컬럼을 한번에 선택할 수 있으며, 셋째, lazy evaluation으로 성능이 최적화됩니다.
이러한 특징들이 대용량 데이터 처리에서 엄청난 성능 차이를 만들어냅니다.
코드 예제
import polars as pl
# 샘플 데이터 생성
df = pl.DataFrame({
'name': ['Alice', 'Bob', 'Charlie'],
'age': [25, 30, 35],
'salary': [50000, 60000, 70000],
'department': ['IT', 'HR', 'IT']
})
# 특정 컬럼만 선택
result = df.select(['name', 'salary'])
# 표현식을 사용한 선택 및 변환
result2 = df.select([
pl.col('name'),
(pl.col('salary') * 1.1).alias('increased_salary')
])
설명
이것이 하는 일: select는 DataFrame에서 지정한 컬럼들만 추출하여 새로운 DataFrame을 반환합니다. 원본 데이터는 변경되지 않으며, 항상 새로운 객체가 생성됩니다.
첫 번째로, 가장 기본적인 사용법은 리스트로 컬럼명을 전달하는 것입니다. df.select(['name', 'salary'])는 name과 salary 컬럼만 포함하는 새로운 DataFrame을 만듭니다.
이는 Pandas의 df[['name', 'salary']]와 유사하지만, Polars는 내부적으로 쿼리 최적화를 수행합니다. 그 다음으로, pl.col() 표현식을 사용하면 더 강력한 기능을 활용할 수 있습니다.
pl.col('salary') * 1.1처럼 선택과 동시에 연산을 적용할 수 있고, .alias()로 새로운 컬럼명을 지정할 수 있습니다. 이는 여러 단계를 하나로 합쳐 코드를 간결하게 만들어줍니다.
마지막으로, pl.col('^._id$')처럼 정규식을 사용하거나 pl.col('').exclude('sensitive_data')처럼 와일드카드로 여러 컬럼을 선택할 수 있습니다. 이는 수십 개의 컬럼을 다룰 때 특히 유용합니다.
여러분이 이 코드를 사용하면 메모리 사용량 감소, 처리 속도 향상, 코드 가독성 개선이라는 세 가지 이점을 얻을 수 있습니다. 특히 CSV나 Parquet 파일을 읽을 때 필요한 컬럼만 선택하면 I/O 시간이 크게 단축됩니다.
실전 팁
💡 pl.col('*')로 모든 컬럼을 선택한 후 .exclude()로 제외할 컬럼을 지정하면 대부분을 선택할 때 편리합니다
💡 lazy evaluation을 활용하려면 df.lazy().select(...).collect()처럼 사용하세요. 쿼리 최적화가 자동으로 수행됩니다
💡 컬럼명에 공백이나 특수문자가 있으면 pl.col('column name')처럼 따옴표로 감싸야 합니다
💡 select 내에서 여러 표현식을 조합할 때는 리스트로 묶어서 전달하면 한 번에 처리됩니다
💡 성능이 중요한 경우 pl.col()보다 문자열 리스트를 사용하는 것이 약간 더 빠릅니다 (단, 변환 없이 선택만 할 때)
2. filter - 조건에 맞는 행만 추출하기
시작하며
여러분이 수백만 건의 거래 데이터에서 특정 기간, 특정 금액 이상의 거래만 분석해야 하는 상황을 생각해보세요. 전체 데이터를 로딩하면 메모리가 부족하고, 불필요한 데이터 때문에 분석 시간만 늘어납니다.
이런 문제는 데이터 분석의 첫 단계에서 가장 자주 마주치는 과제입니다. 원하는 데이터만 효율적으로 필터링하지 못하면 이후 모든 작업이 느려지고, 심지어 메모리 부족으로 작업이 중단될 수도 있습니다.
바로 이럴 때 필요한 것이 filter입니다. Polars의 filter는 SQL의 WHERE절처럼 조건을 지정하여 원하는 행만 빠르게 추출해줍니다.
개요
간단히 말해서, filter는 지정한 조건을 만족하는 행만 선택하여 새로운 DataFrame을 반환하는 기능입니다. Pandas의 boolean indexing(df[df['age'] > 30])도 같은 역할을 하지만, Polars의 filter는 훨씬 직관적이고 성능이 뛰어납니다.
특히 lazy mode에서는 여러 filter 조건을 자동으로 최적화하여 한 번의 스캔으로 처리합니다. 예를 들어, 로그 데이터에서 에러만 추출하거나, 판매 데이터에서 고액 거래만 분석할 때 매우 유용합니다.
기존 Pandas에서는 df[df['salary'] > 50000]처럼 작성했다면, Polars에서는 df.filter(pl.col('salary') > 50000)으로 더 명확하게 표현합니다. filter의 핵심 특징은 첫째, 여러 조건을 &(AND)나 |(OR)로 조합할 수 있고, 둘째, 표현식 기반이라 복잡한 조건도 간결하게 작성 가능하며, 셋째, lazy evaluation으로 조건들이 자동 최적화됩니다.
이러한 특징들이 복잡한 필터링 로직을 간단하고 빠르게 만들어줍니다.
코드 예제
import polars as pl
df = pl.DataFrame({
'name': ['Alice', 'Bob', 'Charlie', 'David'],
'age': [25, 30, 35, 28],
'salary': [50000, 60000, 70000, 55000],
'department': ['IT', 'HR', 'IT', 'Finance']
})
# 단일 조건 필터링
high_earners = df.filter(pl.col('salary') > 55000)
# 여러 조건 조합 (AND)
it_high_earners = df.filter(
(pl.col('department') == 'IT') & (pl.col('salary') > 55000)
)
# 여러 조건 조합 (OR)
result = df.filter(
(pl.col('age') < 30) | (pl.col('department') == 'IT')
)
설명
이것이 하는 일: filter는 Boolean 조건식을 받아 True인 행만 포함하는 새로운 DataFrame을 생성합니다. 원본 데이터는 변경되지 않습니다.
첫 번째로, 가장 기본적인 사용법은 pl.col('컬럼명')과 비교 연산자를 사용하는 것입니다. df.filter(pl.col('salary') > 55000)은 salary가 55000보다 큰 행만 선택합니다.
이때 내부적으로 Boolean 배열이 생성되고, True인 인덱스의 행만 추출됩니다. 그 다음으로, &(AND)와 |(OR) 연산자로 복잡한 조건을 만들 수 있습니다.
(pl.col('department') == 'IT') & (pl.col('salary') > 55000)처럼 괄호로 각 조건을 감싸야 합니다. Python의 and/or가 아닌 &/|를 사용하는 이유는 비트 연산자를 오버로딩하여 표현식을 조합하기 때문입니다.
추가로, pl.col('name').str.contains('ice')처럼 문자열 메서드를 사용하거나, pl.col('date').is_between(start, end)처럼 범위 조건도 사용할 수 있습니다. ~(NOT) 연산자로 조건을 반전시킬 수도 있어, ~(pl.col('status') == 'inactive')처럼 활성 사용자만 선택하는 것도 가능합니다.
마지막으로, lazy mode에서 여러 filter를 연속으로 사용하면 Polars가 자동으로 하나의 조건으로 합쳐 최적화합니다. df.lazy().filter(...).filter(...).collect()처럼 작성해도 실제로는 한 번만 스캔합니다.
여러분이 이 코드를 사용하면 불필요한 데이터 로딩 방지, 메모리 사용량 감소, 분석 속도 향상이라는 효과를 얻을 수 있습니다. 특히 Parquet 파일에서 predicate pushdown이 지원되어 파일에서 읽는 시점부터 필터링이 적용됩니다.
실전 팁
💡 조건을 조합할 때 반드시 각 조건을 괄호로 감싸세요. &와 | 연산자의 우선순위 때문에 예상과 다른 결과가 나올 수 있습니다
💡 문자열 조건은 pl.col('name').str.contains()를 사용하고, 대소문자 무시는 .str.to_lowercase()를 먼저 적용하세요
💡 is_null(), is_not_null()로 결측치를 필터링할 수 있습니다. df.filter(pl.col('email').is_not_null())처럼 사용하세요
💡 성능을 위해 가장 제한적인 조건(가장 많은 행을 제거하는 조건)을 먼저 배치하면 좋지만, Polars는 자동 최적화하므로 크게 신경 쓰지 않아도 됩니다
💡 is_in() 메서드로 여러 값 중 하나인지 체크할 수 있습니다. pl.col('status').is_in(['active', 'pending'])처럼 사용하세요
3. with_columns - 새로운 컬럼 추가하기
시작하며
여러분이 데이터 분석을 하다 보면 기존 컬럼들을 조합하거나 변환하여 새로운 파생 변수를 만들어야 하는 경우가 정말 많습니다. 예를 들어, 생년월일로부터 나이를 계산하거나, 매출과 비용으로 이익률을 계산하는 상황입니다.
이런 작업을 할 때 원본 데이터는 유지하면서 새로운 컬럼만 추가하고 싶은데, Pandas에서는 df['new_col'] = ... 처럼 할당하면 원본이 변경되어 위험할 수 있습니다.
바로 이럴 때 필요한 것이 with_columns입니다. 원본을 변경하지 않고 새로운 컬럼을 추가한 DataFrame을 반환하여 안전하고 명확한 데이터 처리가 가능합니다.
개요
간단히 말해서, with_columns는 기존 DataFrame에 하나 이상의 새로운 컬럼을 추가하거나 기존 컬럼을 덮어쓰는 기능입니다. Pandas에서는 df['new'] = df['old'] * 2처럼 직접 할당했지만, 이는 원본을 변경하고 체이닝이 어렵습니다.
Polars의 with_columns는 immutable 방식으로 동작하여 항상 새로운 DataFrame을 반환합니다. 예를 들어, 전처리 파이프라인에서 여러 단계를 연결할 때 각 단계가 독립적으로 동작하여 디버깅이 쉽습니다.
기존에는 여러 줄로 나누어 컬럼을 추가했다면, with_columns는 한 번의 호출로 여러 컬럼을 동시에 추가할 수 있습니다. with_columns의 핵심 특징은 첫째, 여러 컬럼을 한 번에 추가하여 성능이 좋고, 둘째, 표현식 기반이라 복잡한 변환도 간결하게 작성 가능하며, 셋째, 메서드 체이닝이 자연스럽게 연결됩니다.
이러한 특징들이 데이터 전처리 파이프라인을 깔끔하고 효율적으로 만들어줍니다.
코드 예제
import polars as pl
df = pl.DataFrame({
'product': ['A', 'B', 'C'],
'price': [100, 200, 150],
'quantity': [5, 3, 8]
})
# 단일 컬럼 추가
result = df.with_columns(
(pl.col('price') * pl.col('quantity')).alias('total')
)
# 여러 컬럼 동시 추가
result2 = df.with_columns([
(pl.col('price') * pl.col('quantity')).alias('total'),
(pl.col('price') * 1.1).alias('price_with_tax'),
pl.lit('KRW').alias('currency') # 고정값 컬럼
])
설명
이것이 하는 일: with_columns는 지정한 표현식들을 평가하여 새로운 컬럼으로 추가한 DataFrame을 생성합니다. 기존 컬럼명과 같은 이름을 사용하면 해당 컬럼이 덮어써집니다.
첫 번째로, 가장 기본적인 사용법은 표현식과 .alias()로 새 컬럼명을 지정하는 것입니다. (pl.col('price') * pl.col('quantity')).alias('total')은 price와 quantity를 곱한 값을 total이라는 새 컬럼에 저장합니다.
이때 기존 컬럼들은 모두 유지됩니다. 그 다음으로, 여러 컬럼을 추가할 때는 리스트로 표현식들을 묶어 전달합니다.
각 표현식은 독립적으로 평가되며, 한 번의 DataFrame 스캔으로 모든 컬럼이 계산되어 성능이 좋습니다. 예를 들어, 세 개의 파생 변수를 만들 때 세 번 반복하는 것보다 한 번에 처리하는 것이 훨씬 빠릅니다.
추가로, pl.lit()를 사용하면 모든 행에 같은 값을 갖는 컬럼을 만들 수 있습니다. pl.lit('KRW').alias('currency')는 모든 행의 currency 값이 'KRW'인 컬럼을 추가합니다.
이는 여러 DataFrame을 합칠 때 출처를 표시하는 용도로 자주 사용됩니다. 마지막으로, 조건부 컬럼 생성은 pl.when().then().otherwise() 구문을 사용합니다.
pl.when(pl.col('quantity') > 5).then('high').otherwise('low')처럼 IF-THEN-ELSE 로직을 표현할 수 있습니다. 여러분이 이 코드를 사용하면 불변성으로 인한 안전성 향상, 메서드 체이닝으로 가독성 개선, 여러 컬럼 동시 처리로 성능 향상이라는 이점을 얻을 수 있습니다.
특히 데이터 전처리 파이프라인에서 각 단계를 명확하게 표현할 수 있습니다.
실전 팁
💡 같은 이름의 컬럼을 지정하면 덮어쓰기가 됩니다. df.with_columns(pl.col('price').round().alias('price'))처럼 변환 후 같은 이름으로 저장 가능합니다
💡 표현식 내에서 방금 추가한 컬럼을 참조할 수 없습니다. 순차적으로 의존하는 경우 with_columns를 여러 번 체이닝하세요
💡 pl.lit()로 고정값 컬럼을 추가할 때는 명시적으로 타입을 지정할 수 있습니다. pl.lit(0, dtype=pl.Int32)처럼 사용하세요
💡 복잡한 조건은 pl.when().then().when().then().otherwise()처럼 여러 when을 체이닝하여 CASE WHEN처럼 사용할 수 있습니다
💡 성능을 위해 비슷한 연산들은 하나의 with_columns에 모아서 처리하세요. Polars가 병렬 처리할 수 있습니다
4. sort - 데이터 정렬하기
시작하며
여러분이 분석 결과를 보고서로 정리할 때, 매출이 높은 순서대로 정렬하거나 날짜 순으로 정렬해야 하는 경우가 정말 많습니다. 정렬되지 않은 데이터는 패턴을 파악하기 어렵고, 상위/하위 항목을 찾기도 힘듭니다.
이런 정렬 작업은 데이터 분석의 필수 과정이지만, 대용량 데이터에서는 성능 문제가 발생할 수 있습니다. 특히 여러 컬럼을 기준으로 복합 정렬을 할 때 효율적인 알고리즘이 중요합니다.
바로 이럴 때 필요한 것이 sort입니다. Polars의 sort는 빠른 정렬 알고리즘과 lazy evaluation을 활용하여 대용량 데이터도 효율적으로 정렬합니다.
개요
간단히 말해서, sort는 하나 이상의 컬럼을 기준으로 DataFrame의 행을 오름차순 또는 내림차순으로 정렬하는 기능입니다. Pandas의 sort_values()와 유사하지만, Polars의 sort는 멀티스레드를 활용하여 훨씬 빠릅니다.
특히 lazy mode에서는 정렬이 실제로 필요한 시점까지 지연되어 불필요한 정렬을 방지합니다. 예를 들어, 정렬 후 상위 10개만 가져오는 경우 전체를 정렬하지 않고 partial sort를 수행하여 성능을 크게 향상시킵니다.
기존 Pandas에서는 df.sort_values('salary', ascending=False)처럼 작성했다면, Polars에서는 df.sort('salary', descending=True)로 더 직관적으로 표현합니다. sort의 핵심 특징은 첫째, 여러 컬럼을 기준으로 복합 정렬이 가능하고, 둘째, null 값의 위치를 지정할 수 있으며, 셋째, 멀티스레드 정렬로 성능이 뛰어납니다.
이러한 특징들이 복잡한 정렬 요구사항도 간단하고 빠르게 처리할 수 있게 해줍니다.
코드 예제
import polars as pl
df = pl.DataFrame({
'name': ['Alice', 'Bob', 'Charlie', 'David'],
'department': ['IT', 'HR', 'IT', 'HR'],
'salary': [50000, 60000, 70000, 55000],
'hire_date': ['2020-01-15', '2019-03-20', '2021-07-10', '2020-11-05']
})
# 단일 컬럼 정렬 (내림차순)
sorted_df = df.sort('salary', descending=True)
# 여러 컬럼 기준 정렬
# department로 먼저 정렬하고, 같은 department 내에서 salary로 정렬
multi_sorted = df.sort(['department', 'salary'], descending=[False, True])
# null 값 위치 지정
df_with_nulls = df.with_columns(pl.lit(None).alias('bonus'))
sorted_nulls = df_with_nulls.sort('bonus', nulls_last=True)
설명
이것이 하는 일: sort는 지정한 컬럼의 값을 기준으로 행의 순서를 재배열한 새로운 DataFrame을 반환합니다. 기본적으로 오름차순이며, descending 파라미터로 조절합니다.
첫 번째로, 단일 컬럼 정렬은 df.sort('salary', descending=True)처럼 간단합니다. descending=True를 지정하면 큰 값부터 작은 값 순서로 정렬됩니다.
내부적으로 Polars는 빠른 정렬 알고리즘(보통 introsort)을 사용하며, 멀티코어를 활용하여 병렬로 처리합니다. 그 다음으로, 여러 컬럼 기준 정렬은 리스트로 컬럼명을 전달합니다.
df.sort(['department', 'salary'], descending=[False, True])는 먼저 department를 오름차순으로 정렬하고, 같은 department 내에서는 salary를 내림차순으로 정렬합니다. 각 컬럼마다 다른 정렬 방향을 지정할 수 있어 복잡한 정렬도 한 번에 처리됩니다.
추가로, null 값의 위치는 nulls_last 파라미터로 제어합니다. nulls_last=True면 null 값이 맨 뒤에 배치되고, False면 맨 앞에 배치됩니다.
이는 결측치를 분리하여 분석할 때 유용합니다. 마지막으로, lazy mode에서 sort 후 head()나 tail()을 사용하면 Polars가 자동으로 최적화합니다.
df.lazy().sort('salary').head(10).collect()는 전체를 정렬하지 않고 상위 10개만 효율적으로 찾아냅니다. 여러분이 이 코드를 사용하면 빠른 정렬 속도, 직관적인 복합 정렬, 자동 최적화라는 이점을 얻을 수 있습니다.
특히 수백만 행의 데이터를 정렬할 때 Pandas 대비 3-5배 빠른 성능을 경험할 수 있습니다.
실전 팁
💡 문자열 컬럼 정렬 시 대소문자를 무시하려면 먼저 .str.to_lowercase()로 변환한 임시 컬럼을 만들어 정렬하세요
💡 정렬 후 인덱스를 재설정하고 싶다면 with_row_count('index')를 사용하여 새로운 순번 컬럼을 추가할 수 있습니다
💡 성능을 위해 정렬이 꼭 필요한지 먼저 확인하세요. 단순히 최댓값/최솟값을 찾는다면 max()/min()이 훨씬 빠릅니다
💡 날짜/시간 컬럼 정렬 시 문자열이 아닌 실제 Date/Datetime 타입으로 변환하면 올바르게 정렬됩니다
💡 메모리가 부족한 경우 lazy().sort().collect(streaming=True)를 사용하여 스트리밍 방식으로 처리할 수 있습니다
5. group_by와 agg - 그룹별 집계하기
시작하며
여러분이 부서별 평균 급여를 계산하거나, 월별 매출 합계를 구해야 하는 상황을 자주 마주칩니다. 이런 그룹별 집계는 데이터 분석에서 가장 핵심적인 작업 중 하나입니다.
이런 작업을 수작업으로 하려면 각 그룹을 필터링하고, 계산하고, 결과를 합치는 복잡한 과정을 거쳐야 합니다. 코드도 길어지고 실수하기 쉬우며, 성능도 좋지 않습니다.
바로 이럴 때 필요한 것이 group_by와 agg입니다. SQL의 GROUP BY처럼 데이터를 그룹으로 나누고, 각 그룹에 대해 집계 함수를 적용하여 요약 통계를 쉽게 구할 수 있습니다.
개요
간단히 말해서, group_by는 하나 이상의 컬럼을 기준으로 데이터를 그룹으로 나누고, agg는 각 그룹에 대해 집계 함수를 적용하는 기능입니다. Pandas의 groupby()와 개념은 같지만, Polars는 훨씬 빠르고 메모리 효율적입니다.
특히 lazy evaluation에서는 group_by와 agg를 최적화하여 한 번의 패스로 여러 집계를 동시에 수행합니다. 예를 들어, 고객별 구매 패턴 분석, 지역별 매출 통계, 시간대별 트래픽 분석 등 실무에서 매일 사용하는 필수 기능입니다.
기존 Pandas에서는 df.groupby('department')['salary'].mean()처럼 작성했다면, Polars에서는 df.group_by('department').agg(pl.col('salary').mean())으로 표현식 기반으로 작성합니다. group_by와 agg의 핵심 특징은 첫째, 여러 집계를 한 번에 수행할 수 있고, 둘째, 표현식 기반이라 복잡한 집계도 간결하게 작성 가능하며, 셋째, 멀티스레드로 각 그룹을 병렬 처리합니다.
이러한 특징들이 복잡한 분석을 간단하고 빠르게 만들어줍니다.
코드 예제
import polars as pl
df = pl.DataFrame({
'department': ['IT', 'HR', 'IT', 'HR', 'IT'],
'employee': ['Alice', 'Bob', 'Charlie', 'David', 'Eve'],
'salary': [50000, 60000, 70000, 55000, 65000],
'experience': [3, 5, 7, 4, 6]
})
# 단일 집계
dept_avg = df.group_by('department').agg(
pl.col('salary').mean().alias('avg_salary')
)
# 여러 집계 동시 수행
dept_stats = df.group_by('department').agg([
pl.col('salary').mean().alias('avg_salary'),
pl.col('salary').max().alias('max_salary'),
pl.col('employee').count().alias('employee_count'),
pl.col('experience').sum().alias('total_experience')
])
설명
이것이 하는 일: group_by는 지정한 컬럼의 고유값별로 데이터를 나누고, agg는 각 그룹에 대해 집계 함수를 적용하여 요약된 DataFrame을 반환합니다. 첫 번째로, 가장 기본적인 사용법은 df.group_by('컬럼명').agg(집계표현식)입니다.
df.group_by('department').agg(pl.col('salary').mean())는 department의 각 고유값(IT, HR)별로 그룹을 만들고, 각 그룹의 salary 평균을 계산합니다. 결과는 department와 평균값 두 개의 컬럼을 가진 DataFrame입니다.
그 다음으로, 여러 집계를 동시에 수행할 때는 agg 안에 리스트로 표현식들을 전달합니다. 각 표현식은 독립적으로 평가되며, Polars는 이를 병렬로 처리하여 성능을 극대화합니다.
예를 들어, 평균, 최댓값, 개수를 한 번에 구하면 세 번 순회하는 것보다 훨씬 빠릅니다. 추가로, 다양한 집계 함수를 사용할 수 있습니다.
mean(), sum(), count(), min(), max()는 기본이고, std()(표준편차), median()(중앙값), quantile()(분위수) 등 통계 함수도 지원합니다. 심지어 pl.col('salary').filter(pl.col('experience') > 5).mean()처럼 조건부 집계도 가능합니다.
마지막으로, 여러 컬럼으로 그룹핑하려면 df.group_by(['department', 'team'])처럼 리스트로 전달합니다. 이는 SQL의 GROUP BY department, team과 같으며, 각 조합별로 그룹이 생성됩니다.
여러분이 이 코드를 사용하면 빠른 그룹 집계, 여러 통계량 동시 계산, 복잡한 조건부 집계가 가능해집니다. 특히 수백만 행을 그룹핑할 때 Pandas보다 5-10배 빠른 성능을 보입니다.
실전 팁
💡 집계 결과를 다시 원본과 조인하려면 join을 사용하거나, over() 표현식으로 윈도우 함수처럼 사용할 수 있습니다
💡 그룹별 첫 번째/마지막 값을 가져오려면 first(), last()를 사용하세요. 정렬 후 사용하면 최댓값/최솟값의 전체 행을 얻을 수 있습니다
💡 count()는 null을 제외하고 세지만, len()은 전체 행 수를 셉니다. 그룹 크기를 알고 싶다면 len()을 사용하세요
💡 고유값 개수는 n_unique()로 구할 수 있습니다. 예를 들어, 고객별 구매한 제품 종류 수를 셀 때 유용합니다
💡 성능을 위해 그룹핑 컬럼은 카디널리티가 낮은 것부터 나열하세요. 하지만 Polars는 자동 최적화하므로 크게 중요하지 않습니다
6. head와 tail - 일부 행만 보기
시작하며
여러분이 대용량 데이터를 처음 불러왔을 때, 전체 데이터를 확인하기보다는 처음 몇 줄만 빠르게 확인하고 싶은 경우가 많습니다. 데이터의 구조, 컬럼명, 샘플 값들을 파악하는 것이 분석의 첫 단계입니다.
이런 탐색 작업에서 전체 데이터를 로딩하면 시간도 오래 걸리고 메모리도 낭비됩니다. 특히 수억 건의 데이터에서 단지 5줄만 보려는데 전부 처리하는 것은 비효율적입니다.
바로 이럴 때 필요한 것이 head와 tail입니다. 데이터의 처음 또는 마지막 일부만 빠르게 가져와 데이터 구조를 파악하거나 샘플링할 수 있습니다.
개요
간단히 말해서, head는 DataFrame의 처음 n개 행을 반환하고, tail은 마지막 n개 행을 반환하는 기능입니다. Pandas의 head()와 tail()과 같은 역할이지만, Polars는 lazy evaluation과 결합되어 훨씬 효율적입니다.
특히 파일에서 직접 읽을 때 처음 n줄만 파싱하여 I/O 시간을 크게 단축합니다. 예를 들어, 10GB CSV 파일의 처음 5줄만 보는 경우 전체를 읽지 않고 처음 부분만 읽어 즉시 결과를 보여줍니다.
기존에는 데이터 전체를 로딩한 후 슬라이싱했다면, Polars는 lazy mode에서 실제로 필요한 부분만 로딩합니다. head와 tail의 핵심 특징은 첫째, 데이터 탐색과 검증에 필수적이고, 둘째, lazy mode에서 쿼리 최적화가 적용되며, 셋째, 빠른 샘플링이 가능합니다.
이러한 특징들이 대용량 데이터 탐색을 빠르고 효율적으로 만들어줍니다.
코드 예제
import polars as pl
# 큰 데이터 생성 (실무에서는 파일에서 읽음)
df = pl.DataFrame({
'id': range(1000),
'value': range(1000, 2000),
'category': ['A', 'B', 'C'] * 333 + ['A']
})
# 처음 5개 행 보기 (기본값)
first_rows = df.head()
# 처음 10개 행 보기
first_10 = df.head(10)
# 마지막 5개 행 보기
last_rows = df.tail()
# lazy mode에서 최적화
# CSV에서 처음 100줄만 읽기
lazy_result = pl.scan_csv('large_file.csv').head(100).collect()
설명
이것이 하는 일: head(n)은 처음부터 n개 행을 추출하고, tail(n)은 마지막부터 n개 행을 추출하여 새로운 DataFrame을 반환합니다. n을 지정하지 않으면 기본값은 5입니다.
첫 번째로, 가장 기본적인 사용법은 df.head()로 처음 5줄을 보는 것입니다. 데이터를 처음 로딩했을 때 구조를 파악하거나, 전처리 결과를 빠르게 확인할 때 매우 유용합니다.
이는 print(df)보다 훨씬 깔끔하게 결과를 보여줍니다. 그 다음으로, 개수를 지정할 수 있습니다.
df.head(100)은 처음 100줄을 가져옵니다. 이는 테스트 데이터셋을 만들거나, 일부 샘플로 코드를 먼저 검증할 때 유용합니다.
전체 데이터로 테스트하면 시간이 오래 걸리므로 일부만 사용하여 빠르게 반복합니다. 추가로, lazy mode에서 head()를 사용하면 쿼리 최적화가 적용됩니다.
pl.scan_csv('file.csv').filter(...).head(10).collect()처럼 작성하면 Polars는 필터링 후 10개만 필요하다는 것을 알고, 10개를 찾는 즉시 스캔을 중단합니다. 이는 수억 건의 데이터에서 엄청난 성능 향상을 가져옵니다.
마지막으로, tail()은 정렬 결과를 확인하거나 최신 데이터를 볼 때 유용합니다. 시계열 데이터에서 날짜 기준 정렬 후 tail()을 사용하면 가장 최근 데이터를 확인할 수 있습니다.
여러분이 이 코드를 사용하면 빠른 데이터 탐색, 효율적인 샘플링, 메모리 절약이라는 이점을 얻을 수 있습니다. 특히 개발 단계에서 head()로 작은 샘플만 사용하여 빠르게 코드를 검증할 수 있습니다.
실전 팁
💡 대용량 파일을 처음 열 때는 항상 scan_csv().head(100).collect()로 구조를 먼저 파악하세요. 전체를 읽지 않아 빠릅니다
💡 무작위 샘플링이 필요하다면 df.sample(n=100)을 사용하세요. head()는 순서대로 가져오므로 편향될 수 있습니다
💡 데이터 파이프라인 디버깅 시 각 단계마다 .head()를 추가하여 중간 결과를 확인하면 문제를 빠르게 찾을 수 있습니다
💡 tail()은 정렬 후 사용하면 최댓값/최솟값 주변 데이터를 보는 용도로 유용합니다
💡 Jupyter 노트북에서는 자동으로 예쁘게 표시되지만, 스크립트에서는 print()로 감싸야 보기 좋게 출력됩니다
7. unique와 n_unique - 고유값 다루기
시작하며
여러분이 데이터를 분석하다 보면 특정 컬럼에 어떤 값들이 있는지, 몇 가지 종류가 있는지 파악해야 할 때가 많습니다. 예를 들어, 제품 카테고리가 몇 개인지, 어떤 상태값들이 있는지 확인하는 상황입니다.
이런 작업은 데이터 탐색의 핵심입니다. 중복된 값들 때문에 전체 데이터를 보면 파악하기 어렵고, 데이터 품질 검증이나 카테고리 분석에서도 고유값을 아는 것이 첫 단계입니다.
바로 이럴 때 필요한 것이 unique와 n_unique입니다. unique는 고유한 값들의 목록을 반환하고, n_unique는 고유값의 개수를 세어 데이터를 빠르게 이해할 수 있게 해줍니다.
개요
간단히 말해서, unique는 컬럼의 중복을 제거한 고유값들을 반환하고, n_unique는 고유값의 개수를 반환하는 기능입니다. Pandas의 unique()와 nunique()와 비슷하지만, Polars는 해시 기반 알고리즘으로 훨씬 빠르게 처리합니다.
특히 대용량 데이터에서 카디널리티(고유값 개수)를 확인할 때 매우 유용합니다. 예를 들어, 로그 데이터에서 고유 사용자 수를 세거나, 상품 데이터에서 카테고리 목록을 추출할 때 자주 사용됩니다.
기존 Pandas에서는 df['column'].unique()와 df['column'].nunique()를 사용했다면, Polars에서는 df['column'].unique()와 df['column'].n_unique() 또는 표현식으로 pl.col('column').n_unique()를 사용합니다. unique와 n_unique의 핵심 특징은 첫째, 중복 제거와 카운팅이 매우 빠르고, 둘째, null 값 처리 옵션이 있으며, 셋째, 표현식 기반으로 집계와 함께 사용 가능합니다.
이러한 특징들이 데이터 탐색과 분석을 효율적으로 만들어줍니다.
코드 예제
import polars as pl
df = pl.DataFrame({
'category': ['A', 'B', 'A', 'C', 'B', 'A', 'C'],
'value': [1, 2, 1, 3, 2, 1, 3],
'status': ['active', 'inactive', 'active', None, 'active', 'inactive', 'active']
})
# 고유값 목록 가져오기
unique_categories = df['category'].unique()
# 고유값 개수 세기
num_unique = df['category'].n_unique()
# DataFrame 전체에서 중복 행 제거
unique_rows = df.unique()
# 특정 컬럼 기준으로 중복 제거
unique_by_category = df.unique(subset=['category'])
# 표현식으로 그룹별 고유값 개수
category_stats = df.group_by('category').agg(
pl.col('status').n_unique().alias('unique_statuses')
)
설명
이것이 하는 일: unique()는 중복을 제거한 고유값들을 Series 또는 DataFrame으로 반환하고, n_unique()는 고유값의 개수를 정수로 반환합니다. 첫 번째로, 가장 기본적인 사용법은 df['컬럼명'].unique()로 해당 컬럼의 고유값들을 가져오는 것입니다.
결과는 Series 형태로 반환되며, 순서는 보장되지 않습니다. 만약 정렬된 결과가 필요하면 .unique().sort()처럼 체이닝하세요.
그 다음으로, n_unique()는 고유값의 개수만 필요할 때 사용합니다. df['category'].n_unique()는 unique()를 호출하고 len()을 하는 것보다 효율적입니다.
내부적으로 카운팅만 하므로 메모리에 모든 고유값을 저장하지 않습니다. 추가로, DataFrame 전체에서 중복 행을 제거하려면 df.unique()를 사용합니다.
모든 컬럼의 값이 같은 행만 제거됩니다. 특정 컬럼만 기준으로 삼으려면 df.unique(subset=['col1', 'col2'])처럼 subset 파라미터를 지정하세요.
마지막으로, 표현식으로 사용하면 group_by와 함께 강력한 분석이 가능합니다. df.group_by('category').agg(pl.col('status').n_unique())는 각 카테고리별로 status의 고유값 개수를 세어줍니다.
이는 카테고리별 다양성을 분석할 때 유용합니다. 여러분이 이 코드를 사용하면 빠른 카디널리티 확인, 효율적인 중복 제거, 데이터 품질 검증이 가능해집니다.
특히 대용량 데이터에서 고유값을 세는 것은 Pandas보다 5-10배 빠릅니다.
실전 팁
💡 unique()는 순서를 보장하지 않으므로, 정렬이 필요하면 .unique().sort()를 사용하세요
💡 null 값을 제외하고 고유값을 세려면 df['col'].drop_nulls().n_unique()처럼 체이닝하세요
💡 첫 번째 등장 위치의 값을 유지하려면 df.unique(keep='first', subset=['col'])를 사용하세요. 마지막은 keep='last'
💡 카디널리티가 매우 높은 컬럼(예: ID)에서는 n_unique()만 사용하고 unique()는 피하세요. 메모리를 많이 사용할 수 있습니다
💡 데이터 품질 검증 시 예상 고유값 개수와 실제를 비교하여 이상치를 찾을 수 있습니다
8. join - 데이터 결합하기
시작하며
여러분이 실무에서 데이터를 다루다 보면 여러 테이블의 정보를 합쳐야 하는 경우가 정말 많습니다. 예를 들어, 주문 테이블과 고객 테이블을 고객 ID로 연결하여 고객 정보와 함께 주문 내역을 분석하는 상황입니다.
이런 작업을 수작업으로 하려면 각 테이블을 순회하며 매칭되는 행을 찾아야 하는데, 이는 매우 비효율적이고 코드도 복잡해집니다. 특히 대용량 데이터에서는 성능 문제가 심각합니다.
바로 이럴 때 필요한 것이 join입니다. SQL의 JOIN처럼 공통 컬럼을 기준으로 두 DataFrame을 효율적으로 결합하여 통합된 분석이 가능하게 해줍니다.
개요
간단히 말해서, join은 하나 이상의 공통 컬럼(키)을 기준으로 두 DataFrame을 결합하는 기능입니다. Pandas의 merge()나 join()과 비슷하지만, Polars는 해시 조인과 멀티스레딩으로 훨씬 빠릅니다.
특히 lazy mode에서는 조인 순서를 최적화하여 성능을 극대화합니다. 예를 들어, 주문-고객 조인, 로그-사용자 정보 조인, 시계열 데이터 병합 등 데이터베이스처럼 관계형 데이터를 다룰 때 필수적입니다.
기존 Pandas에서는 df1.merge(df2, on='key')처럼 작성했다면, Polars에서는 df1.join(df2, on='key', how='inner')로 더 SQL에 가깝게 표현합니다. join의 핵심 특징은 첫째, inner, left, outer, cross 등 다양한 조인 타입을 지원하고, 둘째, 여러 컬럼을 키로 사용할 수 있으며, 셋째, 해시 조인으로 대용량 데이터도 빠르게 처리합니다.
이러한 특징들이 복잡한 데이터 통합을 간단하고 효율적으로 만들어줍니다.
코드 예제
import polars as pl
# 주문 테이블
orders = pl.DataFrame({
'order_id': [1, 2, 3, 4],
'customer_id': [101, 102, 101, 103],
'amount': [1000, 1500, 2000, 500]
})
# 고객 테이블
customers = pl.DataFrame({
'customer_id': [101, 102, 104],
'name': ['Alice', 'Bob', 'Charlie'],
'city': ['Seoul', 'Busan', 'Incheon']
})
# Inner join - 양쪽에 모두 있는 것만
inner_result = orders.join(customers, on='customer_id', how='inner')
# Left join - 왼쪽(orders) 기준, 오른쪽 없으면 null
left_result = orders.join(customers, on='customer_id', how='left')
# 여러 컬럼으로 조인
# df1.join(df2, on=['key1', 'key2'], how='inner')
설명
이것이 하는 일: join은 지정한 키 컬럼의 값을 기준으로 두 DataFrame의 행들을 매칭하여 하나의 DataFrame으로 결합합니다. 조인 방식에 따라 매칭되지 않는 행의 처리가 달라집니다.
첫 번째로, inner join은 양쪽 DataFrame에 모두 존재하는 키만 결과에 포함됩니다. orders.join(customers, on='customer_id', how='inner')는 customer_id가 양쪽에 모두 있는 경우만 결합합니다.
위 예제에서 customer_id 101, 102만 매칭되므로 해당 주문만 결과에 나타납니다. 그 다음으로, left join은 왼쪽 DataFrame의 모든 행을 유지하고, 오른쪽에서 매칭되는 것을 붙입니다.
매칭이 없으면 오른쪽 컬럼들은 null이 됩니다. how='left'를 사용하면 모든 주문이 결과에 포함되고, customer_id 103은 고객 정보가 null로 표시됩니다.
추가로, outer join(how='outer')은 양쪽 모든 행을 포함하고 매칭되지 않는 부분은 null로 채웁니다. cross join(how='cross')은 모든 조합을 생성하는 카테시안 곱입니다.
실무에서는 주로 inner와 left를 사용합니다. 마지막으로, 컬럼명이 다른 경우 left_on과 right_on을 따로 지정할 수 있습니다.
df1.join(df2, left_on='id', right_on='customer_id')처럼 사용하면 서로 다른 이름의 컬럼을 키로 사용할 수 있습니다. 여러 컬럼으로 조인하려면 리스트를 전달하세요.
여러분이 이 코드를 사용하면 관계형 데이터 통합, 빠른 조인 성능, SQL 스타일의 직관적인 코드라는 이점을 얻을 수 있습니다. 특히 수백만 행의 조인에서 Pandas보다 3-10배 빠른 성능을 경험할 수 있습니다.
실전 팁
💡 조인 전 키 컬럼의 중복을 확인하세요. 일대다 관계가 아니라 다대다면 결과가 폭발적으로 늘어날 수 있습니다
💡 조인 후 행 수가 예상과 다르면 키의 null 값이나 데이터 타입 불일치를 확인하세요. null은 매칭되지 않습니다
💡 성능을 위해 작은 DataFrame을 오른쪽에 배치하세요. Polars는 오른쪽을 해시 테이블로 만듭니다
💡 suffix 파라미터로 중복 컬럼명에 접미사를 추가할 수 있습니다. join(..., suffix='_right')처럼 사용하세요
💡 lazy mode에서 조인 순서가 중요한 경우 명시적으로 collect() 후 조인하거나, 쿼리 플랜을 확인하세요
9. is_null과 fill_null - 결측치 다루기
시작하며
여러분이 실제 데이터를 다루다 보면 완벽하게 깨끗한 데이터는 거의 없습니다. 센서 오류로 누락된 값, 사용자가 입력하지 않은 필드, 외부 API에서 받지 못한 정보 등 결측치는 항상 존재합니다.
이런 결측치를 제대로 처리하지 않으면 분석 결과가 왜곡되거나, 심지어 코드가 에러를 일으킬 수 있습니다. 평균을 계산하거나 머신러닝 모델을 학습할 때 결측치 처리는 필수 전처리 단계입니다.
바로 이럴 때 필요한 것이 is_null과 fill_null입니다. 결측치를 찾아내고, 적절한 값으로 채워 데이터 품질을 향상시킬 수 있습니다.
개요
간단히 말해서, is_null은 null 값을 찾아 Boolean으로 표시하고, fill_null은 null 값을 지정한 값이나 전략으로 채우는 기능입니다. Pandas의 isna(), fillna()와 비슷하지만, Polars는 더 다양한 채우기 전략을 제공하고 성능도 뛰어납니다.
특히 forward fill, backward fill 같은 시계열 데이터 처리나, 그룹별로 다르게 채우기 등 복잡한 전략도 간단하게 구현할 수 있습니다. 예를 들어, 센서 데이터의 누락값을 이전 값으로 채우거나, 설문조사의 미응답을 평균값으로 대체할 때 유용합니다.
기존 Pandas에서는 df['col'].fillna(0)처럼 작성했다면, Polars에서는 df.with_columns(pl.col('col').fill_null(0))로 표현식 기반으로 작성합니다. is_null과 fill_null의 핵심 특징은 첫째, 다양한 채우기 전략(고정값, forward, backward, mean 등)을 지원하고, 둘째, 표현식 기반이라 조건부 처리가 가능하며, 셋째, 그룹별로 다른 전략을 적용할 수 있습니다.
이러한 특징들이 복잡한 결측치 처리를 간단하고 유연하게 만들어줍니다.
코드 예제
import polars as pl
df = pl.DataFrame({
'name': ['Alice', 'Bob', None, 'David'],
'age': [25, None, 35, 28],
'salary': [50000, 60000, None, 55000]
})
# null 값 찾기 (Boolean Series 반환)
has_null = df['name'].is_null()
# null이 있는 행만 필터링
null_rows = df.filter(pl.col('age').is_null())
# 고정값으로 채우기
filled_df = df.with_columns(
pl.col('age').fill_null(0),
pl.col('salary').fill_null(50000)
)
# 평균값으로 채우기
filled_mean = df.with_columns(
pl.col('salary').fill_null(pl.col('salary').mean())
)
# Forward fill (이전 값으로 채우기)
filled_forward = df.with_columns(
pl.col('age').fill_null(strategy='forward')
)
설명
이것이 하는 일: is_null()은 각 값이 null인지 Boolean으로 표시하고, fill_null()은 null 값을 지정한 값이나 전략에 따라 채워 새로운 Series/DataFrame을 반환합니다. 첫 번째로, is_null()로 결측치를 찾을 수 있습니다.
df['name'].is_null()은 각 행이 null인지를 True/False로 나타내는 Boolean Series를 반환합니다. 이를 filter와 함께 사용하면 df.filter(pl.col('age').is_null())처럼 결측치가 있는 행만 추출하여 데이터 품질을 검증할 수 있습니다.
그 다음으로, fill_null()로 결측치를 채울 수 있습니다. 가장 간단한 방법은 고정값을 전달하는 것입니다.
pl.col('age').fill_null(0)은 age의 모든 null을 0으로 바꿉니다. 숫자뿐 아니라 문자열, 날짜 등 모든 타입에 사용할 수 있습니다.
추가로, 통계값으로 채울 수도 있습니다. pl.col('salary').fill_null(pl.col('salary').mean())처럼 표현식을 중첩하면 salary의 평균값으로 null을 채웁니다.
median(), max(), min() 등 다양한 집계 함수를 사용할 수 있어 데이터 특성에 맞는 대체값을 선택할 수 있습니다. 마지막으로, 시계열 데이터에서는 전략을 지정할 수 있습니다.
fill_null(strategy='forward')는 이전 값으로 채우고(forward fill), strategy='backward'는 다음 값으로 채웁니다(backward fill). 센서 데이터처럼 시간 순서가 중요한 경우 매우 유용합니다.
여러분이 이 코드를 사용하면 데이터 품질 향상, 분석 정확도 개선, 유연한 결측치 처리 전략이라는 이점을 얻을 수 있습니다. 특히 그룹별로 다른 평균값으로 채우기 같은 복잡한 작업도 간단하게 구현됩니다.
실전 팁
💡 결측치를 채우기 전에 왜 결측치가 발생했는지 먼저 분석하세요. 무조건 채우는 것이 항상 좋은 것은 아닙니다
💡 is_not_null()로 null이 아닌 값만 필터링할 수 있습니다. df.filter(pl.col('email').is_not_null())처럼 사용하세요
💡 여러 컬럼의 결측치를 한 번에 확인하려면 df.null_count()를 사용하세요. 각 컬럼별 null 개수를 보여줍니다
💡 forward/backward fill은 정렬된 데이터에서만 의미가 있습니다. 시계열은 먼저 날짜로 정렬하세요
💡 카테고리형 데이터는 최빈값(mode)으로 채우는 것도 고려해보세요. 직접 구현하거나 그룹별 집계를 활용하세요
10. cast - 데이터 타입 변환하기
시작하며
여러분이 CSV 파일을 읽었을 때 모든 컬럼이 문자열로 인식되거나, 숫자를 계산하려는데 타입이 맞지 않아 에러가 발생하는 경우를 겪어본 적 있나요? 데이터 타입이 맞지 않으면 연산도 불가능하고 메모리도 낭비됩니다.
이런 문제는 데이터를 처음 로딩할 때나 전처리 과정에서 자주 발생합니다. 날짜 문자열을 Date 타입으로, 숫자 문자열을 Int64로, Float를 Int로 변환하는 등 타입 변환은 데이터 처리의 기본입니다.
바로 이럴 때 필요한 것이 cast입니다. 컬럼의 데이터 타입을 원하는 타입으로 안전하고 효율적으로 변환하여 올바른 분석과 연산이 가능하게 해줍니다.
개요
간단히 말해서, cast는 컬럼의 데이터 타입을 다른 타입으로 변환하는 기능입니다. Pandas의 astype()과 비슷하지만, Polars는 더 엄격한 타입 시스템과 명확한 에러 메시지를 제공합니다.
특히 Arrow 타입 시스템을 사용하여 메모리 효율적이고, 변환 실패 시 strict 파라미터로 처리 방식을 제어할 수 있습니다. 예를 들어, '123'을 정수로, '2024-01-01'을 날짜로, True/False를 1/0으로 변환하는 등 다양한 상황에서 사용됩니다.
기존 Pandas에서는 df['col'].astype(int)처럼 작성했다면, Polars에서는 df.with_columns(pl.col('col').cast(pl.Int64))로 명시적인 타입을 지정합니다. cast의 핵심 특징은 첫째, 풍부한 타입 시스템(Int8~Int64, Float32/64, Date, Datetime 등)을 지원하고, 둘째, 변환 실패 시 처리 방식을 제어할 수 있으며, 셋째, 표현식 기반이라 체이닝이 자연스럽습니다.
이러한 특징들이 안전하고 정확한 데이터 타입 관리를 가능하게 합니다.
코드 예제
import polars as pl
df = pl.DataFrame({
'id': ['1', '2', '3'], # 문자열
'price': ['100.5', '200.3', '150.7'], # 문자열
'is_active': [1, 0, 1], # 정수
'date': ['2024-01-01', '2024-01-02', '2024-01-03']
})
# 문자열을 정수로 변환
df_converted = df.with_columns(
pl.col('id').cast(pl.Int64)
)
# 여러 컬럼 동시 변환
df_multi = df.with_columns([
pl.col('id').cast(pl.Int64),
pl.col('price').cast(pl.Float64),
pl.col('is_active').cast(pl.Boolean),
pl.col('date').str.strptime(pl.Date, '%Y-%m-%d')
])
# 변환 실패 시 null로 (strict=False)
df_safe = df.with_columns(
pl.col('price').cast(pl.Int32, strict=False)
)
설명
이것이 하는 일: cast()는 지정한 타입으로 데이터를 변환합니다. 변환이 불가능하면 기본적으로 에러를 발생시키며, strict=False로 설정하면 null을 반환합니다.
첫 번째로, 가장 기본적인 사용법은 pl.col('컬럼').cast(타입)입니다. pl.col('id').cast(pl.Int64)는 id 컬럼을 64비트 정수로 변환합니다.
Polars는 Int8, Int16, Int32, Int64처럼 크기를 명시적으로 지정하여 메모리를 최적화할 수 있습니다. 그 다음으로, 실수와 정수 간 변환도 가능합니다.
Float64를 Int32로 변환하면 소수점이 버려집니다. 반대로 Int를 Float로 변환하면 정확도 손실 없이 변환됩니다.
pl.col('price').cast(pl.Int32)는 100.5를 100으로 만듭니다. 추가로, Boolean 변환은 0/1을 False/True로, 또는 그 반대로 변환합니다.
pl.col('is_active').cast(pl.Boolean)은 1을 True로, 0을 False로 바꿉니다. 문자열 'true', 'false'도 Boolean으로 변환 가능합니다.
마지막으로, 날짜/시간 변환은 str.strptime()을 사용합니다. pl.col('date').str.strptime(pl.Date, '%Y-%m-%d')는 날짜 형식 문자열을 Date 타입으로 파싱합니다.
파싱 실패 시 에러가 발생하므로 형식이 일치하는지 확인하세요. 여러분이 이 코드를 사용하면 메모리 최적화, 올바른 연산 수행, 타입 안정성 향상이라는 이점을 얻을 수 있습니다.
특히 큰 데이터셋에서 Int64 대신 Int32를 사용하면 메모리를 절반으로 줄일 수 있습니다.
실전 팁
💡 데이터를 읽을 때부터 올바른 타입을 지정하면 나중에 변환할 필요가 없습니다. pl.read_csv(..., dtypes={'id': pl.Int64})처럼 사용하세요
💡 변환 실패가 예상되면 strict=False를 사용하여 에러 대신 null을 받고, 나중에 null을 처리하세요
💡 큰 정수는 Int64, 작은 정수는 Int32/Int16을 사용하여 메모리를 절약하세요. 범위를 확인 후 적절한 타입을 선택하세요
💡 날짜 문자열의 형식이 일관되지 않으면 먼저 정규식이나 str.replace()로 통일한 후 파싱하세요
💡 카테고리형 데이터는 pl.Categorical 타입으로 변환하면 메모리와 성능이 크게 향상됩니다. 특히 중복이 많은 문자열 컬럼에 유용합니다