[Pandas 강좌 – 9] Pandas(판다스) 성능 향상을 이용한 대용량 데이터 처리 방법

1. 데이터 타입 최적화

 

Pandas에서는 각 데이터 유형에 따라 메모리를 다르게 사용합니다. 따라서 데이터 타입을 최적화하면 메모리 사용량을 줄일 수 있습니다. 예를 들어, 'category' 데이터 타입은 범주형 데이터(즉, 값의 범위가 제한적인 데이터)를 표현할 때 효율적인 메모리 사용이 가능합니다.

 

범주형 데이터는 특정한 범위 또는 범주 내에서만 값이 변하는 데이터를 의미합니다. 예를 들어, '성별' 열에는 '남성', '여성' 두 가지 값만 있을 수 있으며, '계절' 열에는 '봄', '여름', '가을', '겨울' 네 가지 값만 있을 수 있습니다. 이처럼 고유한 값의 종류가 제한적인 열을 'category' 데이터 타입으로 표현하면 메모리를 효율적으로 사용할 수 있습니다.

 

 

import pandas as pd

# 간단한 데이터프레임을 생성합니다.
df = pd.DataFrame({
    'A': ['dog', 'cat', 'dog', 'cat', 'dog', 'cat', 'dog', 'dog'],
    'B': ['one', 'one', 'two', 'three', 'two', 'two', 'one', 'three']
})

# 'A' 열의 데이터 타입을 'category'로 변경합니다.
df['A'] = df['A'].astype('category')

print(df.dtypes)

 

 

이와 같은 방법 외에도 정수 또는 부동소수점 수를 저장하는 열의 경우, 필요한 범위에 맞는 가장 작은 데이터 타입을 선택함으로써 메모리를 절약할 수 있습니다. 예를 들어, -128에서 127 사이의 정수값만을 가진 열의 경우 'int8' 데이터 타입을 사용하면 됩니다. 이는 'int16', 'int32', 'int64'와 같은 더 큰 데이터 타입을 사용하는 것에 비해 메모리 사용량을 줄일 수 있습니다.

 

 

import numpy as np

# 'int64' 데이터 타입을 가진 열을 생성합니다.
df = pd.DataFrame(np.random.randint(0, 100, size=(1000, 1)), columns=list('A'))

# 'int8' 데이터 타입으로 변경합니다.
df['A'] = df['A'].astype(np.int8)

print(df.dtypes)

 


2. 벡터화된 연산 사용

 

Pandas는 벡터화된 연산을 지원하며 이를 사용하면 계산의 성능을 크게 향상시킬 수 있습니다. 벡터화된 연산이란 배열 또는 시리즈 등의 모든 요소에 대한 연산을 한 번에 처리하는 것을 말합니다. 이는 일반적인 for-loop를 통한 반복 연산에 비해 훨씬 빠르고 효율적입니다. 이는 Pandas가 내부적으로 NumPy를 사용하기 때문입니다. NumPy는 고성능의 다차원 배열 객체와 이를 다루기 위한 도구들을 제공하며, 이를 통해 벡터화된 연산을 수행할 수 있습니다.

 

# 좋지 않은 예: 반복문을 사용한 연산
for i in range(len(df)):
    df.loc[i, 'age'] = df.loc[i, 'age'] + 1

# 좋은 예: 벡터화된 연산 사용
df['age'] = df['age'] + 1

 


3. 필요한 데이터만 불러오기

 

큰 데이터셋을 다룰 때, 모든 데이터를 한 번에 메모리에 로드하는 것은 비효율적이며, 때로는 불가능할 수도 있습니다. 이런 경우에는 필요한 열만 선택하여 불러오는 것이 좋습니다. 이렇게 하면 사용하는 메모리 양을 크게 줄일 수 있습니다. Pandas의 read_csv 함수를 사용할 때 usecols 매개변수를 활용하여 필요한 열만 불러올 수 있습니다. usecols는 불러올 열의 이름이나 인덱스를 리스트로 전달하면 됩니다.

 

 

# 'age'와 'fare' 열만 불러오기
cols = ['age', 'fare']
df = pd.read_csv('titanic.csv', usecols=cols)

 


4. 대용량 데이터 처리를 위한 chunksize 사용

 

 

Pandas의 read_csv() 함수에는 chunksize 매개변수가 있어서 이를 활용하면 대용량 데이터를 효율적으로 처리할 수 있습니다. chunksize를 설정하면 read_csv() 함수는 'TextFileReader' 객체를 반환합니다. 이 객체는 이터레이터로 사용할 수 있으며, 각 이터레이션은 데이터프레임의 청크(조각)를 반환합니다. 코드에서는 각 청크를 별도로 처리한 후, 마지막에 pd.concat() 함수를 사용하여 모든 청크를 하나의 데이터프레임으로 결합하고 있습니다. 이 방법을 사용하면 메모리에 한 번에 로드되는 데이터의 양을 제한하면서도 대용량 데이터를 효과적으로 처리할 수 있습니다.

 

 

chunksize = 50000  # 한 번에 불러올 행의 수를 설정합니다.

chunks = []  # 각 청크를 저장할 리스트를 생성합니다.

for chunk in pd.read_csv('large_dataset.csv', chunksize=chunksize):
    # 이 곳에서 각 청크를 처리할 수 있습니다. 예를 들어, 각 청크에 대해 데이터 전처리를 수행하거나,
    # 특정 조건을 만족하는 행만 선택할 수 있습니다.
    
    chunks.append(chunk)

# 모든 청크를 하나의 데이터프레임으로 결합합니다.
df = pd.concat(chunks, axis=0)