본문 바로가기

Computer/Pandas

Pandas Categorical Data

반응형

범주형 데이터는 굉장히 많이 존재합니다. 보통 pandas를 이용해 csv나 엑셀파일을 읽어드리면 범주형 컬럼은 pandas에서 문자열을 나타내는 object 타입으로 잡히게 됩니다. 하지만 문자열 자체는 메모리에서 차지하는 공간이 다른 원시타입에 비해 큰 편이고 범주 개수만큼의 문자열이 반복된다면 메모리를 비효율적으로 사용하고 있는 것이겠죠. 이럴 경우에는 pandas의 categorical 타입을 이용할 수 있습니다. 먼저 다음과 같이 5개의 범주를 가진 데이터를 생성하고 sys.getsizeof() 함수를 이용하여 메모리 사용량을 알아보겠습니다.

>>> colors = pd.Series([
...     'periwinkle',
...     'mint green',
...     'burnt orange',
...     'periwinkle',
...     'burnt orange',
...     'rose',
...     'rose',
...     'mint green',
...     'rose',
...     'navy'
... ])
...
>>> import sys
>>> colors.apply(sys.getsizeof)
0    59
1    59
2    61
3    59
4    61
5    53
6    53
7    59
8    53
9    53
dtype: int64

이때 5개의 문자열을 0부터 4까지의 정수로 매핑한 이후에 사이즈를 재면 어떨까요?

>>> mapper = {v: k for k, v in enumerate(colors.unique())}
>>> mapper
{'periwinkle': 0, 'mint green': 1, 'burnt orange': 2, 'rose': 3, 'navy': 4}

>>> as_int = colors.map(mapper)
>>> as_int
0    0
1    1
2    2
3    0
4    2
5    3
6    3
7    1
8    3
9    4
dtype: int64

>>> as_int.apply(sys.getsizeof)
0    24
1    28
2    28
3    24
4    28
5    28
6    28
7    28
8    28
9    28
dtype: int64

보시다시피 메모리 용량이 object 타입에 비해 거의 절반으로 감소하였습니다. 이는 pandas의 categorical 타입의 용량은 범주 개수와 데이터 길이를 다한 값에 비례하지만 object 타입은 데이터 길이에 상수를 곱한 값에 비례하기 때문입니다.

“The memory usage of a Categorical  is proportional to the number of categories plus the length of the data. In contrast, an object dtype is a constant times the length of the data.”

Pandas에는 각 열의 메모리 사용량을 알 수 있는 memory_usage라는 함수가 존재하는데, 이를 사용하면 전체적인 메모리 사용량을 알 수 있습니다. 보다시피 astype('categorical') 을 통해 categorical 타입으로 변환한 데이터프레임이 용량을 덜 사용하는 것을 볼 수 있습니다.

>>> colors.memory_usage(index=False, deep=True)
650
>>> colors.astype('category').memory_usage(index=False, deep=True)
495

위의 경우는 메모리 용량이 크게 차이가 나지 않으나 데이터가 더 많고 범주 개수가 더 적은 경우에는 categorical 타입이 메모리적으로 훨씬 유리합니다. 타입만 변환했을 뿐인데 메모리 사용량이 확연히 감소하였습니다. 참고로 memory_usage 함수 사용시 "index=False"이면 index의 메모리 사용량을 포함하지 않고, "deep=True"로 설정해야 object 타입의 메모리를 정확히 알 수 있습니다.

>>> manycolors = colors.repeat(10)
>>> len(manycolors) / manycolors.nunique()  # Much greater than 2.0x
20.0

>>> manycolors.memory_usage(index=False, deep=True)
6500
>>> manycolors.astype('category').memory_usage(index=False, deep=True)
585
반응형

'Computer > Pandas' 카테고리의 다른 글

Pandas groupby (2)  (0) 2021.08.07
Pandas groupby (1)  (0) 2021.08.07
Onehot 인코딩의 역변환 (inverse transform)  (0) 2021.07.25
Pandas Multiple Columns Label Encoding  (1) 2021.07.14
Pandas DataFrame 합치기 - merge, concat  (0) 2021.03.31