캐스팅
캐스팅 (Casting)
열의 데이터 타입을 다른 타입으로 바꾸는 작업을 캐스팅(casting)이라고 하고, Polars에서는 함수 cast로 할 수 있어요. 이 섹션에서는 숫자 사이의 변환, 다운캐스팅, 문자열·불리언·시간 타입과의 변환까지 캐스팅의 기본기를 차례로 살펴볼게요.
출처: 공식문서
함수 cast는 strict 파라미터를 갖는데, 이 값이 소스 타입에서 대상 타입으로 변환할 수 없는 값을 만났을 때 Polars가 어떻게 행동할지를 결정해요. 기본 동작은 strict=True로, 변환에 실패한 값에 대한 세부 정보와 함께 오류를 던져 사용자에게 알립니다. 반면 strict=False로 설정하면 대상 타입으로 변환할 수 없는 값은 조용히 null로 바뀌어요.
기본 예시 (Basic example)
정수와 부동소수점을 모두 담고 있는 아래 데이터프레임을 생각해 볼게요.
import polars as pl
df = pl.DataFrame(
{
"integers": [1, 2, 3],
"big_integers": [10000002, 2, 30000003],
"floats": [4.0, 5.8, -6.3],
}
)
print(df)
부동소수점과 정수 사이(또는 그 반대)의 변환은 함수 cast로 합니다.
result = df.select(
pl.col("integers").cast(pl.Float32).alias("integers_as_floats"),
pl.col("floats").cast(pl.Int32).alias("floats_as_integers"),
)
print(result)
여기서 한 가지 주의할 점은, 부동소수점을 정수 타입으로 캐스팅할 때 소수점 이하는 절삭(truncate)된다는 거예요.
수치 데이터 타입 다운캐스팅 (Downcasting numerical data types)
숫자 타입의 정밀도를 낮추면 열이 차지하는 메모리 공간을 줄일 수 있어요. 아래 코드는 Int64에서 Int16으로, Float64에서 Float32로 캐스팅해 메모리 사용량을 낮추는 예시입니다.
print(f"Before downcasting: {df.estimated_size()} bytes")
result = df.with_columns(
pl.col("integers").cast(pl.Int16),
pl.col("floats").cast(pl.Float32),
)
print(f"After downcasting: {result.estimated_size()} bytes")
다운캐스팅을 할 때는 고른 비트 수(예: 64, 32, 16)가 열 안의 가장 큰 값과 가장 작은 값을 담기에 충분한지 꼭 확인해야 해요. 예를 들어 32비트 부호 정수(Int32)는 -2147483648부터 2147483647까지 표현할 수 있지만, 8비트 부호 정수는 -128부터 127까지만 표현할 수 있습니다. 정밀도가 부족한 타입으로 다운캐스팅하려 하면 Polars가 오류를 던져요.
from polars.exceptions import InvalidOperationError
try:
result = df.select(pl.col("big_integers").cast(pl.Int8))
print(result)
except InvalidOperationError as err:
print(err)
strict 파라미터를 False로 설정하면 오버플로/언더플로가 발생하는 값들이 null로 변환됩니다.
result = df.select(pl.col("big_integers").cast(pl.Int8, strict=False))
print(result)
문자열을 숫자 타입으로 변환 (Converting strings to numeric data types)
숫자를 나타내는 문자열은 캐스팅을 통해 적절한 데이터 타입으로 바꿀 수 있고, 반대 방향의 변환도 가능해요.
df = pl.DataFrame(
{
"integers_as_strings": ["1", "2", "3"],
"floats_as_strings": ["4.0", "5.8", "-6.3"],
"floats": [4.0, 5.8, -6.3],
}
)
result = df.select(
pl.col("integers_as_strings").cast(pl.Int32),
pl.col("floats_as_strings").cast(pl.Float64),
pl.col("floats").cast(pl.String),
)
print(result)
열 안에 숫자가 아닌 값이나 형식이 엉성한 값이 있으면 Polars는 변환 오류에 대한 세부 정보와 함께 오류를 던져요. strict=False로 설정하면 오류 대신 null 값을 얻을 수 있습니다.
df = pl.DataFrame(
{
"floats": ["4.0", "5.8", "- 6 . 3"],
}
)
try:
result = df.select(pl.col("floats").cast(pl.Float64))
except InvalidOperationError as err:
print(err)
불리언 (Booleans)
불리언은 1(True) 또는 0(False)으로 표현할 수 있어요. 숫자 타입과 불리언 사이(또는 그 반대)의 캐스팅도 가능합니다. 숫자를 불리언으로 변환할 때는 Python의 Truthy·Falsy 규칙에 맞춰 숫자 0은 False로, 나머지 숫자는 전부 True로 바뀝니다.
df = pl.DataFrame(
{
"integers": [-1, 0, 2, 3, 4],
"floats": [0.0, 1.0, 2.0, 3.0, 4.0],
"bools": [True, False, True, False, True],
}
)
result = df.select(
pl.col("integers").cast(pl.Boolean),
pl.col("floats").cast(pl.Boolean),
pl.col("bools").cast(pl.Int8),
)
print(result)
shape: (5, 3)
┌──────────┬────────┬───────┐
│ integers ┆ floats ┆ bools │
│ --- ┆ --- ┆ --- │
│ bool ┆ bool ┆ i8 │
╞══════════╪════════╪═══════╡
│ true ┆ false ┆ 1 │
│ false ┆ true ┆ 0 │
│ true ┆ true ┆ 1 │
│ true ┆ true ┆ 0 │
│ true ┆ true ┆ 1 │
└──────────┴────────┴───────┘
시간 데이터 타입 파싱·포맷 (Parsing / formatting temporal data types)
모든 시간 데이터 타입은 내부적으로 기준 시점(epoch) 이후로 흐른 시간 단위의 개수로 표현돼요. 예를 들어 Date 타입 값은 epoch 이후 흐른 일수로, Datetime 타입은 마이크로초(us)로, Time 타입은 나노초(ns)로 저장됩니다.
숫자 타입과 시간 타입 사이의 캐스팅은 허용되며, 이 관계를 그대로 드러내 줍니다.
from datetime import date, datetime, time
df = pl.DataFrame(
{
"date": [
date(1970, 1, 1), # epoch
date(1970, 1, 10), # 9 days later
],
"datetime": [
datetime(1970, 1, 1, 0, 0, 0), # epoch
datetime(1970, 1, 1, 0, 1, 0), # 1 minute later
],
"time": [
time(0, 0, 0), # reference time
time(0, 0, 1), # 1 second later
],
}
)
result = df.select(
pl.col("date").cast(pl.Int64).alias("days_since_epoch"),
pl.col("datetime").cast(pl.Int64).alias("us_since_epoch"),
pl.col("time").cast(pl.Int64).alias("ns_since_midnight"),
)
print(result)
시간 타입을 문자열로 포맷할 때는 함수 dt.to_string을, 문자열에서 시간 타입을 파싱할 때는 함수 str.to_datetime을 써요. 두 함수 모두 포맷에 chrono 포맷 문법을 사용합니다.
df = pl.DataFrame(
{
"date": [date(2022, 1, 1), date(2022, 1, 2)],
"string": ["2022-01-01", "2022-01-02"],
}
)
result = df.select(
pl.col("date").dt.to_string("%Y-%m-%d"),
pl.col("string").str.to_datetime("%Y-%m-%d"),
)
print(result)
참고로 str.to_datetime은 시간대(timezone) 기능을 지원하는 추가 옵션을 갖고 있어요. 더 자세한 내용은 API 문서를 참고하세요.