- 原文地址:Overview of Pandas Data Types
- 原文做者:Chris Moffitt
- 譯文出自:掘金翻譯計劃
- 本文永久連接:github.com/xitu/gold-m…
- 譯者:stormluke
- 校對者:Starrier、luochen1992
在進行數據分析時,確保使用正確的數據類型很是重要,不然可能會獲得意想不到的結果或錯誤。對 Pandas 而言,它會在不少狀況下正確地做出數據類型推斷,你能夠繼續進行分析工做,而無需深刻思考該主題。html
儘管 Pandas 工做得很好,但在數據分析過程當中的某個時刻,你可能須要將數據從一種類型顯式轉換爲另外一種類型。本文將討論 Pandas 的基本數據類型(即 dtypes
),它們如何映射到 python 和 numpy 數據類型,以及從一種 Pandas 類型轉換爲另外一種類型的幾個方式。前端
數據類型本質上是編程語言用來理解如何存儲和操做數據的內部結構。例如,一個程序須要理解你能夠將兩個數字加起來,好比 5 + 10 獲得 15。或者,若是是兩個字符串,好比「cat」和「hat」,你能夠將它們鏈接(加)起來獲得「cathat」。python
有關 Pandas 數據類型的一個可能使人困惑的地方是,Pandas、Python 和 numpy 的數據類型之間有一些重疊。下表總結了關鍵點:android
Pandas dtype
映射:ios
Pandas dtype | Python 類型 | NumPy 類型 | 用途 |
---|---|---|---|
object | str | string_, unicode_ | 文本 |
int64 | int | int_, int8, int16, int32, int64, uint8, uint16, uint32, uint64 | 整數 |
float64 | float | float_, float16, float32, float64 | 浮點數 |
bool | bool | bool_ | 布爾值 |
datetime64 | NA | NA | 日期時間 |
timedelta[ns] | NA | NA | 時間差 |
category | NA | NA | 有限長度的文本值列表 |
大多數狀況下,你沒必要擔憂是否應該明確地將熊貓類型強制轉換爲對應的 NumPy 類型。通常來講使用 Pandas 的默認 int64
和 float64
就能夠。我列出此表的惟一緣由是,有時你可能會在代碼行間或本身的分析過程當中看到 Numpy 的類型。git
對於本文,我將重點關注如下 Pandas 類型:github
object
int64
float64
datetime64
bool
若是你有興趣,我會再寫一篇文章來專門介紹 category
和 timedelta
類型。不過本文中概述的基本方法也適用於這些類型。編程
數據類型是在你遇到錯誤或意外結果以前並不會關心的事情之一。不過當你將新數據加載到 Pandas 進行進一步分析時,這也是你應該檢查的第一件事情。後端
我將使用一個很是簡單的 CSV文件 來講明在 Pandas 中可能會遇到的一些常見的由數據類型致使的錯誤。另外,在 github 上也一個示例 notbook。bash
import numpy as np
import pandas as pd
df = pd.read_csv("sales_data_types.csv")
複製代碼
Customer Number | Customer Name | 2016 | 2017 | Percent Growth | Jan Units | Month | Day | Year | Active | |
---|---|---|---|---|---|---|---|---|---|---|
0 | 10002.0 | Quest Industries | $125,000.00 | $162500.00 | 30.00% | 500 | 1 | 10 | 2015 | Y |
1 | 552278.0 | Smith Plumbing | $920,000.00 | $101,2000.00 | 10.00% | 700 | 6 | 15 | 2014 | Y |
2 | 23477.0 | ACME Industrial | $50,000.00 | $62500.00 | 25.00% | 125 | 3 | 29 | 2016 | Y |
3 | 24900.0 | Brekke LTD | $350,000.00 | $490000.00 | 4.00% | 75 | 10 | 27 | 2015 | Y |
4 | 651029.0 | Harbor Co | $15,000.00 | $12750.00 | -15.00% | Closed | 2 | 2 | 2014 | N |
乍一看,數據沒什麼問題,因此咱們能夠嘗試作一些操做來分析數據。咱們來試一下把 2016 和 2017 年的銷售額加起來:
df['2016'] + df['2017']
複製代碼
0 $125,000.00$162500.00
1 $920,000.00$101,2000.00
2 $50,000.00$62500.00
3 $350,000.00$490000.00
4 $15,000.00$12750.00
dtype: object
複製代碼
這看起來就不對了。咱們但願將總計加在一塊兒,但 Pandas 只是將這兩個值鏈接在一塊兒建立了一個長字符串。這個問題的一個線索是 dtype:object
。object
在 Pandas 表明字符串,因此它執行的是字符串操做而不是數學操做。
若是咱們想查看 dataframe 中的全部數據類型,使用 df.dtypes
df.dtypes
複製代碼
Customer Number float64
Customer Name object
2016 object
2017 object
Percent Growth object
Jan Units object
Month int64
Day int64
Year int64
Active object
dtype: object
複製代碼
此外,df.info()
函數能夠顯示更有用的信息。
df.info()
複製代碼
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 5 entries, 0 to 4
Data columns (total 10 columns):
Customer Number 5 non-null float64
Customer Name 5 non-null object
2016 5 non-null object
2017 5 non-null object
Percent Growth 5 non-null object
Jan Units 5 non-null object
Month 5 non-null int64
Day 5 non-null int64
Year 5 non-null int64
Active 5 non-null object
dtypes: float64(1), int64(3), object(6)
memory usage: 480.0+ bytes
複製代碼
查看自動分配的數據類型後,有幾個問題:
Customer Number
被歸爲 float64
但它應該是 int64
2016
和 2017
這兩列被存儲爲 object
,但應該是 float64
或 int64
這樣的數值類型Percent Growth
和 Jan Units
也被存儲爲 object
而不是數值類型Month
、Day
和 Year
這三列應該被轉換爲 datetime64
Active
列應該是布爾型在咱們清洗這些數據類型以前,要對這些數據作更多的附加分析是很是困難的。
爲了在 Pandas 中轉換數據類型,有三個基本選項:
astype()
來強制轉換到合適的 dtype
to_numeric()
或 to_datetime()
astype()
函數將 Pandas 數據列轉換爲不一樣類型的最簡單方法就是用 astype()
。例如,要將 Customer Number
轉換爲整數,咱們能夠這樣調用:
df['Customer Number'].astype('int')
複製代碼
0 10002
1 552278
2 23477
3 24900
4 651029
Name: Customer Number, dtype: int64
複製代碼
爲了真正修改原始 dataframe 中的客戶編號(Customer Number),記得把 astype()
函數的返回值從新賦值給 dataframe,由於 astype()
僅返回數據的副本而不原地修改。
df["Customer Number"] = df['Customer Number'].astype('int')
df.dtypes
複製代碼
Customer Number int64
Customer Name object
2016 object
2017 object
Percent Growth object
Jan Units object
Month int64
Day int64
Year int64
Active object
dtype: object
複製代碼
如下是客戶編號(Customer Number)爲整數的新 dataframe:
Customer Number | Customer Name | 2016 | 2017 | Percent Growth | Jan Units | Month | Day | Year | Active | |
---|---|---|---|---|---|---|---|---|---|---|
0 | 10002 | Quest Industries | $125,000.00 | $162500.00 | 30.00% | 500 | 1 | 10 | 2015 | Y |
1 | 552278 | Smith Plumbing | $920,000.00 | $101,2000.00 | 10.00% | 700 | 6 | 15 | 2014 | Y |
2 | 23477 | ACME Industrial | $50,000.00 | $62500.00 | 25.00% | 125 | 3 | 29 | 2016 | Y |
3 | 24900 | Brekke LTD | $350,000.00 | $490000.00 | 4.00% | 75 | 10 | 27 | 2015 | Y |
4 | 651029 | Harbor Co | $15,000.00 | $12750.00 | -15.00% | Closed | 2 | 2 | 2014 | N |
這一切看起來不錯,而且彷佛很簡單。讓咱們嘗試對 2016
列作一樣的事情並將其轉換爲浮點數:
df['2016'].astype('float')
複製代碼
ValueError Traceback (most recent call last)
<ipython-input-45-999869d577b0> in <module>()
----> 1 df['2016'].astype('float')
[一些錯誤信息]
ValueError: could not convert string to float: '$15,000.00'
複製代碼
以相似的方式,咱們能夠嘗試將 Jan Units
列轉換爲整數:
df['Jan Units'].astype('int')
複製代碼
ValueError Traceback (most recent call last)
<ipython-input-44-31333711e4a4> in <module>()
----> 1 df['Jan Units'].astype('int')
[一些錯誤信息]
ValueError: invalid literal for int() with base 10: 'Closed'
複製代碼
這兩個都返回 ValueError
異常,這意味着轉換不起做用。
在這兩個例子中,數據都包含不能被解釋爲數字的值。在銷售額(2016)列中,數據包括貨幣符號以及每一個值中的逗號。在 Jan Units
列中,最後一個值是 "Closed",它不是一個數字;因此咱們獲得了異常。
到目前爲止,astype()
做爲工具看起來並不怎麼好。咱們在 Active
列中再試一次。
df['Active'].astype('bool')
複製代碼
0 True
1 True
2 True
3 True
4 True
Name: Active, dtype: bool
複製代碼
第一眼,這看起來不錯,但通過仔細檢查,存在一個大問題。全部的值都被解釋爲 True
,但最後一個客戶有一個 N
的活動(Active)標誌,因此這並不正確。
這一節的重點是 astype()
只有在下列狀況下才能工做:
若是數據具備非數字字符或它們間不一樣質(homogeneous),那麼 astype()
並非類型轉換的好選擇。你須要進行額外的變換才能完成正確的類型轉換。
因爲該數據轉換稍微複雜一些,所以咱們能夠構建一個自定義函數,將其應用於每一個值並轉換爲適當的數據類型。
對於貨幣轉換(這個特定的數據集),下面是一個咱們可使用的簡單函數:
def convert_currency(val):
""" Convert the string number value to a float - Remove $ - Remove commas - Convert to float type """
new_val = val.replace(',','').replace('$', '')
return float(new_val)
複製代碼
該代碼使用python的字符串函數去掉 $
和 ,
,而後將該值轉換爲浮點數。在這個特定狀況下,咱們能夠將值轉換爲整數,但我選擇在這種狀況下使用浮點數。
我也懷疑有人會建議咱們對貨幣使用 Decimal
類型。這不是 Pandas 的本地數據類型,因此我故意堅持使用 float 方式。
另外值得注意的是,該函數將數字轉換爲 python 的 float
,但 Pandas 內部將其轉換爲 float64
。正如前面提到的,我建議你容許 Pandas 在肯定合適的時候將其轉換爲特定的大小 float
或 int
。你不須要嘗試將其轉換爲更小或更大的字節大小,除非你真的知道爲何須要那樣作。
如今,咱們可使用 Pandas 的 apply
函數將其應用於 2016 列中的全部值。
df['2016'].apply(convert_currency)
複製代碼
0 125000.0
1 920000.0
2 50000.0
3 350000.0
4 15000.0
Name: 2016, dtype: float64
複製代碼
成功!全部的值都顯示爲 float64
,咱們能夠完成所須要的全部數學計算了。
我確信有經驗的讀者會問爲何我不使用 lambda 函數?在回答以前,先看下咱們能夠在一行中使用 lambda
函數完成的操做:
df['2016'].apply(lambda x: x.replace('$', '').replace(',', '')).astype('float')
複製代碼
使用 lambda
,咱們能夠將代碼簡化爲一行,這是很是有效的方法。但我對這種方法有三個主要的意見:
read_csv()
時輕鬆清洗數據。我將在文章結尾處介紹具體的使用方法。有些人也可能會爭辯說,其餘基於 lambda 的方法比自定義函數的性能有所提升。但爲了教導新手,我認爲函數方法更好。
如下是使用 convert_currency
函數轉換兩個銷售(2016 / 2017)列中數據的完整示例。
df['2016'] = df['2016'].apply(convert_currency)
df['2017'] = df['2017'].apply(convert_currency)
df.dtypes
複製代碼
Customer Number int64
Customer Name object
2016 float64
2017 float64
Percent Growth object
Jan Units object
Month int64
Day int64
Year int64
Active object
dtype: object
複製代碼
有關使用 lambda
和函數的另外一個例子,咱們能夠看看修復 Percent Growth
列的過程。
使用 lambda
:
df['Percent Growth'].apply(lambda x: x.replace('%', '')).astype('float') / 100
複製代碼
用自定義函數作一樣的事情:
def convert_percent(val):
""" Convert the percentage string to an actual floating point percent - Remove % - Divide by 100 to make decimal """
new_val = val.replace('%', '')
return float(new_val) / 100
df['Percent Growth'].apply(convert_percent)
複製代碼
二者返回的值相同:
0 0.30
1 0.10
2 0.25
3 0.04
4 -0.15
Name: Percent Growth, dtype: float64
複製代碼
我將介紹的最後一個自定義函數是使用 np.where()
將活動(Active)列轉換爲布爾值。有不少方法來解決這個特定的問題。np.where()
方法對於不少類型的問題都頗有用,因此我選擇在這裏介紹它。
其基本思想是使用 np.where()
函數將全部 Y
值轉換爲 True
,其餘全部值爲 False
df["Active"] = np.where(df["Active"] == "Y", True, False)
複製代碼
其結果以下 dataframe:
Customer Number | Customer Name | 2016 | 2017 | Percent Growth | Jan Units | Month | Day | Year | Active | |
---|---|---|---|---|---|---|---|---|---|---|
0 | 10002.0 | Quest Industries | $125,000.00 | $162500.00 | 30.00% | 500 | 1 | 10 | 2015 | True |
1 | 552278.0 | Smith Plumbing | $920,000.00 | $101,2000.00 | 10.00% | 700 | 6 | 15 | 2014 | True |
2 | 23477.0 | ACME Industrial | $50,000.00 | $62500.00 | 25.00% | 125 | 3 | 29 | 2016 | True |
3 | 24900.0 | Brekke LTD | $350,000.00 | $490000.00 | 4.00% | 75 | 10 | 27 | 2015 | True |
4 | 651029.0 | Harbor Co | $15,000.00 | $12750.00 | -15.00% | Closed | 2 | 2 | 2014 | False |
dtype 被正確地設置爲了 bool
。
df.dtypes
複製代碼
Customer Number float64
Customer Name object
2016 object
2017 object
Percent Growth object
Jan Units object
Month int64
Day int64
Year int64
Active bool
dtype: object
複製代碼
不管你選擇使用 lambda
函數,仍是建立一個更標準的 Python 函數,或者是使用其餘方法(如 np.where
),這些方法都很是靈活,而且能夠根據你本身獨特的數據需求進行定製。
Pandas 在直白的 astype()
函數和複雜的自定義函數之間有一箇中間地帶。這些輔助函數對於某些數據類型轉換很是有用。
若是你順序讀下來,你會注意到我沒有對日期列或 Jan Units
列作任何事情。這兩種列均可以使用 Pandas 的內置函數(如 pd.to_numeric()
和 pd.to_datetime()
)進行轉換。
Jan Units
轉換出現問題的緣由是列中包含一個非數字值。若是咱們嘗試使用 astype()
,咱們會獲得一個錯誤(如前所述)。pd.to_numeric()
函數能夠更優雅地處理這些值:
pd.to_numeric(df['Jan Units'], errors='coerce')
複製代碼
0 500.0
1 700.0
2 125.0
3 75.0
4 NaN
Name: Jan Units, dtype: float64
複製代碼
這裏有幾個值得注意的地方。首先,該函數輕鬆地處理了數據並建立了一個 float64
列。 此外,它會用 NaN
值替換無效的 Closed
值,由於咱們配置了 errors=coerce
。咱們能夠將 Nan
留在那裏,也可使用 fillna(0)
來用 0 填充:
pd.to_numeric(df['Jan Units'], errors='coerce').fillna(0)
複製代碼
0 500.0
1 700.0
2 125.0
3 75.0
4 0.0
Name: Jan Units, dtype: float64
複製代碼
我最終介紹的轉換是將單獨的月份、日期和年份列轉換爲到一個 datetime
類型的列。Pandas 的 pd.to_datetime()
函數 可定製性很好,但默認狀況下也十分明智。
pd.to_datetime(df[['Month', 'Day', 'Year']])
複製代碼
0 2015-01-10
1 2014-06-15
2 2016-03-29
3 2015-10-27
4 2014-02-02
dtype: datetime64[ns]
複製代碼
在這種狀況下,函數將這些列組合成適當 datateime64
dtype 的新列。
咱們須要確保將這些值賦值回 dataframe:
df["Start_Date"] = pd.to_datetime(df[['Month', 'Day', 'Year']])
df["Jan Units"] = pd.to_numeric(df['Jan Units'], errors='coerce').fillna(0)
複製代碼
Customer Number | Customer Name | 2016 | 2017 | Percent Growth | Jan Units | Month | Day | Year | Active | Start_Date | |
---|---|---|---|---|---|---|---|---|---|---|---|
0 | 10002 | Quest Industries | 125000.0 | 162500.0 | 0.30 | 500.0 | 1 | 10 | 2015 | True | 2015-01-10 |
1 | 552278 | Smith Plumbing | 920000.0 | 1012000.0 | 0.10 | 700.0 | 6 | 15 | 2014 | True | 2014-06-15 |
2 | 23477 | ACME Industrial | 50000.0 | 62500.0 | 0.25 | 125.0 | 3 | 29 | 2016 | True | 2016-03-29 |
3 | 24900 | Brekke LTD | 350000.0 | 490000.0 | 0.04 | 75.0 | 10 | 27 | 2015 | True | 2015-10-27 |
4 | 651029 | Harbor Co | 15000.0 | 12750.0 | -0.15 | NaN | 2 | 2 | 2014 | False | 2014-02-02 |
如今數據已正確轉換爲咱們須要的全部類型:
df.dtypes
複製代碼
Customer Number int64
Customer Name object
2016 float64
2017 float64
Percent Growth float64
Jan Units float64
Month int64
Day int64
Year int64
Active bool
Start_Date datetime64[ns]
複製代碼
Dataframe 已準備好進行分析!
在數據採集過程當中應該儘早地使用 astype()
和自定義轉換函數。若是你有一個打算重複處理的數據文件,而且它老是以相同的格式存儲,你能夠定義在讀取數據時須要應用的 dtype
和 converters
。將 dtype
視爲對數據執行 astype()
頗有幫助。converters
參數容許你將函數應用到各類輸入列,相似於上面介紹的方法。
須要注意的是,只能使用 dtype
或 converter
函數中的一種來應用於指定的列。若是你嘗試將二者應用於同一列,則會跳過 dtype
。
下面是一個簡化的例子,它在數據讀入 dataframe 時完成幾乎全部的轉換:
df_2 = pd.read_csv("sales_data_types.csv",
dtype={'Customer Number': 'int'},
converters={'2016': convert_currency,
'2017': convert_currency,
'Percent Growth': convert_percent,
'Jan Units': lambda x: pd.to_numeric(x, errors='coerce'),
'Active': lambda x: np.where(x == "Y", True, False)
})
df_2.dtypes
複製代碼
Customer Number int64
Customer Name object
2016 float64
2017 float64
Percent Growth float64
Jan Units float64
Month int64
Day int64
Year int64
Active object
dtype: object
複製代碼
正如前面提到的,我選擇了包含用於轉換數據的 lambda
示例和函數示例。惟一沒法被應用在這裏的函數就是那個用來將 Month
、Day
和 Year
三列轉換到 datetime
列的函數。不過,這還是一個強大的能夠幫助改進數據處理流程的約定。
探索新數據集的第一步是確保數據類型設置正確。大部分時間 Pandas 都會作出合理推論,但數據集中有不少細微差異,所以知道如何使用 Pandas 中的各類數據轉換選項很是重要。若是你有任何其餘建議,或者有興趣探索 category
數據類型,請隨時在下面發表評論。
掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。