第8章 分類數據
import pandas as pd
import numpy as np
df = pd. read_csv( 'data/table.csv' )
df. head( )
School
Class
ID
Gender
Address
Height
Weight
Math
Physics
0
S_1
C_1
1101
M
street_1
173
63
34.0
A+
1
S_1
C_1
1102
F
street_2
192
73
32.5
B+
2
S_1
C_1
1103
M
street_2
186
82
87.2
B+
3
S_1
C_1
1104
F
street_2
167
81
80.4
B-
4
S_1
C_1
1105
F
street_4
159
64
84.8
B+
一、category的創建及其性質
1. 分類變量的創建
(a)用Series創建
pd. Series( [ "a" , "b" , "c" , "a" ] , dtype= "category" )
0 a
1 b
2 c
3 a
dtype: category
Categories (3, object): [a, b, c]
(b)對DataFrame指定類型創建
temp_df = pd. DataFrame( { 'A' : pd. Series( [ "a" , "b" , "c" , "a" ] , dtype= "category" ) , 'B' : list ( 'abcd' ) } )
temp_df. dtypes
A category
B object
dtype: object
(c)利用內置Categorical類型創建
cat = pd. Categorical( [ "a" , "b" , "c" , "a" ] , categories= [ 'a' , 'b' , 'c' ] )
pd. Series( cat)
0 a
1 b
2 c
3 a
dtype: category
Categories (3, object): [a, b, c]
(d)利用cut函數創建
默認使用區間類型爲標籤
pd. cut( np. random. randint( 0 , 60 , 5 ) , [ 0 , 10 , 30 , 60 ] )
[(10, 30], (0, 10], (10, 30], (30, 60], (30, 60]]
Categories (3, interval[int64]): [(0, 10] < (10, 30] < (30, 60]]
可指定字符爲標籤
pd. cut( np. random. randint( 0 , 60 , 5 ) , [ 0 , 10 , 30 , 60 ] , right= False , labels= [ '0-10' , '10-30' , '30-60' ] )
[10-30, 30-60, 30-60, 10-30, 30-60]
Categories (3, object): [0-10 < 10-30 < 30-60]
2. 分類變量的結構
一個分類變量包括三個部分,元素值(values)、分類類別(categories)、是否有序(order)
從上面可以看出,使用cut函數創建的分類變量默認爲有序分類變量
下面介紹如何獲取或修改這些屬性
(a)describe方法
該方法描述了一個分類序列的情況,包括非缺失值個數、元素值類別數(不是分類類別數)、最多次出現的元素及其頻數
s = pd. Series( pd. Categorical( [ "a" , "b" , "c" , "a" , np. nan] , categories= [ 'a' , 'b' , 'c' , 'd' ] ) )
s. describe( )
count 4
unique 3
top a
freq 2
dtype: object
(b)categories和ordered屬性
查看分類類別和是否排序
s. cat. categories
Index(['a', 'b', 'c', 'd'], dtype='object')
s. cat. ordered
False
3. 類別的修改
(a)利用set_categories修改
修改分類,但本身值不會變化
s = pd. Series( pd. Categorical( [ "a" , "b" , "c" , "a" , np. nan] , categories= [ 'a' , 'b' , 'c' , 'd' ] ) )
s. cat. set_categories( [ 'new_a' , 'c' ] )
0 NaN
1 NaN
2 c
3 NaN
4 NaN
dtype: category
Categories (2, object): [new_a, c]
(b)利用rename_categories修改
需要注意的是該方法會把值和分類同時修改
s = pd. Series( pd. Categorical( [ "a" , "b" , "c" , "a" , np. nan] , categories= [ 'a' , 'b' , 'c' , 'd' ] ) )
s. cat. rename_categories( [ 'new_%s' % i for i in s. cat. categories] )
0 new_a
1 new_b
2 new_c
3 new_a
4 NaN
dtype: category
Categories (4, object): [new_a, new_b, new_c, new_d]
利用字典修改值
s. cat. rename_categories( { 'a' : 'new_a' , 'b' : 'new_b' } )
0 new_a
1 new_b
2 c
3 new_a
4 NaN
dtype: category
Categories (4, object): [new_a, new_b, c, d]
(c)利用add_categories添加
s = pd. Series( pd. Categorical( [ "a" , "b" , "c" , "a" , np. nan] , categories= [ 'a' , 'b' , 'c' , 'd' ] ) )
s. cat. add_categories( [ 'e' ] )
0 a
1 b
2 c
3 a
4 NaN
dtype: category
Categories (5, object): [a, b, c, d, e]
(d)利用remove_categories移除
s = pd. Series( pd. Categorical( [ "a" , "b" , "c" , "a" , np. nan] , categories= [ 'a' , 'b' , 'c' , 'd' ] ) )
s. cat. remove_categories( [ 'd' ] )
0 a
1 b
2 c
3 a
4 NaN
dtype: category
Categories (3, object): [a, b, c]
(e)刪除元素值未出現的分類類型
s = pd. Series( pd. Categorical( [ "a" , "b" , "c" , "a" , np. nan] , categories= [ 'a' , 'b' , 'c' , 'd' ] ) )
s. cat. remove_unused_categories( )
0 a
1 b
2 c
3 a
4 NaN
dtype: category
Categories (3, object): [a, b, c]
二、分類變量的排序
前面提到,分類數據類型被分爲有序和無序,這非常好理解,例如分數區間的高低是有序變量,考試科目的類別一般看做無序變量
1. 序的建立
(a)一般來說會將一個序列轉爲有序變量,可以利用as_ordered方法
s = pd. Series( [ "a" , "d" , "c" , "a" ] ) . astype( 'category' ) . cat. as_ordered( )
s
0 a
1 d
2 c
3 a
dtype: category
Categories (3, object): [a < c < d]
退化爲無序變量,只需要使用as_unordered
s. cat. as_unordered( )
0 a
1 d
2 c
3 a
dtype: category
Categories (3, object): [a, c, d]
(b)利用set_categories方法中的order參數
pd. Series( [ "a" , "d" , "c" , "a" ] ) . astype( 'category' ) . cat. set_categories( [ 'a' , 'c' , 'd' ] , ordered= True )
0 a
1 d
2 c
3 a
dtype: category
Categories (3, object): [a < c < d]
(c)利用reorder_categories方法
這個方法的特點在於,新設置的分類必須與原分類爲同一集合
s = pd. Series( [ "a" , "d" , "c" , "a" ] ) . astype( 'category' )
s. cat. reorder_categories( [ 'a' , 'c' , 'd' ] , ordered= True )
0 a
1 d
2 c
3 a
dtype: category
Categories (3, object): [a < c < d]
2. 排序
先前在第1章介紹的值排序和索引排序都是適用的
s = pd. Series( np. random. choice( [ 'perfect' , 'good' , 'fair' , 'bad' , 'awful' ] , 50 ) ) . astype( 'category' )
s. cat. set_categories( [ 'perfect' , 'good' , 'fair' , 'bad' , 'awful' ] [ : : - 1 ] , ordered= True ) . head( )
0 good
1 fair
2 bad
3 perfect
4 perfect
dtype: category
Categories (5, object): [awful < bad < fair < good < perfect]
s. sort_values( ascending= False ) . head( )
29 perfect
17 perfect
31 perfect
3 perfect
4 perfect
dtype: category
Categories (5, object): [awful, bad, fair, good, perfect]
df_sort = pd. DataFrame( { 'cat' : s. values, 'value' : np. random. randn( 50 ) } ) . set_index( 'cat' )
df_sort. head( )
value
cat
good
-1.746975
fair
0.836732
bad
0.094912
perfect
-0.724338
perfect
-1.456362
df_sort. sort_index( ) . head( )
value
cat
awful
0.245782
awful
0.063991
awful
1.541862
awful
-0.062976
awful
0.472542
三、分類變量的比較操作
1. 與標量或等長序列的比較
(a)標量比較
s = pd. Series( [ "a" , "d" , "c" , "a" ] ) . astype( 'category' )
s == 'a'
0 True
1 False
2 False
3 True
dtype: bool
(b)等長序列比較
s == list ( 'abcd' )
0 True
1 False
2 True
3 False
dtype: bool
2. 與另一分類變量的比較
(a)等式判別(包含等號和不等號)
兩個分類變量的等式判別需要滿足分類完全相同
s = pd. Series( [ "a" , "d" , "c" , "a" ] ) . astype( 'category' )
s == s
0 True
1 True
2 True
3 True
dtype: bool
s != s
0 False
1 False
2 False
3 False
dtype: bool
s_new = s. cat. set_categories( [ 'a' , 'd' , 'e' ] )
(b)不等式判別(包含>=,<=,<,>)
兩個分類變量的不等式判別需要滿足兩個條件:① 分類完全相同 ② 排序完全相同
s = pd. Series( [ "a" , "d" , "c" , "a" ] ) . astype( 'category' )
s = pd. Series( [ "a" , "d" , "c" , "a" ] ) . astype( 'category' ) . cat. reorder_categories( [ 'a' , 'c' , 'd' ] , ordered= True )
s >= s
0 True
1 True
2 True
3 True
dtype: bool
四、問題與練習
【問題一】 如何使用union_categoricals方法?它的作用是什麼?
【問題二】 利用concat方法將兩個序列縱向拼接,它的結果一定是分類變量嗎?什麼情況下不是?
【問題三】 當使用groupby方法或者value_counts方法時,分類變量的統計結果和普通變量有什麼區別?
【問題四】 下面的代碼說明了Series創建分類變量的什麼“缺陷”?如何避免?(提示:使用Series中的copy參數)
cat = pd. Categorical( [ 1 , 2 , 3 , 10 ] , categories= [ 1 , 2 , 3 , 4 , 10 ] )
s = pd. Series( cat, name= "cat" )
cat
[1, 2, 3, 10]
Categories (5, int64): [1, 2, 3, 4, 10]
s. iloc[ 0 : 2 ] = 10
cat
[10, 10, 3, 10]
Categories (5, int64): [1, 2, 3, 4, 10]
【練習一】 現繼續使用第四章中的地震數據集,請解決以下問題:
(a)現在將深度分爲七個等級:[0,5,10,15,20,30,50,np.inf],請以深度等級Ⅰ,Ⅱ,Ⅲ,Ⅳ,Ⅴ,Ⅵ,Ⅶ爲索引並按照由淺到深的順序進行排序。
(b)在(a)的基礎上,將烈度分爲4個等級:[0,3,4,5,np.inf],依次對南部地區的深度和烈度等級建立多級索引排序。
pd. read_csv( 'data/Earthquake.csv' ) . head( )
日期
時間
維度
經度
方向
距離
深度
烈度
0
2003.05.20
12:17:44 AM
39.04
40.38
west
0.1
10.0
0.0
1
2007.08.01
12:03:08 AM
40.79
30.09
west
0.1
5.2
4.0
2
1978.05.07
12:41:37 AM
38.58
27.61
south_west
0.1
0.0
0.0
3
1997.03.22
12:31:45 AM
39.47
36.44
south_west
0.1
10.0
0.0
4
2000.04.02
12:57:38 AM
40.80
30.24
south_west
0.1
7.0
0.0
【練習二】 對於分類變量而言,調用第4章中的變形函數會出現一個BUG(目前的版本下還未修復):例如對於crosstab函數,按照官方文檔的說法 ,即使沒有出現的變量也會在變形後的彙總結果中出現,但事實上並不是這樣,比如下面的例子就缺少了原本應該出現的行’c’和列’f’。基於這一問題,請嘗試設計my_crosstab函數,在功能上能夠返回正確的結果。
foo = pd. Categorical( [ 'a' , 'b' ] , categories= [ 'a' , 'b' , 'c' ] )
bar = pd. Categorical( [ 'd' , 'e' ] , categories= [ 'd' , 'e' , 'f' ] )
pd. crosstab( foo, bar)
col_0
d
e
row_0
a
1
0
b
0
1