본문으로 건너뛰기
김신건의 로그

[Pandas] Nullable Types (Int64, boolean, string[pyarrow])

· 수정 · 📖 약 1분 · 521자/단어 #python #pandas #dtype #nullable
Pandas Nullable Types, Pandas nullable, Pandas Int64, Pandas boolean, Pandas string

정의

Nullable dtypes 는 NaN/None 을 타입 그대로 표현할 수 있는 pandas 의 새 타입 시스템. NumPy 기반의 int64 (NaN 미허용) 의 한계를 극복.

  • Int8, Int16, Int32, Int64 : nullable 정수 (대문자 I)
  • UInt8, …, UInt64 : unsigned
  • Float32, Float64 : nullable 실수
  • boolean : nullable bool
  • string : 명시적 문자열 (object 대신)

dtype 계열 시각화

flowchart TD
    A["pandas dtype 계열"] --> B["NumPy 계열 (기존)"]
    A --> C["Nullable 계열 (pandas 1.0+)"]
    A --> D["Arrow 계열 (pandas 2.0+)"]

    B --> B1["int64 / float64 / bool / object"]
    C --> C1["Int64 / boolean / string"]
    C --> C2["Float32 / Float64"]
    D --> D1["int64[pyarrow] / string[pyarrow]"]
    D --> D2["timestamp[ns][pyarrow]"]

사용 상황

상황권장 dtype
NaN 포함 정수 컬럼Int64 (nullable)
DB 에서 읽어 온 boolboolean
문자열 메모리 절약string[pyarrow]
Arrow 파이프라인 통합dtype_backend='pyarrow'
단순 수치 연산 (NaN 없음)int64 / float64 (NumPy, 빠름)

왜 필요한가

NaN 이 있으면 int → float 강등 문제

import pandas as pd
import numpy as np
df = pd.DataFrame({'count': [1, 2, None, 4]})
df['count'].dtype     # float64 (NaN 때문에 int 못 함)

NumPy 의 int64 는 NaN 표현 불가 → NaN 행이 하나라도 있으면 float 로 변환.

nullable 로 해결

df['count'] = df['count'].astype('Int64')   # 대문자 I
df['count']
# 0       1
# 1       2
# 2    <NA>
# 3       4
# dtype: Int64

정수 유지 + NaN 표현.

사용

df = df.astype({
    'id': 'Int32',
    'is_active': 'boolean',
    'name': 'string',
})

# read_csv 에서 직접
df = pd.read_csv('data.csv', dtype={
    'id': 'Int32',
    'is_active': 'boolean',
})

# pyarrow backend (pandas 2.0+)
df = pd.read_csv('data.csv', dtype_backend='numpy_nullable')

pd.NA

nullable dtype 의 결측치 표현.

pd.NA
# 산술: any op with pd.NA = pd.NA
pd.NA + 1            # <NA>
pd.NA == pd.NA       # <NA> (NaN 처럼)

NaN 과 다르게 boolean context 에서 ambiguous → 명시적 비교 필요.

df['x'] == 5     # 결과에 pd.NA 가 섞일 수 있음
# 필터링은 isna() / notna() 활용
df[df['x'].notna() & (df['x'] == 5)]

string dtype

df['name'] = df['name'].astype('string')   # 명시적 string
df['name'] = df['name'].astype('string[pyarrow]')   # arrow 백킹 (더 빠름)

object vs string vs string[pyarrow]

dtype저장str accessor메모리
objectPython str 리스트
stringpandas 내부중간
string[pyarrow]Arrow 백킹작음

문자열 컬럼은 가능하면 string[pyarrow] 권장.

pyarrow backend (pandas 2.0+)

pd.options.mode.dtype_backend = 'pyarrow'
df = pd.read_csv('data.csv', dtype_backend='pyarrow')
df.dtypes
# id            int64[pyarrow]
# name          string[pyarrow]
# created_at    timestamp[ns][pyarrow]

Arrow 백킹의 장점:

  • 메모리 효율
  • 빠른 IO (Parquet 와 호환)
  • 정밀한 dtype (timezone, decimal 등)

Pandas pyarrow backend 참고.

타입별 메모리 비교

import pandas as pd

s_obj = pd.Series(['hello'] * 10_000, dtype='object')
s_str = pd.Series(['hello'] * 10_000, dtype='string')
s_arrow = pd.Series(['hello'] * 10_000, dtype='string[pyarrow]')

# deep=True 로 실제 Python 객체 포함 측정
print(s_obj.memory_usage(deep=True))    # ~590000 bytes
print(s_str.memory_usage(deep=True))    # ~590000 bytes
print(s_arrow.memory_usage(deep=True))  # ~50000 bytes (10x 이상 절약)

pandas 2.x 전환 패턴

# 전체 DataFrame 을 nullable 로 일괄 전환
df = df.convert_dtypes()
# int64 -> Int64, object -> string, float64 -> Float64 (자동 추론)

# pyarrow 전환
df = df.convert_dtypes(dtype_backend='pyarrow')
# int64 -> int64[pyarrow], string -> large_string[pyarrow]

특정 컬럼만 선택적 전환

int_cols = df.select_dtypes('int').columns
df[int_cols] = df[int_cols].astype('Int64')

str_cols = df.select_dtypes('object').columns
df[str_cols] = df[str_cols].astype('string')

타입 간 연산 동작

s_int = pd.array([1, 2, pd.NA, 4], dtype='Int64')
s_float = pd.array([1.5, 2.5, pd.NA, 4.5], dtype='Float64')

# 산술: NA 전파
s_int + 10           # [11, 12, <NA>, 14]
s_int + s_float      # [2.5, 4.5, <NA>, 8.5]

# 비교
s_int > 2            # [False, False, <NA>, True] (boolean dtype 반환)

# 집계: NA 자동 무시
s_int.sum()          # 7  (1+2+4)
s_int.mean()         # 2.333...  (NA 제외)

NA 처리 흐름

flowchart LR
    A["NA 포함 컬럼"] --> B{"어떤 연산?"}
    B -->|"필터링"| C["isna() / notna()"]
    B -->|"집계"| D["sum() / mean() NA 자동 무시"]
    B -->|"boolean context"| E["fillna(False) 명시"]
    B -->|"numpy 함수"| F["fillna 후 변환"]

    C --> G["df[df['x'].notna()]"]
    D --> H["skipna=False 로 NA 포함 집계 가능"]
    E --> I["mask.fillna(False).any()"]
    F --> J["np.where 에는 fillna 선행"]

자주 만나는 함정

1. Int64 (NumPy) vs Int64 (nullable)

df['x'].astype('int64')        # NumPy, NaN 미허용
df['x'].astype('Int64')        # nullable, NaN 허용

대소문자가 의미를 바꾼다.

2. boolean 의 truthiness

mask = df['active']            # boolean dtype, pd.NA 가능
if mask.any():                  # ambiguous (NA 때문에 TypeError)
mask.fillna(False).any()        # ✓ 명시적

3. 일부 NumPy 함수 미지원

일부 numpy 함수는 pandas nullable 을 직접 처리 못 함. 변환이 필요.

np.where(df['flag'], 'A', 'B')  # boolean dtype 에서 동작 안 할 수 있음
np.where(df['flag'].fillna(False), 'A', 'B')   # 회피

4. arrow string 의 일부 regex

대부분 동작하지만 일부 특수 regex 옵션은 fallback 가능. 표준 str 메서드만 쓰는 것이 안전.

5. to_parquet / to_csv 호환성

# nullable dtype 은 parquet 로 저장할 때 pyarrow 가 알아서 처리
df.to_parquet('out.parquet')   # ✓ 정상 동작

# CSV 는 pd.NA 가 빈 문자열로 출력됨
df.to_csv('out.csv')           # <NA> -> 빈 문자열

6. groupby + nullable key

# pandas 2.x 에서 groupby key 가 nullable 이면 NA 그룹 처리
df.groupby('id_nullable', dropna=False).sum()
# dropna=False 로 NA 그룹 포함 (기본은 NA 그룹 제외)

7. concat 후 dtype 변화

df1 = pd.DataFrame({'x': pd.array([1, 2], dtype='Int64')})
df2 = pd.DataFrame({'x': [3, 4]})          # int64 (NumPy)
pd.concat([df1, df2])['x'].dtype            # float64 (강등 가능)

# 안전하게
df2['x'] = df2['x'].astype('Int64')
pd.concat([df1, df2])['x'].dtype            # Int64 유지

pandas 3.0 의 미래

pandas 3.0 부터 Arrow 가 기본 백킹이 될 예정. 지금부터 nullable dtype 에 익숙해지면 마이그레이션이 쉽다.

관련 위키

이 글의 용어 (6개)
[Pandas] DataFramepandas
정의 은 2차원 레이블 테이블. 각 열이 , 모든 열이 같은 (행 라벨) 를 공유. SQL 테이블 / Excel 시트 / R data.frame 의 Python 대응체. 구조 시…
[Pandas] dropna / fillnapandas
정의 - : NaN 이 있는 행/열 제거 - : NaN 을 특정 값으로 대체 데이터 분석의 가장 기본적인 결측치 처리. 사용 상황 - CSV 로드 후 빈 셀이 NaN 으로 읽혔을…
[Pandas] PyArrow Backendpandas
정의 pandas 2.0 부터 Apache Arrow 를 백킹으로 사용 하는 새 dtype 시스템. NumPy 기반의 한계 (메모리, 문자열, 타입 다양성) 를 크게 개선. 사용…
[Pandas] read_csv / to_csvpandas
정의 는 CSV(Comma-Separated Values) 또는 구분자 기반 텍스트 파일을 DataFrame 으로 읽는 함수. pandas 사용의 출발점. 반대 방향은 : Dat…
[Pandas] read_parquet / to_parquetpandas
정의 Parquet 은 column-oriented binary 포맷. CSV 대비 수십 배 빠르고 작다. dtype 보존, 압축, 부분 컬럼 읽기 지원. 데이터 분석의 사실상 …
[Pandas] replace / astypepandas
정의 - : 특정 값을 다른 값으로 치환 - : dtype 변환 dtype 변환 경로 replace 기본 <CodeWithOutput language="python" output…

💬 댓글

사이트 검색 / 명령어

검색

스크롤 = 확대/축소 · 드래그 = 이동 · 0 = 원래 크기 · ESC = 닫기