【Python】pandas 分类数据处理大全(附代码)
所有數據和代碼可在GitHub獲取:
https://github.com/xiaoyusmd/PythonDataScience
category是pandas的一種分類的定類數據類型。和文本數據.str.<methond>一樣,它也有訪問器功能.cat.<method>。
本文將介紹:
什么是分類數據?
分類數據cat的處理方法
為什么要使用分類數據?
分類數據cat使用時的一些坑
什么是分類數據?
分類數據表達數值具有某種屬性、類型和特征,也是我們理解的定類數據。比如,人口按性別分為男和女,按年齡分為老、中、少。
在計算機語言里,我們通常會用數字來表示,比如用1代表男,0代表女,但是0和1之間并沒有大小關系,pandas中用category來表示分類數據。
創建分類數據
創建數據時可以用dtpye來指定類型,比如:
s?=?pd.Series(['a','b','c'],dtype='category') s ------ 0????a 1????b 2????c dtype:?category Categories?(3,?object):?['a',?'b',?'c']自動創建分類數據
在某些操作情況下會自動轉變為分類類型,比如用cut進行分箱操作返回的分箱就是分類類型。
pd.Series(pd.cut(range(1,10,2),3)) ----------------- 0????(0.992,?3.667] 1????(0.992,?3.667] 2????(3.667,?6.333] 3??????(6.333,?9.0] 4??????(6.333,?9.0] dtype:?category Categories?(3,?interval[float64]):?[(0.992,?3.667]?<?(3.667,?6.333]?<?(6.333,?9.0]]分類數據類型轉換
直接用astype方法轉換即可,如:
s?=?pd.Series(['a','b','c']) s ------ 0????a 1????b 2????c dtype:?objects.astype('category') ------ 0????a 1????b 2????c dtype:?category Categories?(3,?object):?['a',?'b',?'c']自定義分類數據
除此之外,還可以通過CategoricalDtype自定義分類數據,自定義的類型適用于以上全部方法。
比如下面自定義了abc3個分類,并指定了順序。然后就可以通過dtype指定自定義的數據類型了,d不在定義類型abc中,顯示為空。
from?pandas.api.types?import?CategoricalDtype #?自定義分類數據,有序 c=?CategoricalDtype(categories=['a','b','c'],ordered=True) pd.Series(list('abcabd'),dtype=c) -------- 0??????a 1??????b 2??????c 3??????a 4??????b 5????NaN dtype:?category Categories?(3,?object):?['a'?<?'b'?<?'c']分類數據的處理方法
修改分類
通過.cat.rename_categories()修改分類的名稱。
s?=?pd.Series(['a','b','c'],dtype='category') #?指定分類為x、y、z s.cat.categories?=?['x','y','z'] 0????x 1????y 2????z dtype:?category Categories?(3,?object):?['x',?'y',?'z']#?列表形式:修改分類類型為mno s.cat.rename_categories(['m','n','o']) #?字典形式: s.cat.rename_categories({'x':'m','y':'n','z':'o'}) 0????m 1????n 2????o dtype:?category Categories?(3,?object):?['m',?'n',?'o']追加新分類
通過.cat.add_categories()追加分類。
s.cat.add_categories(['r','t']) 0????x 1????y 2????z dtype:?category Categories?(5,?object):?['x',?'y',?'z',?'r',?'t']刪除分類
同理,也可以刪除分類。有兩種方法remove_categories和remove_unused_categories。
#?刪除指定的分類r和t s.cat.remove_categories(['r','t']) #?自動刪除未使用的分類 s.cat.remove_unused_categories()順序
默認情況下分類數據不自動排序,可以通過前面CategoricalDtype設置順序,或者通過.cat.as_ordered設置。
#?有序設置 s.cat.as_ordered() 0????x 1????y 2????z dtype:?category Categories?(3,?object):?['x'?<?'y'?<?'z'] #?無序設置 s.cat.as_unordered() #?重新排序 s.cat.reorder_categories(['y','x','z'],?ordered=True)為什么使用category數據類型?
總結一下,使用category有以下一些好處:
內存使用情況:對于重復值很多的字符串列,category可以大大減少將數據存儲在內存中所需的內存量;
運行性能:進行了一些優化,可以提高某些操作的執行速度
算法庫的適用:在某些情況下,一些算法模型需要category這種類型。比如,我們知道lightgbm相對于xgboost優化的一個點就是可以處理分類變量,而在構建模型時我們需要指定哪些列是分類變量,并將它們調整為category作為超參數傳給模型。
一個簡單的例子。
df_size?=?100_000 df1?=?pd.DataFrame({"float_1":?np.random.rand(df_size),"species":?np.random.choice(["cat",?"dog",?"ape",?"gorilla"],?size=df_size),} ) df1_cat?=?df1.astype({"species":?"category"})創建了兩個DataFrame,其中df1包含了species并且為object類型,df1_cat復制了df1,但指定了species為category類型。
>>?df1.memory_usage(deep=True) Index??????????128 float_1?????800000 species????6100448 dtype:?int64就內存使用而言,我們可以直接看到包含字符串的列的成本是多高。species列的字符串大約占用了6MB,如果這些字符串較長,則將會更多。
>>?df1_cat.memory_usage(deep=True) Index?????????128 float_1????800000 species????100416 dtype:?int64再看轉換為category類別后的內存使用情況。有了相當大的改進,使用的內存減少了大約60倍。沒有對比,就沒有傷害。
這就是使用category的其中一個好處。
使用category的一些坑!
但愛之深,責之切呀,category有很多坑要注意,這里東哥總結出以下幾點,供大家參考。
1、category列的操作
好吧,這部分應該才是大家較為關心的,因為經常會遇到一些莫名其妙的報錯或者感覺哪里不對,又不知道問題出在哪里。
首先,說明一下:使用category的時候需要格外小心,因為如果姿勢不對,它就很可能變回object 。而變回object的結果就是,會降低代碼的性能(因為強制轉換類型成本很高),并會消耗內存。
日常面對category類型的數據,我們肯定是要對其進行操作的,比如做一些轉換。下面看一個例子,我們要分別對category和object類型進行同樣的字符串大寫操作,使用accessor的.str方法。
在非category字符串上:
>>?%timeit?df1["species"].str.upper() 25.6?ms?±?2.07?ms?per?loop?(mean?±?std.?dev.?of?7?runs,?10?loops?each)在category字符串上:
>>?%timeit?df1_cat["species"].str.upper() 1.85?ms?±?41.1?μs?per?loop?(mean?±?std.?dev.?of?7?runs,?1000?loops?each)結果很明顯了。在這種情況下,速度提高了大約14倍(因為內部優化會讓.str.upper()僅對分類的唯一類別值調用一次,然后根據結果構造一個seires,而不是對結果中的每個值都去調用一次)。
怎么理解?假設現有一個列叫animal,其類別有cat和dog兩種,假設樣本為10000個,4000個cat和6000個dog。那么如果我用對category本身處理,意味著我只分別對cat和dog兩種類別處理一次,一共兩次就解決。如果對每個值處理,那就需要樣本數量10000次的處理。
盡管從時間上有了一些優化,然而這種方法的使用也是有一些問題的。。。看一下內存使用情況。
>>?df1_cat["species"].str.upper().memory_usage(deep=True) 6100576意外的發現category類型丟了。。結果竟是一個object類型,數據壓縮的效果也沒了,現在的結果再次回到剛才的6MB內存占用。
這是因為使用str會直接讓原本的category類型強制轉換為object,所以內存占用又回去了,這是我為什么最開始說要格外小心。
解決方法就是:直接對category本身操作而不是對它的值操作。 要直接使用cat的方法來完成轉換操作,如下。
%timeit?df1_cat["species"].cat.rename_categories(str.upper) 239?μs?±?13.9?μs?per?loop?(mean?±?std.?dev.?of?7?runs,?1000?loops?each)可以看到,這個速度就更快了,因為省去了將category類別轉換為object的時間,并且內存占用也非常少。因此,這才是最優的做法。
2、與category列的合并
還是上面那個例子,但是這次增加了habitat一列,并且species中增加了sanke。
df2?=?pd.DataFrame({"species":?["cat",?"dog",?"ape",?"gorilla",?"snake"],"habitat":?["house",?"house",?"jungle",?"jungle",?"jungle"],} ) df2_cat?=?df2.astype({"species":?"category",?"habitat":?"category"})和前面一樣,創建該數據集的一個category版本,并創建了一個帶有object字符串的版本。如果將兩個object列合并在一起的,沒什么意思,因為大家都知道會發生什么,object+ object= object而已。
把object列合并到category列上
接著上面的例子。
>>?df1.merge(df2_cat,?on="species").dtypes float_1?????float64 species??????object habitat????category dtype:?object左邊的df1中species列為object,右邊的df2_cat中species列為category。我們可以看到,當我們合并時,在結果中的合并列會得到category+ object= object。
這顯然不行了,又回到原來那樣了。我們再試下其他情況。
兩個category列的合并
>>?df1_cat.merge(df2_cat,?on="species").dtypes float_1?????float64 species??????object habitat????category dtype:?object結果是:category+ category= object?
有點想打人了,但是別急,我們看看為啥。
在合并中,為了保存分類類型,兩個category類型必須是完全相同的。 這個與pandas中的其他數據類型略有不同,例如所有float64列都具有相同的數據類型,就沒有什么區分。
而當我們討論category數據類型時,該數據類型實際上是由該特定類別中存在的一組值來描述的,因此一個類別包含["cat", "dog", "mouse"]與類別包含["cheese", "milk", "eggs"]是不一樣的。上面的例子之所以沒成功,是因為多加了一個snake。
因此,我們可以得出結論:
category1+ category2=object
category1+ category1=category1
因此,解決辦法就是:兩個category類別一模一樣,讓其中一個等于另外一個。
>>?df1_cat.astype({"species":?df2_cat["species"].dtype}).merge(df2_cat,?on="species").dtypesfloat_1?????float64 species????category habitat????category dtype:?object3、category列的分組
用category類列分組時,一旦誤操作就會發生意外,結果是Dataframe會被填成空值,還有可能直接跑死。。
當對category列分組時,默認情況下,即使category類別的各個類不存在值,也會對每個類進行分組。
一個例子來說明。
habitat_df?=?(df1_cat.astype({"species":?df2_cat["species"].dtype}).merge(df2_cat,?on="species") ) house_animals_df?=?habitat_df.loc[habitat_df["habitat"]?==?"house"]這里采用habitat_df,從上面例子得到的,篩選habitat為house的,只有dog和cat是house,看下面分組結果。
>>?house_animals_df.groupby("species")["float_1"].mean() species ape?????????????NaN cat????????0.501507 dog????????0.501023 gorilla?????????NaN snake???????????NaN Name:?float_1,?dtype:?float64在groupby中得到了一堆空值。默認情況下,當按category列分組時,即使數據不存在,pandas也會為該類別中的每個值返回結果。略坑,如果數據類型包含很多不存在的,尤其是在多個不同的category列上進行分組,將會極其損害性能。
因此,解決辦法是:可以傳遞observed=True到groupby調用中,這確保了我們僅獲取數據中有值的組。
>>?house_animals_df.groupby("species",?observed=True)["float_1"].mean() species cat????0.501507 dog????0.501023 Name:?float_1,?dtype:?float644、category列的索引
仍以上面例子舉例,使用groupby-unstack實現了一個交叉表,species作為列,habitat作為行,均為category類型。
>>?species_df?=?habitat_df.groupby(["habitat",?"species"],?observed=True)["float_1"].mean().unstack() >>?species_dfspecies???????cat???????ape???????dog???gorilla habitat???????????????????????????????????????? house????0.501507???????NaN??0.501023???????NaN jungle????????NaN??0.501284???????NaN??0.501108這好像看似也沒什么毛病,我們繼續往下看。為這個交叉表添加一個新列new_col,值為1。
>>?species_df["new_col"]?=?1 TypeError:?'fill_value=new_col'?is?not?present?in?this?Categorical's?categories正常情況下,上面這段代碼是完全可以的,但這里報錯了,為什么?
原因是:species和habitat現在均為category類型。使用.unstack()會把species索引移到列索引中(類似pivot交叉表的操作)。而當添加的新列不在species的分類索引中時,就會報錯。
總結一下,pandas的category類型非常有用,可以帶來一些良好的性能優勢。但是它也很嬌氣,使用過程中要尤為小心,確保category類型在整個流程中保持不變,避免變回object。本文介紹的4個點注意點:
category列的變換操作:直接對category本身操作而不是對它的值操作。這樣可以保留分類性質并提高性能。
category列的合并:合并時注意,要保留category類型,且每個dataframe的合并列中的分類類型必須完全匹配。
category列的分組:默認情況下,獲得數據類型中每個值的結果,即使數據中不存在該結果。可以通過設置observed=True調整。
category列的索引:當索引為category類型的時候,注意是否可能與類別變量發生奇怪的交互作用。
以上就是本次分享內容。
所有數據和代碼可在我的GitHub獲取:
https://github.com/xiaoyusmd/PythonDataScience
原創不易,歡迎點贊、留言、分享,支持作者繼續寫下去。
往期精彩回顧適合初學者入門人工智能的路線及資料下載(圖文+視頻)機器學習入門系列下載中國大學慕課《機器學習》(黃海廣主講)機器學習及深度學習筆記等資料打印《統計學習方法》的代碼復現專輯 AI基礎下載機器學習交流qq群955171419,加入微信群請掃碼:總結
以上是生活随笔為你收集整理的【Python】pandas 分类数据处理大全(附代码)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 上位机和下位机的概念,理解如何实现PC从
- 下一篇: 【机器学习】深入理解CatBoost