【Python基礎】學習用Pandas處理分類資料!

語言: CN / TW / HK

```php

Datawhale乾貨  作者:耿遠昊,Datawhale成員,華東師範大學 ```

分類資料(categorical data)是按照現象的某種屬性對其進行分類或分組而得到的反映事物型別的資料,又稱定類資料。直白來說,就是取值為有限的,或者說是固定數量的可能值。例如:性別、血型等。

今天,我們來學習下,Pandas如何處理分類資料。主要圍繞以下幾個方面展開:\

本文目錄\

1. Category的建立及其性質

1.1. 分類變數的建立

1.2. 分類變數的結構

1.3. 類別的修改

2. 分類變數的排序

2.1. 序的建立

2.2. 排序

3. 分類變數的比較操作

3.1. 與標量或等長序列的比較

3.2. 與另一分類變數的比較

4. 問題及練習

4.1. 問題

4.2. 練習

首先,讀入資料:\

javascript import pandas as pd import numpy as np df = pd.read_csv('data/table.csv') df.head()

一、category的建立及其性質

1.1. 分類變數的建立

(a)用Series建立

javascript pd.Series(["a", "b", "c", "a"], dtype="category")

(b)對DataFrame指定型別建立

ruby temp_df = pd.DataFrame({'A':pd.Series(["a", "b", "c", "a"], dtype="category"),'B':list('abcd')}) temp_df.dtypes

(c)利用內建Categorical型別建立

nginx cat = pd.Categorical(["a", "b", "c", "a"], categories=['a','b','c']) pd.Series(cat)

(d)利用cut函式建立,預設使用區間型別為標籤

css pd.cut(np.random.randint(0,60,5), [0,10,30,60])

可指定字元為標籤

php pd.cut(np.random.randint(0,60,5), [0,10,30,60], right=False, labels=['0-10','10-30','30-60'])

1.2. 分類變數的結構

一個分類變數包括三個部分,元素值(values)、分類類別(categories)、是否有序(order)。從上面可以看出,使用cut函式建立的分類變數預設為有序分類變數。下面介紹如何獲取或修改這些屬性。

(a)describe方法

該方法描述了一個分類序列的情況,包括非缺失值個數、元素值類別數(不是分類類別數)、最多次出現的元素及其頻數。

nginx s = pd.Series(pd.Categorical(["a", "b", "c", "a",np.nan], categories=['a','b','c','d'])) s.describe()

(b)categories和ordered屬性,檢視分類類別和是否排序

css s.cat.categories

Index(['a', 'b', 'c', 'd'], dtype='object')

css s.cat.ordered

php False

1.3. 類別的修改

(a)利用set_categories修改,修改分類,但本身值不會變化

nginx s = pd.Series(pd.Categorical(["a", "b", "c", "a",np.nan], categories=['a','b','c','d'])) s.cat.set_categories(['new_a','c'])

(b)利用rename_categories修改,需要注意的是該方法會把值和分類同時修改

javascript 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])

利用字典修改值

javascript s.cat.rename_categories({'a':'new_a','b':'new_b'})

(c)利用add_categories新增

nginx s = pd.Series(pd.Categorical(["a", "b", "c", "a",np.nan], categories=['a','b','c','d'])) s.cat.add_categories(['e'])

(d)利用remove_categories移除

nginx s = pd.Series(pd.Categorical(["a", "b", "c", "a",np.nan], categories=['a','b','c','d'])) s.cat.remove_categories(['d'])

(e)刪除元素值未出現的分類型別

nginx s = pd.Series(pd.Categorical(["a", "b", "c", "a",np.nan], categories=['a','b','c','d'])) s.cat.remove_unused_categories()

二、分類變數的排序

前面提到,分類資料型別被分為有序和無序,這非常好理解,例如分數區間的高低是有序變數,考試科目的類別一般看做無序變數

2.1. 序的建立

(a)一般來說會將一個序列轉為有序變數,可以利用as_ordered方法

makefile s = pd.Series(["a", "d", "c", "a"]).astype('category').cat.as_ordered() s

php 退化為無序變數,只需要使用as_unordered

css s.cat.as_unordered()

(b)利用set_categories方法中的order引數

php pd.Series(["a", "d", "c", "a"]).astype('category').cat.set_categories(['a','c','d'],ordered=True)

(c)利用reorder_categories方法,這個方法的特點在於,新設定的分類必須與原分類為同一集合

nginx s = pd.Series(["a", "d", "c", "a"]).astype('category') s.cat.reorder_categories(['a','c','d'],ordered=True)

```shell

s.cat.reorder_categories(['a','c'],ordered=True) #報錯

s.cat.reorder_categories(['a','c','d','e'],ordered=True) #報錯

```

2.2. 排序

先前在第1章介紹的值排序和索引排序都是適用的

nginx 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()

cs s.sort_values(ascending=False).head()

ruby df_sort = pd.DataFrame({'cat':s.values,'value':np.random.randn(50)}).set_index('cat') df_sort.head()

css df_sort.sort_index().head()

三、分類變數的比較操作

3.1. 與標量或等長序列的比較

(a)標量比較

ini s = pd.Series(["a", "d", "c", "a"]).astype('category') s == 'a'

(b)等長序列比較

ini s == list('abcd')

3.2. 與另一分類變數的比較

(a)等式判別(包含等號和不等號),兩個分類變數的等式判別需要滿足分類完全相同。

ini s = pd.Series(["a", "d", "c", "a"]).astype('category') s == s

nginx s != s

```ini s_new = s.cat.set_categories(['a','d','e'])

s == s_new #報錯

```

(b)不等式判別(包含>=,<=,<,>),兩個分類變數的不等式判別需要滿足兩個條件:① 分類完全相同 ② 排序完全相同

```ini s = pd.Series(["a", "d", "c", "a"]).astype('category')

s >= s #報錯

```

nginx s = pd.Series(["a", "d", "c", "a"]).astype('category').cat.reorder_categories(['a','c','d'],ordered=True) s >= s

四、問題與練習

4.1. 問題

【問題一】 如何使用union_categoricals方法?它的作用是什麼?

  • 如果要組合不一定具有相同類別的類別,union_categoricals函式將組合類似列表的類別。新類別將是合併的類別的並集。如下所示:

javascript from pandas.api.types import union_categoricals a = pd.Categorical(['b','c']) b = pd.Categorical(['a','b']) union_categoricals([a,b])

  • 預設情況下,生成的類別將按照在資料中顯示的順序排列。如果要對類別進行排序,可使用sort_categories=True引數。
  • union_categoricals也適用於組合相同類別和順序資訊的兩個分類。
  • union_categoricals可以在合併分類時重新編碼類別的整數程式碼。

【問題二】 利用concat方法將兩個序列縱向拼接,它的結果一定是分類變數嗎?什麼情況下不是?

【問題三】 當使用groupby方法或者value_counts方法時,分類變數的統計結果和普通變數有什麼區別?\

  • 分類變數的groupby方法/value_counts方法,統計物件是類別。
  • 普通變數groupby方法/value_counts方法,統計物件是唯一值(不包含NA)。

【問題四】 下面的程式碼說明了Series建立分類變數的什麼“缺陷”?如何避免?(提示:使用Series中的copy引數)

makefile cat = pd.Categorical([1, 2, 3, 10], categories=[1, 2, 3, 4, 10]) s = pd.Series(cat, name="cat") cat

makefile s.iloc[0:2] = 10 cat

php 4.2. 練習

【練習一】 現繼續使用第四章中的地震資料集,請解決以下問題:

(a)現在將深度分為七個等級:[0,5,10,15,20,30,50,np.inf],請以深度等級Ⅰ,Ⅱ,Ⅲ,Ⅳ,Ⅴ,Ⅵ,Ⅶ為索引並按照由淺到深的順序進行排序。

  • 使用cut方法對列表中的深度劃分,並將該列作為索引值。然後按索引排序即可。

nginx df = pd.read_csv('data/Earthquake.csv') df_result = df.copy() df_result['深度'] = pd.cut(df['深度'],[0,5,10,15,20,30,50,np.inf], right=False, labels=['Ⅰ','Ⅱ','Ⅲ','Ⅳ','Ⅴ','Ⅵ','Ⅶ']) df_result = df_result.set_index('深度').sort_index() df_result.head()

(b)在(a)的基礎上,將烈度分為4個等級:[0,3,4,5,np.inf],依次對南部地區的深度和烈度等級建立多級索引排序。

  • 跟(a)很相似,cut方法對深度,烈度進行切分,把index設為[‘深度’,‘烈度’],然後進行索引排序即可。

php df['烈度'] = pd.cut(df['烈度'],[0,3,4,5,np.inf], right=False, labels=['Ⅰ','Ⅱ','Ⅲ','Ⅳ']) df['深度'] = pd.cut(df['深度'],[0,5,10,15,20,30,50,np.inf], right=False, labels=['Ⅰ','Ⅱ','Ⅲ','Ⅳ','Ⅴ','Ⅵ','Ⅶ']) df_ds = df.set_index(['深度','烈度']) df_ds.sort_index()

【練習二】 對於分類變數而言,呼叫第4章中的變形函式會出現一個BUG(目前的版本下還未修復):例如對於crosstab函式,按照官方文件的說法,即使沒有出現的變數也會在變形後的彙總結果中出現,但事實上並不是這樣,比如下面的例子就缺少了原本應該出現的行'c'和列'f'。基於這一問題,請嘗試設計my_crosstab函式,在功能上能夠返回正確的結果。

  • 因為Categories中肯定包含出現的變數。所以將第一個引數作為index,第二個引數作為columns,建立一個DataFrame,然後把出現的變數組合起來,對應位置填入1即可。

python foo = pd.Categorical(['b','a'], categories=['a', 'b', 'c']) bar = pd.Categorical(['d', 'e'], categories=['d', 'e', 'f']) import numpy def my_crosstab(a, b): s1 = pd.Series(list(foo.categories), name='row') s2 = list(bar.categories) df = pd.DataFrame(np.zeros((len(s1), len(s2)),int),index=s1, columns=s2) index_1 = list(foo) index_2 = list(bar) for loc in zip(index_1, index_2): df.loc[loc] = 1 return df my_crosstab(foo, bar)