Pandas
Pandas는 Python에서 널리 사용되는 데이터 분석 라이브러리로, 다양한 형식의 데이터를 쉽게 불러오고 처리할 수 있다. 그중 가장 자주 사용하는 함수 중 하나는 read_csv로 CSV 파일을 읽을 때 사용된다.
하지만 이 함수는 데이터를 즉시 메모리에 로딩하므로, 물리적 메모리보다 큰 파일을 읽는 데는 한계가 있다. 예를 들어, 64GB의 메모리를 가진 로컬 PC에서 500GB 크기의 CSV 파일을 처리하는 경우, 단순하게 read_csv로 불러오는 것은 불가능하다.
예전에는 500GB를 한 땀 한 땀 자르기도 했는데 더 스마트한 방법이 있다.
nrows, chunksize
Pandas는 이러한 문제를 해결하기 위해 일부 데이터를 로딩하거나, 데이터를 나누어 처리할 수 있는 옵션을 제공한다. nrows 옵션을 사용하면 데이터의 앞부분 일부만 로드할 수 있으며, chunksize는 데이터를 일정 크기로 나누어 처리할 수 있다.
df = pd.read_csv('sample_data/california_housing_train.csv', nrows=1000)
또는 skiprows 옵션을 사용해 특정 범위의 행을 건너뛸 수도 있다.
df = pd.read_csv('sample_data/california_housing_train.csv', skiprows=[i for i in range(1, 1001)])
chunksize를 사용하면 DataFrame 대신, 반복 가능한 chunk 객체가 생성된다.
df = pd.read_csv('sample_data/california_housing_train.csv', chunksize=1000)
dataframe이 아니기때문에 info()를 하면 에러가 발생한다.
대신 반복문을 통해 처리할 수 있다.
sum_long = 0
for i, chunk in enumerate(df):
print(f'{i}번째 실행')
sum_long = chunk['Longitude'].sum()
print(sum_long)
이 방법은 비록 속도는 느리지만 메모리 효율성을 높일 수 있다.
Pandas의 한계
Pandas의 단점은 싱글 코어로만 동작하며, 물리적 메모리 내에서만 처리할 수 있다는 것이다. 따라서 대용량 데이터를 처리하기에는 적합하지 않다. 이 한계를 극복하기 위한 방법으로 Dask가 제안되었다.
Dask DataFrame
Dask는 Pandas의 대안으로, 멀티 코어를 활용하고, 필요한 데이터만 메모리에 로드하는 방식으로 동작한다. 즉, 데이터 전체를 메모리에 즉시 로드하지 않고, 처리할 때만 데이터를 메모리에 가져온다.
Pandas와 Dask의 성능 비교
Dask는 성능 면에서도 큰 차이를 보여준다.
Pandas로 데이터를 읽는 데 1.26초가 걸렸다면, Dask를 사용하면 67ms밖에 걸리지 않는다.
그런데 dask dataframe을 찍어보면 없다고 뜬다. 이건 아직 메모리에 올리지 않았다는 뜻이다.
dask dataframe은 메모리에 즉시 올리지 않고, 처리할 일이 있을 때 그때 메모리에 올린다.
pandas 프레임을 뿌려보면 이미 다 로딩 되었으니까 5ms로 얼마 안걸린다. dask df를 뿌려보면 시간은 똑같이 4ms 걸렸지만 아무것도 뿌려지는 게 없다. 이건 현재 필요해서 사용하는 단계까지 오지 않았기 때문이다.
이처럼 Dask는 데이터를 메모리에 올리지 않고, 메타 정보만을 유지하여 필요할 때만 데이터를 처리하는 방식으로 메모리 효율성을 극대화한다.
dask dataframe 실습
우선 Dask를 설치한 후, Pandas와 Dask를 비교해보자.
pip install dask
Pandas로 1.2GB 크기의 데이터를 불러오면 메모리에 모든 데이터를 즉시 로드한다.
%%time
df = pd.read_csv('test/MyDrive/data/crime.csv')
df.info()
이렇게 보면 54초 걸린 걸 확인할 수 있다.
이제 chunk를 이용해서 sum 해주는 코드를 작성해 보자.
%%time
df = pd.read_csv('test/MyDrive/data/crime.csv',chunksize=1000)
%%time
sum_ward = 0
for i, chunk in enumerate(df):
if i % 10000 == 0:
print(f"{i+1}th execution")
sum_ward += chunk['Ward'].sum()
print(sum_ward)
sum을 해주면 1분이 걸린다.
Dask로 대용량 데이터 처리
위 df랑 헷갈리지 말라고 ddf로 정의해 줬다.
from dask import dataframe as ddf
지금 데이터는 값이 혼재되어 있기 때문에 그냥 읽으면 에러가 난다. 그래서 dtype으로 Community Area, IUCR, Ward를 설정해 줬다.
%%time
ddf = ddf.read_csv('test/MyDrive/data/crime.csv', dtype={'Community Area': 'float64',
'IUCR': 'object',
'Ward': 'float64'})
아까 pandas는 54초 걸린 게 지금은 178ms 걸린다.
데이터를 보려고 ddf 찍어보면 아무것도 안 나온다.
여기서 확인할 수 있는 건 '이런 컬럼들이 있고 데이터가 많아서 파티션을 28개로 나눠놨다.' 라는 정보로 이 ddf에 대한 메타 정보를 생성해줬다.
ddf.info()
info를 찍어주면 df랑 거의 똑같은 형태로 볼 수 있다.
이제 실제 데이터를 확인해보자.
ddf.head(10)
이때 실제 데이터를 불러온다.
이제 위에서 동일한 작업이 얼마나 차이나는지 확인해보자.
%%time
ddf_sum = ddf['Ward'].sum().compute()
ddf를 사용하면 체감상 엄청 빨라져야 할거같은데 사실 반밖에 줄지 않은 32초가 나왔다. 생각보다 크게 줄지 않았지만 그래도 큰 가치가 있다고 생각한다.
🤔 왜 pandas에서는 그냥 .sum()만 했는데 여기서는 뒤에 .compute()를 붙일까?
😮 ddf에서 .sum()과 같은 함수는 연산 준비만 하고 compute()를 호출해서 실제로 연산을 수행해야 한다.
결론
Pandas는 작은 데이터셋을 처리할 때는 유용하지만, 대용량 데이터를 처리하는 데는 한계가 있다. Dask는 이러한 한계를 보완해 멀티 코어와 지연 로딩 기법을 사용하여 대용량 데이터를 효과적으로 처리할 수 있다. 따라서 메모리보다 큰 데이터를 처리해야 할 때는 Dask와 같은 도구를 사용하는 것이 효율적이다.
'프로그래밍 언어 > Python' 카테고리의 다른 글
[Pytorch] 맥북 M1 칩에서 GPU 사용하기(CUDA 대신 MPS 사용) (2) | 2024.10.18 |
---|---|
[트러블 슈팅] conda activate 했는데 which python이/usr/bin/python3 로 나오는 이슈 (0) | 2024.08.07 |
[Seaborn] 선형 회귀선, 히스토그램, 커널 밀도 그래프, 산점도 그래프, 빈도 그래프, 조인트 그래프, 관계 그래프 (0) | 2024.08.05 |
[Seaborn] 기본 개념 - 데이터 불러오기 (0) | 2024.08.05 |
[Matplotlib] 영역을 지정하여 여러 개 그래프 적용 (0) | 2024.08.05 |