Pandas 提供多種資料型別,有效運用這些型別對於提升資料處理效能至關重要。不同於 NumPy 的基礎型別,Pandas 的擴充型別能妥善處理缺失值,並針對特定資料型別提供最佳化操作。選擇資料型別時,建議優先考慮 Pandas 擴充型別,其次是 Arrow 資料型別,最後才是 NumPy 型別。理解不同型別的特性,例如整數的位元寬度、浮點數的精確度限制,以及字串和布林值的處理方式,有助於撰寫更有效率且穩定的程式碼。此外,Pandas 的字串存取器簡化了字串操作,而擴充型別如 pd.BooleanDtype
和 pd.CategoricalDtype
則提供了更精確和高效的資料處理方式,特別是在處理缺失值和分類別資料時。
資料型別的管理與最佳實踐
在 pandas 中,資料型別(Data Types)的選擇對於確保資料品質以及實作高效能的演算法至關重要。與資料函式庫類別似,pandas 提供了多種資料型別,如整數、浮點數、字串等,但其實作方式卻有多種選擇,這使得使用者需要了解不同資料型別的實作細節,以做出最佳選擇。
pandas 資料型別的演進
早期 pandas 依賴 NumPy 的型別系統,但 NumPy 的型別系統存在一些重大缺陷,例如不支援缺失值、字串處理能力弱等。從 pandas 0.23 版本開始,引入了新的資料型別,稱為 pandas extension types,這些型別建立在 NumPy 之上,但能夠處理缺失值。在 pandas 1.0 中,實作了自己的字串資料型別。從 pandas 2.0 開始,pandas 支援使用 Apache Arrow 的資料型別。
資料型別的最佳實踐
儘管 pandas 支援多種資料型別,但預設的型別仍然根據 NumPy。本章將介紹如何管理 pandas 中的資料型別,並給出以下指導原則:
- 盡可能使用 pandas extension types
- 當 pandas extension types 不足時,使用 Arrow 資料型別
- 最後才使用根據 NumPy 的資料型別
整數型別(Integral Types)
整數型別是最基本的資料型別之一,用於表示整數。它們在許多應用中非常有用,包括算術運算、索引、計數和列舉等。整數型別在 pandas 中經過高度最佳化,從 pandas 到硬體層面都有很好的支援。
如何使用整數型別
任何有效的整數序列都可以傳遞給 pd.Series
建構函式,並指定 dtype=pd.Int64Dtype()
引數,以建立 64 位元整數資料型別:
pd.Series(range(3), dtype=pd.Int64Dtype())
輸出結果:
0 0
1 1
2 2
dtype: Int64
當儲存和計算資源不是問題時,使用者通常會選擇 64 位元整數,但也可以選擇更小的資料型別:
pd.Series(range(3), dtype=pd.Int8Dtype())
輸出結果:
0 0
1 1
2 2
dtype: Int8
缺失值的處理
pandas 使用 pd.NA
作為缺失值的指標,類別似於資料函式庫中的 NULL
:
pd.Series([1, pd.NA, 2], dtype=pd.Int64Dtype())
輸出結果:
0 1
1 <NA>
2 2
dtype: Int64
此外,pd.Series
建構函式會自動將 Python 的 None
值轉換為 pd.NA
:
pd.Series([1, None, 2], dtype=pd.Int64Dtype())
輸出結果:
0 1
1 <NA>
2 2
dtype: Int64
需要注意的事項
對於新使用者來說,瞭解 pandas 中的整數型別與 Python 的 int
型別的不同是非常重要的。pandas 中的整數型別具有上下限,這些限制由整數的寬度和符號決定。
內容解密:
在上述程式碼中,我們使用了 pd.Series
建構函式來建立整數序列的 Series 物件,並指定了不同的資料型別。我們還展示瞭如何處理缺失值,包括使用 pd.NA
和自動轉換 Python 的 None
值。這些範例演示瞭如何在 pandas 中有效地使用整數型別。
圖表翻譯:
此圖示展示了整數型別在 pandas 中的使用流程,包括建立 Series 物件、指定資料型別和處理缺失值。
graph LR; A[建立 Series 物件] --> B[指定資料型別]; B --> C[處理缺失值]; C --> D[使用 pd.NA]; C --> E[自動轉換 None 值];
圖表翻譯: 此圖表展示了整數型別在 pandas 中的使用流程。首先,建立 Series 物件;然後,指定所需的資料型別;接著,處理可能出現的缺失值;最後,使用 pd.NA
表示缺失值,或自動轉換 Python 的 None
值。
資料型別詳解
在大多數的計算環境中,使用者常用的整數寬度(integer width)有 8、16、32 和 64 位元。這些整數型別可以根據是否有符號(signed)或無符號(unsigned)進行分類別。無符號整數不允許為負數,而有符號整數則可以表示正數或負數。下表總結了不同寬度和符號的整數型別的範圍限制:
型別 | 下限 | 上限 |
---|---|---|
8 位元有符號 | -128 | 127 |
8 位元無符號 | 0 | 255 |
16 位元有符號 | -32768 | 32767 |
16 位元無符號 | 0 | 65535 |
32 位元有符號 | -2147483648 | 2147483647 |
32 位元無符號 | 0 | 4294967295 |
64 位元有符號 | -(2**63) | 2**63-1 |
64 位元無符號 | 0 | 2**64-1 |
整數型別的取捨
在選擇整數型別時,需要權衡儲存容量和記憶體使用效率。以 64 位元整數型別為例,其所需的記憶體是 8 位元整數型別的 8 倍。是否會成為問題取決於資料集的大小和分析所使用的系統資源。
pandas 中的整數型別
在 pandas 的擴充型別系統中,整數型別的 dtype
引數遵循特定的命名規則。有符號整數使用 pd.IntXXDtype()
,無符號整數則使用 pd.UIntXXDtype()
,其中 XX
代表位元寬度。
import pandas as pd
# 建立有符號 16 位元整數 Series
signed_series = pd.Series(range(555, 558), dtype=pd.Int16Dtype())
print(signed_series)
# 輸出:
# 0 555
# 1 556
# 2 557
# dtype: Int16
# 建立無符號 8 位元整數 Series
unsigned_series = pd.Series(range(3), dtype=pd.UInt8Dtype())
print(unsigned_series)
# 輸出:
# 0 0
# 1 1
# 2 2
# dtype: UInt8
程式碼解密:
- 匯入 pandas 函式庫:使用
import pandas as pd
將 pandas 匯入,以便後續使用其功能。 - 建立有符號整數 Series:使用
pd.Series()
建立一個 Series 物件,並指定dtype=pd.Int16Dtype()
以確保資料儲存為有符號 16 位元整數。 - 建立無符號整數 Series:同樣使用
pd.Series()
,但指定dtype=pd.UInt8Dtype()
以儲存為無符號 8 位元整數。
浮點數型別
浮點數型別允許表示實數,不僅限於整數。這使得在計算中能夠處理連續且理論上無限的值。浮點數計算廣泛應用於科學計算、金融分析、機器學習等領域。
使用浮點數的注意事項
雖然浮點數提供了極大的靈活性,但其本質上仍受限於電腦硬體的限制。因此,浮點數可能會失去精確度並引入捨入錯誤,尤其是在處理極端值時。若需要絕對精確度,應考慮使用 PyArrow 中的十進位制型別。
如何建立浮點數資料
要建立浮點數資料,可以使用 dtype=pd.Float64Dtype()
:
float_series = pd.Series([3.14, .333333333, -123.456], dtype=pd.Float64Dtype())
print(float_series)
# 輸出:
# 0 3.14
# 1 0.333333
# 2 -123.456
# dtype: Float64
程式碼解密:
- 建立浮點數 Series:使用
pd.Series()
建立 Series,並指定dtype=pd.Float64Dtype()
以確保資料以 64 位元浮點數儲存。 - 處理缺失值:pandas 將自動把
None
或pd.NA
轉換為適當的缺失值表示。
布林型別
布林型別表示一個值為 True 或 False。它常用於回答是/否的問題,並在機器學習演算法中將類別值轉換為電腦易於處理的數字(1 和 0)。
如何建立布林資料
布林資料可以使用 dtype=pd.BooleanDtype()
建立:
bool_series = pd.Series([True, False, True], dtype=pd.BooleanDtype())
print(bool_series)
# 輸出:
# 0 True
# 1 False
# 2 True
# dtype: boolean
程式碼解密:
- 建立布林 Series:使用
pd.Series()
,並指定dtype=pd.BooleanDtype()
以儲存布林值。 - 自動轉換:pandas 可以自動將某些值(如 0 和 1)轉換為布林表示。
字串型別
字串型別適用於表示文字資料。在大多數非純科學領域,字串資料非常普遍。pandas 為字串資料提供了額外的功能,如大小寫轉換、子字串提取和模式匹配等。
如何建立字串資料
可以使用 dtype=pd.StringDtype()
建立字串資料:
string_series = pd.Series(["foo", "bar", "baz"], dtype=pd.StringDtype())
print(string_series)
# 輸出:
# 0 foo
# 1 bar
# 2 baz
# dtype: string
程式碼解密:
- 建立字串 Series:使用
pd.Series()
,並指定dtype=pd.StringDtype()
以儲存字串資料。 - 處理缺失值:與其他型別類別似,
None
將被隱式轉換為缺失值表示pd.NA
。
pandas 中的字串處理與缺失值處理
在處理包含字串資料的 pd.Series
時,pandas 提供了所謂的「字串存取器」(string accessor),透過 pd.Series.str
可以使用許多針對字串的方法。
字串存取器的使用
字串存取器可以幫助我們進行諸如計算每個字串的長度等操作:
ser = pd.Series(["xx", "YyY", "zZzZ"], dtype=pd.StringDtype())
print(ser.str.len())
輸出結果:
0 2
1 3
2 4
dtype: Int64
內容解密:
ser.str.len()
用於計算ser
中每個元素的字串長度。dtype=pd.StringDtype()
確保該 Series 使用 pandas 的字串資料型別。- 輸出結果的
dtype
為Int64
,表示使用的是 pandas 的 64 位元整數型別,可以容納缺失值。
此外,字串存取器還可以用於轉換字串的大小寫:
print(ser.str.upper()) # 轉換為大寫
print(ser.str.lower()) # 轉換為小寫
print(ser.str.title()) # 轉換為標題大小寫(首字母大寫,其餘小寫)
輸出結果分別為:
0 XX
1 YYY
2 ZZZZ
dtype: string
0 xx
1 yyy
2 zzzz
dtype: string
0 Xx
1 Yyy
2 Zzzz
dtype: string
內容解密:
ser.str.upper()
、ser.str.lower()
和ser.str.title()
分別用於將字串轉換為全大寫、全小寫和標題大小寫。- 輸出的
dtype
為string
,表示結果是 pandas 的字串型別。
字串包含檢查
使用 pd.Series.str.contains
可以檢查 Series 中的元素是否包含特定的字串或符合特定的正規表示式:
ser = pd.Series(["foo", "bar", "baz"], dtype=pd.StringDtype())
print(ser.str.contains("o"))
輸出結果:
0 True
1 False
2 False
dtype: boolean
內容解密:
ser.str.contains("o")
檢查每個元素是否包含字母 “o”。- 結果是一個布林型的 Series,表示每個元素是否符合條件。
若要使用正規表示式進行比對,可以設定 regex=True
,並可透過 case=False
進行不區分大小寫的比對:
print(ser.str.contains(r"^ba[rz]$", case=False, regex=True))
輸出結果:
0 False
1 True
2 True
dtype: boolean
內容解密:
r"^ba[rz]$"
是一個正規表示式,用於比對 “bar” 或 “baz”。case=False
使比對過程不區分大小寫。regex=True
表示引數為正規表示式而非普通字串。
缺失值處理
pandas 在處理缺失值時存在一些不一致性,這主要是由於歷史原因和底層實作的差異。早期 pandas 根據 NumPy,而 NumPy 的預設資料型別不支援缺失值。因此,pandas 曾使用 np.nan
作為缺失值的表示,但這帶來了一些問題:
ser = pd.Series(range(3))
ser.iloc[1] = None
print(ser)
輸出結果:
0 0.0
1 NaN
2 2.0
dtype: float64
內容解密:
- 將整數 Series 中的一個元素設為
None
後,Series 的型別被轉換為浮點數型別,因為np.nan
是浮點數。 - 這種轉換可能導致效能、記憶體使用或精確度的損失。
pandas 為瞭解決這些問題,在 0.24 版本引入了擴充型別(Extension Types),使得某些型別(如整數、字串)在遇到缺失值時不會進行隱式型別轉換,而是統一使用 pd.NA
表示缺失值。
如何檢查缺失值
無論使用預設型別還是擴充型別,pd.isna
都可以用來檢查元素是否為缺失值:
print(pd.isna(pd.Series([1, np.nan, 2])))
print(pd.isna(pd.Series([1, pd.NA, 2], dtype=pd.Int64Dtype())))
輸出結果均為:
0 False
1 True
2 False
dtype: bool
內容解密:
pd.isna
能夠正確識別不同型別的缺失值表示法,如np.nan
和pd.NA
。- 這使得檢查缺失值的操作變得簡單且一致。
深入理解pandas中的資料型別
在資料分析與處理的過程中,正確選擇與使用資料型別是至關重要的。pandas函式庫提供了多種資料型別,以滿足不同資料的需求。本章將重點介紹pd.BooleanDtype
、pd.CategoricalDtype
等特殊資料型別的使用方法及其優勢。
布林擴充套件型別(BooleanDtype)
在pandas中,傳統的布林型別在處理缺失值時可能會遇到問題。為瞭解決這一問題,pandas引入了pd.BooleanDtype
。這種型別允許在布林序列中存在缺失值(pd.NA
),並且能夠正確地進行遮罩(masking)操作。
使用範例
ser = pd.Series(range(3), dtype=pd.Int64Dtype())
mask = pd.Series([True, pd.NA, False], dtype=pd.BooleanDtype())
ser[mask]
輸出結果:
0 0
dtype: Int64
在這個例子中,mask
序列包含了布林值和缺失值。使用這個遮罩篩選ser
序列時,只有布林值為True
的對應元素才會被選取,而pd.NA
對應的元素則不會被選取。
與傳統布林型別的比較
如果不使用pd.BooleanDtype
,而是直接使用包含缺失值的布林序列進行遮罩操作,將會導致錯誤:
mask = pd.Series([True, None, False])
ser[mask]
輸出錯誤:
ValueError: Cannot mask with non-boolean array containing NA / NaN values
為了避免這個錯誤,通常需要先將缺失值填充為False
,然後再進行型別轉換:
mask = mask.fillna(False).astype(bool)
ser[mask]
這種做法不僅複雜,而且效率較低。使用pd.BooleanDtype
可以簡化這一過程,提高程式碼的可讀性和執行效率。
分型別別(CategoricalDtype)
分型別別用於表示具有有限個可能值的資料。透過將字串值(如"foo"、“bar”、“baz”)轉換為整數程式碼(0、1、2),可以節省記憶體並提高運算效率。
建立分型別別
有兩種方法可以建立分型別別:
- 直接轉換
values_ser = pd.Series(["foo", "bar", "baz"], dtype=pd.StringDtype())
ser = values_ser.astype(pd.CategoricalDtype())
ser
輸出結果:
0 foo
1 bar
2 baz
dtype: category
Categories (3, object): ['bar', 'baz', 'foo']
- 自定義分型別別
cat = pd.CategoricalDtype(values_ser)
ser = pd.Series(values, dtype=cat)
ser
輸出結果:
0 foo
1 bar
2 baz
dtype: category
Categories (3, object): ['foo', 'bar', 'baz']
分型別別的特點
- 分型別別定義了允許的值集合。在建立分型別別時指定的值集合決定了該序列可以接受的值。
- 嘗試指定不在定義集合中的值將會導致錯誤。
- 可以透過
ordered=True
引數指定值的順序,這對於序數資料(如衣物尺寸)非常有用。
有序分型別別範例
shirt_sizes = pd.Series(["S", "M", "L", "XL"], dtype=pd.StringDtype())
cat = pd.CategoricalDtype(shirt_sizes, ordered=True)
ser = pd.Series(["XL", "L", "S", "L", "S", "M"], dtype=cat)
ser < "L"
輸出結果:
0 False
1 False
2 True
3 False
4 True
5 True
dtype: bool
在這個例子中,我們建立了一個有序的分型別別,用於表示衣物尺寸。這使得比較不同尺寸變得非常自然和高效。