Pandas數據類型轉換的幾個小技巧

利用Pandas進行數據分析時,確保使用正確的數據類型是非常重要的,否則可能會導致一些不可預知的錯誤發生。筆者使用Pandas已經有一段時間了,但是還是會在一些小問題上犯錯誤,追根溯源發現在對數據進行操作時某些特徵列並不是Pandas所能處理的類型。因此本文將討論一些小技巧如何將Python的基本數據類型轉化為Pandas所能處理的數據類型。

Pandas、Numpy、Python各自支持的數據類型

從上述表格中可以看出Pandas支持的數據類型最為豐富,在某種情形下Numpy的數據類型可以和Pandas的數據類型相互轉化,畢竟Pandas庫是在Numpy的基礎之上開發的的。

引入實際數據進行分析

數據類型是你平常可能不太關心,直到得到了錯誤的結果才映像深刻的東西,因此在這裡引入一個實際數據分析的例子來加深理解。

import numpy as npimport pandas as pddata = pd.read_csv(data.csv, encoding=gbk) #因為數據中含有中文數據data

數據載入完畢,如果現在想要在該數據上進行一些操作,比如把數據列2016、2017對應項相加。

data[2016] + data[2017] #想當然的做法

從結果來看並沒有像想像中那樣數值對應相加,這是因為在Pandas中object類型相加等價於Python中的字元串相加。

data.info() #在對數據進行處理之前應該先查看載入數據的相關信息

在看到載入數據的相關信息後可以發現如下幾個問題:

  • 客戶編號的數據類型是int64而不是object類型
  • 2016、2017列的數據類型是object而不是數值類型(int64、float64)
  • 增長率、所屬組的數據類型應該為數值類型而不是object類型
  • year、month、day的數據類型應該為datetime64類型而不是object類型

Pandas中進行數據類型轉換有三種基本方法:

  • 使用astype()函數進行強制類型轉換
  • 自定義函數進行數據類型轉換
  • 使用Pandas提供的函數如to_numeric()、to_datetime()

使用astype()函數進行類型轉換

對數據列進行數據類型轉換最簡單的方法就是使用astype()函數

data[客戶編號].astype(object)

data[客戶編號] = data[客戶編號].astype(object) #對原始數據進行轉換並覆蓋原始數據列

上面的結果看起來很不錯,接下來給出幾個astype()函數作用於列數據但失效的例子

data[2017].astype(float)

data[所屬組].astype(int)

從上面兩個例子可以看出,當待轉換列中含有不能轉換的特殊值時(例子中¥,ErrorValue等)astype()函數將失效。有些時候astype()函數執行成功了也並不一定代表著執行結果符合預期(神坑!)

data[狀態].astype(bool)

乍一看,結果看起來不錯,但仔細觀察後,會發現一個大問題。那就是所有的值都被替換為True了,但是該列中包含好幾個N標誌,所以astype()函數在該列也是失效的。

總結一下astype()函數有效的情形:

  • 數據列中的每一個單位都能簡單的解釋為數字(2, 2.12等)
  • 數據列中的每一個單位都是數值類型且向字元串object類型轉換

如果數據中含有缺失值、特殊字元astype()函數可能失效。

使用自定義函數進行數據類型轉換

該方法特別適用於待轉換數據列的數據較為複雜的情形,可以通過構建一個函數應用於數據列的每一個數據,並將其轉換為適合的數據類型。

對於上述數據中的貨幣,需要將它轉換為float類型,因此可以寫一個轉換函數:

def convert_currency(value): """ 轉換字元串數字為float類型 - 移除 ¥ , - 轉化為float類型 """ new_value = value.replace(,, ).replace(¥, ) return np.float(new_value)

現在可以使用Pandas的apply函數通過covert_currency函數應用於2016列中的所有數據中。

data[2016].apply(convert_currency)

該列所有的數據都轉換成對應的數值類型了,因此可以對該列數據進行常見的數學操作了。

如果利用lambda表達式改寫一下代碼,可能會比較簡潔但是對新手不太友好。

data[2016].apply(lambda x: x.replace(¥, ).replace(,, )).astype(float)

當函數需要重複應用於多個列時,個人推薦使用第一種方法,先定義函數還有一個好處就是可以搭配read_csv()函數使用(後面介紹)。

#2016、2017列完整的轉換代碼data[2016] = data[2016].apply(convert_currency)data[2017] = data[2017].apply(convert_currency)

同樣的方法運用於增長率,首先構建自定義函數

def convert_percent(value): """ 轉換字元串百分數為float類型小數 - 移除 % - 除以100轉換為小數 """ new_value = value.replace(%, ) return float(new_value) / 100

使用Pandas的apply函數通過covert_percent函數應用於增長率列中的所有數據中。

data[增長率].apply(convert_percent)

使用lambda表達式:

data[增長率].apply(lambda x: x.replace(%, )).astype(float) / 100

結果都相同:

為了轉換狀態列,可以使用Numpy中的where函數,把值為Y的映射成True,其他值全部映射成False。

data[狀態] = np.where(data[狀態] == Y, True, False)

同樣的你也可以使用自定義函數或者使用lambda表達式,這些方法都可以完美的解決這個問題,這裡只是多提供一種思路。

利用Pandas的一些輔助函數進行類型轉換

Pandas的astype()函數和複雜的自定函數之間有一個中間段,那就是Pandas的一些輔助函數。這些輔助函數對於某些特定數據類型的轉換非常有用(如to_numeric()、to_datetime())。

所屬組數據列中包含一個非數值,用astype()轉換出現了錯誤,然而用to_numeric()函數處理就優雅很多。

pd.to_numeric(data[所屬組], errors=coerce).fillna(0)

可以看到,非數值被替換成0.0了,當然這個填充值是可以選擇的,具體文檔見

pandas.to_numeric - pandas 0.22.0 documentation?

pandas.pydata.org

Pandas中的to_datetime()函數可以把單獨的year、month、day三列合併成一個單獨的時間戳。

pd.to_datetime(data[[day, month, year]])

完成數據列的替換

data[new_date] = pd.to_datetime(data[[day, month, year]]) #新產生的一列數據data[所屬組] = pd.to_numeric(data[所屬組], errors=coerce).fillna(0)

到這裡所有的數據列都轉換完畢,最終的數據顯示:

在讀取數據時就對數據類型進行轉換,一步到位

data2 = pd.read_csv("data.csv", converters={ 客戶編號: str, 2016: convert_currency, 2017: convert_currency, 增長率: convert_percent, 所屬組: lambda x: pd.to_numeric(x, errors=coerce), 狀態: lambda x: np.where(x == "Y", True, False) }, encoding=gbk)

在這裡也體現了使用自定義函數比lambda表達式要方便很多。(大部分情況下lambda還是很簡潔的,筆者自己也很喜歡使用)

總結

對數據集進行操作的第一步是確保設置正確的數據類型,然後才能進行數據的分析、可視化等操作,Pandas提供了很多非常方便的函數,有了這些函數那麼對數據進行分析將會是很方便的。


推薦閱讀:

如何與pandas愉快地玩耍(一)

TAG:Python | 數據分析 | pandas包 |