[parquet/downcast] 용량이 큰 데이터 불러오기

최하린·2023년 3월 8일
0

캐글의 다음 데이터를 불러오려고 하는데, 데이터의 용량이 너무 커서 read_csv로 불러와지지 않았다.

https://www.kaggle.com/datasets/mkechinov/ecommerce-behavior-data-from-multi-category-store

이럴 때 parquet를 이용하면 된다!

01. parquet란?

  • 하둡에서 칼럼방식으로 저장하는 저장 포맷
  • 효율적인 데이터 저장 및 검색을 위해 설계된 오픈 소스, 열 지향 데이터 파일 형식
  • 열을 기반으로 데이터를 처리하면 행 기반으로 압축했을때에 비해 데이터의 압축률이 더 높고, 필요한 열의 데이터만 읽어서 처리하는 것이 가능하기 때문에 데이터 처리에 들어가는 지원을 절약할 수 있다.

02. 설치 방법

anaconda prompt를 관리자 권한으로 실행하여 다음 코드를 실행해주면 된다.

conda install -c conda-forge fastparquet
conda install -c conda-forge pyarrow

03. 코드

1) 파일 불러오기

위 캐글에서 csv 데이터를 다운받아 전체를 불러오려고 하면 엄청나게 오랜 시간이 걸릴 것이다. 이럴 때 pandas의 read_csv의 skiprows와 nrows를 활용하면 된다. 나는 42,448,764개의 행 중 35,373,970 ~ 42,448,763번째의 행, 총 7,074,794개의 행을 불러오려고 한다.


read_csv의 파라미터 살펴보기

1. header

  • header의 default는 첫 행을 컬럼으로 지정
  • header=0 하면 되나 싶지만 중간부터 불러오는 경우는 해당 안 됨
  • 예를 들어 31행부터 읽어들인다 했을 때, header=0이라고 하면 31번째 행을 컬럼으로 지정함
  • header=None으로 한 후 names에서 컬럼을 지정해주어야 함

2. skiprows

  • 몇 번째 행까지 스킵할 것인지 설정
  • 5번째 행까지 스킵하고 6번째 행부터 읽어들이고 싶다면 skiprows=5라고 설정하면 됨

3. nrows

  • 불러올 행의 개수

4. names

컬럼으로 사용할 이름을 리스트로 입력


df = pd.read_csv('../data/2019-Oct.csv', skiprows=35373971, header=None)
df.shape

결과:
(7074794, 9)
df.info()

결과:
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 7074794 entries, 0 to 7074793
Data columns (total 9 columns):
 #   Column  Dtype  
---  ------  -----  
 0   0       object 
 1   1       object 
 2   2       int64  
 3   3       int64  
 4   4       object 
 5   5       object 
 6   6       float64
 7   7       int64  
 8   8       object 
dtypes: float64(1), int64(3), object(5)
memory usage: 485.8+ MB

2) 컬럼명 지정

col_name = pd.read_csv('../data/2019-Oct.csv', nrows=2).columns.to_list()
col_name

결과:
['event_time',
 'event_type',
 'product_id',
 'category_id',
 'category_code',
 'brand',
 'price',
 'user_id',
 'user_session']
df.columns = col_name
df.head()

3) 데이터 타입 변경 및 컬럼 삭제

메모리 부담을 줄이기 위해 데이터 타입을 변경해주고, 분석에 쓰지 않을 user_session 컬럼을 삭제해주었다.

# user_session 컬럼 삭제
df = df.drop(['user_session'], axis=1)

# event_time 컬럼 -> date 타입으로 변경
df['event_time'] = pd.to_datetime(df['event_time'])

# int 타입으로 시작하는 컬럼은 unsigned 타입으로 변경
# float 타입으로 시작하는 컬럼은 float 타입으로 변경
# object 타입으로 시작하는 컬럼은 category 타입으로 변경
for col in df.columns:
    type_name = df[col].dtypes.name
    if type_name.startswith('int'):
        df[col] = pd.to_numeric(df[col], downcast='unsigned')
    elif type_name.startswith('float'):
        df[col] = pd.to_numeric(df[col], downcast='float')
    elif type_name.startswith('object'):
        df[col] = df[col].astype('category')

이렇게 변경한 후, 데이터프레임의 요약 정보를 보면 다음과 같다.

df.info()

결과:
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 7074794 entries, 0 to 7074793
Data columns (total 8 columns):
 #   Column         Dtype              
---  ------         -----              
 0   event_time     datetime64[ns, UTC]
 1   event_type     category           
 2   product_id     uint32             
 3   category_id    uint64             
 4   category_code  category           
 5   brand          category           
 6   price          float32            
 7   user_id        uint32             
dtypes: category(3), datetime64[ns, UTC](1), float32(1), uint32(2), uint64(1)
memory usage: 216.1 MB

처음에 불러왔을 때 485.8+ MB였던 데이터가 216.1 MB로 줄어든 것을 확인할 수 있다.

4) parquet로 저장

이제 데이터 용량을 줄이기 위해 csv가 아닌 parquet로 저장해볼 것이다.

file_path_parquet = 'df.parquet.gzip'
df.to_parquet(file_path_parquet, compression='gzip')

저장된 데이터프레임을 확인해보면 68.1 MB로 비록 전체 행의 1/6만 불러왔지만 5.67기가 였던 원래 데이터 용량보다 확연하게 줄어든 것을 확인할 수 있다. 나머지 행들도 이런 과정을 거친 후 parquet로 저장하고, 나중에 6개의 파일을 한번에 합치면 되지 않을까 싶다.

[원본데이터]

[parquet로 저장한 데이터]

0개의 댓글