一,分類變量
在做數據分析統計時,常遇到這樣的類型,比如:性別、社會階層、血型、國籍、觀察時段、讚美程度等等。這類數據都是固定的可能值,取值重複並且多爲字符串。如性別中男和女,血型中A、B、O和AB。pandas中有可以存儲和處理這類數據的數據類型——categorical,categorical是pandas中對應分類變量的一種數據類型。
二,創建方式
1,astype進行類型轉換
import pandas as pd
import numpy as np
path = '../data/sz.xlsx'
sz_frame = pd.read_excel(path)
sz_frame['floor'].astype('category')
0 低樓層
1 低樓層
2 低樓層
3 低樓層
4 低樓層
...
45263 中樓層
45264 高樓層
45265 低樓層
45266 低樓層
45267 中樓層
Name: floor, Length: 45268, dtype: category
Categories (3, object): [中樓層, 低樓層, 高樓層]
2,通過 dtype="category "顯式創建
# Series
floor = pd.Series(sz_frame['floor'],dtype='category')
floor
0 低樓層
1 低樓層
2 低樓層
3 低樓層
4 低樓層
...
45263 中樓層
45264 高樓層
45265 低樓層
45266 低樓層
45267 中樓層
Name: floor, Length: 45268, dtype: category
Categories (3, object): [中樓層, 低樓層, 高樓層]
#DataFrame
floor = pd.DataFrame(sz_frame['floor'],dtype='category')
floor.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 45268 entries, 0 to 45267
Data columns (total 1 columns):
floor 45268 non-null category
dtypes: category(1)
memory usage: 44.4 KB
3,cut/qcut 隱式創建
unit_price_level = pd.cut(sz_frame['unit_price'],3,precision=2)
0 (1.18, 14.8]
1 (1.18, 14.8]
2 (1.18, 14.8]
3 (1.18, 14.8]
4 (1.18, 14.8]
...
45263 (28.38, 41.95]
45264 (28.38, 41.95]
45265 (28.38, 41.95]
45266 (28.38, 41.95]
45267 (28.38, 41.95]
Name: unit_price, Length: 45268, dtype: category
Categories (3, interval[float64]): [(1.18, 14.8] < (14.8, 28.38] < (28.38, 41.95]]
unit_price_level = pd.qcut(sz_frame['unit_price'],3,precision=2)
unit_price_level.value_counts()
(1.21, 4.56] 15090
(6.4, 41.95] 15089
(4.56, 6.4] 15089
Name: unit_price, dtype: int64
# 向cut/qcut 傳入整數個箱數,cut 通常不會使每個箱子具有相同數據量,而qcuts使用樣本的分位數,可以通過qcut獲得等數據量的箱子。
4,Categorical顯式創建
index = pd.Index(data=["Tom", "Bob", "Mary", "James", "Andy", "Alice"], name="name")
user_info = pd.Series(data=["A", "AB", np.nan, "AB", "O", "B"], index=index, name="blood_type")
# categories:自定義類別數據
pd.Categorical(user_info, categories=["A", "B", "AB"])
[A, AB, NaN, AB, NaN, B] # 對於不存在的類型,則爲NaN
Categories (3, object): [A, B, AB]
三,應用
1,內存使用與效率
Categorical類型使得DataFrame數據佔用更少的內存。
n = 10000000
labels = pd.Series(['1E76B5DCA3A19D03B0FB39BCF2A2F534',
'6945300E90C69061B463CCDA370DE5D6',
'4F4BEA1914E323156BE0B24EF8205B73',
'191115180C29B1E2AF8BE0FD0ABD138F']*(n //4))
draws = pd.DataFrame({'labels':labels,'data':np.random.randn(n)})
draws.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 10000000 entries, 0 to 9999999
Data columns (total 2 columns):
labels object
data float64
dtypes: float64(1), object(1)
memory usage: 152.6+ MB
draws['labels'] = draws['labels'].astype('category')
draws.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 10000000 entries, 0 to 9999999
Data columns (total 2 columns):
labels category
data float64
dtypes: category(1), float64(1)
memory usage: 85.8 MB
memory usage 上減少了。至於對groupby操作性能的提升,也做了測試,感覺提升也不是很多,反而轉換成category時消耗的部分性能,也可能是測試的數據量,或者分類類型不是很多,所以效果不明顯。
%timeit draws.groupby('labels').sum()
# 159 ms ± 5.6 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
draws['labels'] = draws['labels'].astype('category')
%timeit draws.groupby('labels').sum()
# 157 ms ± 4.44 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
2,屬性與方法
Categorical對象有兩個常用到的屬性categories與codes
floor = pd.Series(sz_frame['floor'],dtype='category')
floor.cat.categories #類別數組,Index(['中樓層', '低樓層', '高樓層'], dtype='object')
floor.cat.codes # 返回一個數據,每個數據對應的類別數據的下標
0 0
1 1
2 1
3 2
4 0
..
45263 1
45264 1
45265 1
45266 1
45267 1
Length: 45268, dtype: int8
其他方法,參考官方文檔 ,pandas中用挺大的篇幅介紹的,應該也是蠻實用的。
,
3,one-hot
Categorical類型數據除了在groupby中使用,還有可以用於機器學習的one-hot編碼,通常會將分類數據轉換成虛擬變量,也成one-hot編碼,這將會產生一個datafrme,每個類型對應一列,如爲該類型,則數值爲1,否則爲0。
floor = pd.Series(sz_frame['floor'],dtype='category')
pd.get_dummies(floor)
floor 中樓層 低樓層 高樓層
0 1 0 0
1 0 1 0
2 0 1 0
3 0 0 1
4 1 0 0
... ... ... ...
45263 0 1 0
45264 0 1 0
45265 0 1 0
45266 0 1 0
45267 0 1 0
45268 rows × 3 columns