數據分析篇 | Pandas基礎用法6【完結篇】
- 2019 年 12 月 11 日
- 筆記
以下文章來源於Python大咖談,作者吱吱不倦的呆鳥
數據類型
大多數情況下,pandas 使用 Numpy 數組、Series 或 DataFrame 里某列的數據類型。Numpy 支援 float
、int
、bool
、timedelta[ns]
、datetime64[ns]
,注意,Numpy 不支援帶時區資訊的 datetime
。
Pandas 與第三方支援庫對 Numpy 類型系統進行了擴充,本節只介紹 pandas 的內部擴展。如需了解自行編寫與 pandas 配合的擴展類型,請參閱擴展類型,參閱擴展數據類型了解第三方支援庫提供的擴展類型。
下表列出了 pandas 擴展類型,參閱列出的文檔內容,查看每種類型的詳情。
數據種類 |
數據類型 |
標量 |
數組 |
文檔 |
---|---|---|---|---|
帶時區的日期時間 |
DatetimeTZ |
Timestamp |
arrays.DatetimeArray |
Time zone handling |
類別型 |
Categorical |
(無) |
Categorical |
Categorical data |
時間段 |
Period |
Period |
arrays.PeriodArray |
Time span representation |
稀疏數據 |
Sparse |
(無) |
arrays.SparseArray |
Sparse data structures |
時間間隔 |
Interval |
Interval |
arrays.IntervalArray |
IntervalIndex |
空整型 |
Int64… |
(無) |
arrays.IntegerArray |
Nullable integer data type |
此表要橫屏看
Pandas 用 object
存儲字元串。
雖然, object
數據類型能夠存儲任何對象,但應盡量避免這種操作,要了解與其它支援庫與方法的性能與交互操作,參閱 對象轉換。
DataFrame 的 dtypes
屬性用起來很方便,以 Series 形式返回每列的數據類型。
In [328]: dft = pd.DataFrame({'A': np.random.rand(3), .....: 'B': 1, .....: 'C': 'foo', .....: 'D': pd.Timestamp('20010102'), .....: 'E': pd.Series([1.0] * 3).astype('float32'), .....: 'F': False, .....: 'G': pd.Series([1] * 3, dtype='int8')}) .....: In [329]: dft Out[329]: A B C D E F G 0 0.035962 1 foo 2001-01-02 1.0 False 1 1 0.701379 1 foo 2001-01-02 1.0 False 1 2 0.281885 1 foo 2001-01-02 1.0 False 1 In [330]: dft.dtypes Out[330]: A float64 B int64 C object D datetime64[ns] E float32 F bool G int8 dtype: object
要查看 Series
的數據類型,用 dtype
屬性。
In [331]: dft['A'].dtype Out[331]: dtype('float64')
Pandas 對象單列中含多種類型的數據時,該列的數據類型為可適配於各類數據的數據類型,通常為 object
。
# 整數被強制轉換為浮點數 In [332]: pd.Series([1, 2, 3, 4, 5, 6.]) Out[332]: 0 1.0 1 2.0 2 3.0 3 4.0 4 5.0 5 6.0 dtype: float64 # 字元串數據決定了該 Series 的數據類型為 ``object`` In [333]: pd.Series([1, 2, 3, 6., 'foo']) Out[333]: 0 1 1 2 2 3 3 6 4 foo dtype: object
DataFrame.dtypes.value_counts()
用於統計 DataFrame 里各列數據類型的數量。
In [334]: dft.dtypes.value_counts() Out[334]: float32 1 object 1 bool 1 int8 1 float64 1 datetime64[ns] 1 int64 1 dtype: int64
多種數值型數據類型可以在 DataFrame 里共存。如果只傳遞一種數據類型,不論是通過 dtype
關鍵字直接傳遞,還是通過 ndarray
或 Series
傳遞,都會保存至 DataFrame 操作。此外,不同數值型數據類型不會合併。示例如下:
In [335]: df1 = pd.DataFrame(np.random.randn(8, 1), columns=['A'], dtype='float32') In [336]: df1 Out[336]: A 0 0.224364 1 1.890546 2 0.182879 3 0.787847 4 -0.188449 5 0.667715 6 -0.011736 7 -0.399073 In [337]: df1.dtypes Out[337]: A float32 dtype: object In [338]: df2 = pd.DataFrame({'A': pd.Series(np.random.randn(8), dtype='float16'), .....: 'B': pd.Series(np.random.randn(8)), .....: 'C': pd.Series(np.array(np.random.randn(8), .....: dtype='uint8'))}) .....: In [339]: df2 Out[339]: A B C 0 0.823242 0.256090 0 1 1.607422 1.426469 0 2 -0.333740 -0.416203 255 3 -0.063477 1.139976 0 4 -1.014648 -1.193477 0 5 0.678711 0.096706 0 6 -0.040863 -1.956850 1 7 -0.357422 -0.714337 0 In [340]: df2.dtypes Out[340]: A float16 B float64 C uint8 dtype: object
默認值
整數的默認類型為 int64
,浮點數的默認類型為 float64
,這裡的默認值與系統平台無關,不管是 32 位系統,還是 64 位系統都是一樣的。下列程式碼返回的結果都是 int64
:
In [341]: pd.DataFrame([1, 2], columns=['a']).dtypes Out[341]: a int64 dtype: object In [342]: pd.DataFrame({'a': [1, 2]}).dtypes Out[342]: a int64 dtype: object In [343]: pd.DataFrame({'a': 1}, index=list(range(2))).dtypes Out[343]: a int64 dtype: object
注意,Numpy 創建數組時,會根據系統選擇類型。下列程式碼在 32 位系統上將返回 int32
。
In [344]: frame = pd.DataFrame(np.array([1, 2]))
向上轉型
與其它類型合併時,要用到向上轉型,這裡指的是從現有類型轉換為另一種類型,如int
變為 float
。
In [345]: df3 = df1.reindex_like(df2).fillna(value=0.0) + df2 In [346]: df3 Out[346]: A B C 0 1.047606 0.256090 0.0 1 3.497968 1.426469 0.0 2 -0.150862 -0.416203 255.0 3 0.724370 1.139976 0.0 4 -1.203098 -1.193477 0.0 5 1.346426 0.096706 0.0 6 -0.052599 -1.956850 1.0 7 -0.756495 -0.714337 0.0 In [347]: df3.dtypes Out[347]: A float32 B float64 C float64 dtype: object
DataFrame.to_numpy()
返回多個數據類型里用的最多的數據類型,這裡指的是輸出結果的數據類型是適用於所有同質 Numpy 數組的數據類型。這裡會強制執行向上轉型。
In [348]: df3.to_numpy().dtype Out[348]: dtype('float64')
astype
astype()
方法顯式地把一種數據類型轉換為另一種,默認返回的是複製數據,就算數據類型沒有改變也會執行複製操作,copy=False
可以改變默認操作模式。此外,如果 astype
無效會觸發異常。
向上轉型一般都會遵循 numpy 的規則。如果操作中涉及兩種不同類型的數據,返回的將是更通用的那種數據類型。
In [349]: df3 Out[349]: A B C 0 1.047606 0.256090 0.0 1 3.497968 1.426469 0.0 2 -0.150862 -0.416203 255.0 3 0.724370 1.139976 0.0 4 -1.203098 -1.193477 0.0 5 1.346426 0.096706 0.0 6 -0.052599 -1.956850 1.0 7 -0.756495 -0.714337 0.0 In [350]: df3.dtypes Out[350]: A float32 B float64 C float64 dtype: object # 轉換數據類型 In [351]: df3.astype('float32').dtypes Out[351]: A float32 B float32 C float32 dtype: object
用 astype()
把一列或多列轉換為指定類型 。
In [352]: dft = pd.DataFrame({'a': [1, 2, 3], 'b': [4, 5, 6], 'c': [7, 8, 9]}) In [353]: dft[['a', 'b']] = dft[['a', 'b']].astype(np.uint8) In [354]: dft Out[354]: a b c 0 1 4 7 1 2 5 8 2 3 6 9 In [355]: dft.dtypes Out[355]: a uint8 b uint8 c int64 dtype: object
0.19.0 版新增。
astype()
通過字典指定哪些列轉換為哪些類型。
In [356]: dft1 = pd.DataFrame({'a': [1, 0, 1], 'b': [4, 5, 6], 'c': [7, 8, 9]}) In [357]: dft1 = dft1.astype({'a': np.bool, 'c': np.float64}) In [358]: dft1 Out[358]: a b c 0 True 4 7.0 1 False 5 8.0 2 True 6 9.0 In [359]: dft1.dtypes Out[359]: a bool b int64 c float64 dtype: object
用
astype()
與loc()
為部分列轉換指定類型時,會發生向上轉型。loc()
嘗試分配當前的數據類型,而[]
則會從右方獲取數據類型並進行覆蓋。因此,下列程式碼會產出意料之外的結果:
In [360]: dft = pd.DataFrame({'a': [1, 2, 3], 'b': [4, 5, 6], 'c': [7, 8, 9]}) In [361]: dft.loc[:, ['a', 'b']].astype(np.uint8).dtypes Out[361]: a uint8 b uint8 dtype: object In [362]: dft.loc[:, ['a', 'b']] = dft.loc[:, ['a', 'b']].astype(np.uint8) In [363]: dft.dtypes Out[363]: a int64 b int64 c int64 dtype: object
對象轉換
Pandas 提供了多種函數可以把 object
從一種類型強制轉為另一種類型。這是因為,數據有時存儲的是正確類型,但在保存時卻存成了 object
類型,此時,用 DataFrame.infer_objects()
與 Series.infer_objects()
方法即可把數據軟轉換為正確的類型。
In [364]: import datetime In [365]: df = pd.DataFrame([[1, 2], .....: ['a', 'b'], .....: [datetime.datetime(2016, 3, 2), .....: datetime.datetime(2016, 3, 2)]]) .....: In [366]: df = df.T In [367]: df Out[367]: 0 1 2 0 1 a 2016-03-02 1 2 b 2016-03-02 In [368]: df.dtypes Out[368]: 0 object 1 object 2 datetime64[ns] dtype: object
因為數據被轉置,所以把原始列的數據類型改成了 object
,但使用 infer_objects
後就變正確了。
In [369]: df.infer_objects().dtypes Out[369]: 0 int64 1 object 2 datetime64[ns] dtype: object
下列函數可以應用於一維數組與標量,執行硬轉換,把對象轉換為指定類型。
- `to_numeric()`,轉換為數值型
In [370]: m = ['1.1', 2, 3] In [371]: pd.to_numeric(m) Out[371]: array([1.1, 2. , 3. ])
- `to_datetime()`,轉換為
datetime
對象
In [372]: import datetime In [373]: m = ['2016-07-09', datetime.datetime(2016, 3, 2)] In [374]: pd.to_datetime(m) Out[374]: DatetimeIndex(['2016-07-09', '2016-03-02'], dtype='datetime64[ns]', freq=None)
- `to_timedelta()`,轉換為
timedelta
對象。
In [375]: m = ['5us', pd.Timedelta('1day')] In [376]: pd.to_timedelta(m) Out[376]: TimedeltaIndex(['0 days 00:00:00.000005', '1 days 00:00:00'], dtype='timedelta64[ns]', freq=None)
如需強制轉換,則要加入 error
參數,指定 pandas 怎樣處理不能轉換為成預期類型或對象的數據。errors
參數的默認值為 False
,指的是在轉換過程中,遇到任何問題都觸發錯誤。設置為 errors='coerce'
時,pandas 會忽略錯誤,強制把問題數據轉換為 pd.NaT
(datetime
與 timedelta
),或 np.nan
(數值型)。讀取數據時,如果大部分要轉換的數據是數值型或 datetime
,這種操作非常有用,但偶爾也會有非制式數據混合在一起,可能會導致展示數據缺失:
In [377]: import datetime In [378]: m = ['apple', datetime.datetime(2016, 3, 2)] In [379]: pd.to_datetime(m, errors='coerce') Out[379]: DatetimeIndex(['NaT', '2016-03-02'], dtype='datetime64[ns]', freq=None) In [380]: m = ['apple', 2, 3] In [381]: pd.to_numeric(m, errors='coerce') Out[381]: array([nan, 2., 3.]) In [382]: m = ['apple', pd.Timedelta('1day')] In [383]: pd.to_timedelta(m, errors='coerce') Out[383]: TimedeltaIndex([NaT, '1 days'], dtype='timedelta64[ns]', freq=None)
error
參數還有第三個選項,error='ignore'
。轉換數據時會忽略錯誤,直接輸出問題數據:
In [384]: import datetime In [385]: m = ['apple', datetime.datetime(2016, 3, 2)] In [386]: pd.to_datetime(m, errors='ignore') Out[386]: Index(['apple', 2016-03-02 00:00:00], dtype='object') In [387]: m = ['apple', 2, 3] In [388]: pd.to_numeric(m, errors='ignore') Out[388]: array(['apple', 2, 3], dtype=object) In [389]: m = ['apple', pd.Timedelta('1day')] In [390]: pd.to_timedelta(m, errors='ignore') Out[390]: array(['apple', Timedelta('1 days 00:00:00')], dtype=object)
執行轉換操作時,to_numeric()
還有一個參數,downcast
,即向下轉型,可以把數值型轉換為減少記憶體佔用的數據類型:
In [391]: m = ['1', 2, 3] In [392]: pd.to_numeric(m, downcast='integer') # smallest signed int dtype Out[392]: array([1, 2, 3], dtype=int8) In [393]: pd.to_numeric(m, downcast='signed') # same as 'integer' Out[393]: array([1, 2, 3], dtype=int8) In [394]: pd.to_numeric(m, downcast='unsigned') # smallest unsigned int dtype Out[394]: array([1, 2, 3], dtype=uint8) In [395]: pd.to_numeric(m, downcast='float') # smallest float dtype Out[395]: array([1., 2., 3.], dtype=float32)
上述方法僅能應用於一維數組、列表或標量;不能直接用於 DataFrame 等多維對象。不過,用 apply()
,可以快速為每列應用函數:
In [396]: import datetime In [397]: df = pd.DataFrame([ .....: ['2016-07-09', datetime.datetime(2016, 3, 2)]] * 2, dtype='O') .....: In [398]: df Out[398]: 0 1 0 2016-07-09 2016-03-02 00:00:00 1 2016-07-09 2016-03-02 00:00:00 In [399]: df.apply(pd.to_datetime) Out[399]: 0 1 0 2016-07-09 2016-03-02 1 2016-07-09 2016-03-02 In [400]: df = pd.DataFrame([['1.1', 2, 3]] * 2, dtype='O') In [401]: df Out[401]: 0 1 2 0 1.1 2 3 1 1.1 2 3 In [402]: df.apply(pd.to_numeric) Out[402]: 0 1 2 0 1.1 2 3 1 1.1 2 3 In [403]: df = pd.DataFrame([['5us', pd.Timedelta('1day')]] * 2, dtype='O') In [404]: df Out[404]: 0 1 0 5us 1 days 00:00:00 1 5us 1 days 00:00:00 In [405]: df.apply(pd.to_timedelta) Out[405]: 0 1 0 00:00:00.000005 1 days 1 00:00:00.000005 1 days
各種坑
對 integer
數據執行選擇操作時,可以很輕而易舉地把數據轉換為 floating
。pandas 會保存輸入數據的數據類型,以防未引入 nans
的情況。參閱 對整數 NA 空值的支援。
In [406]: dfi = df3.astype('int32') In [407]: dfi['E'] = 1 In [408]: dfi Out[408]: A B C E 0 1 0 0 1 1 3 1 0 1 2 0 0 255 1 3 0 1 0 1 4 -1 -1 0 1 5 1 0 0 1 6 0 -1 1 1 7 0 0 0 1 In [409]: dfi.dtypes Out[409]: A int32 B int32 C int32 E int64 dtype: object In [410]: casted = dfi[dfi > 0] In [411]: casted Out[411]: A B C E 0 1.0 NaN NaN 1 1 3.0 1.0 NaN 1 2 NaN NaN 255.0 1 3 NaN 1.0 NaN 1 4 NaN NaN NaN 1 5 1.0 NaN NaN 1 6 NaN NaN 1.0 1 7 NaN NaN NaN 1 In [412]: casted.dtypes Out[412]: A float64 B float64 C float64 E int64 dtype: object
浮點數類型未改變。
In [413]: dfa = df3.copy() In [414]: dfa['A'] = dfa['A'].astype('float32') In [415]: dfa.dtypes Out[415]: A float32 B float64 C float64 dtype: object In [416]: casted = dfa[df2 > 0] In [417]: casted Out[417]: A B C 0 1.047606 0.256090 NaN 1 3.497968 1.426469 NaN 2 NaN NaN 255.0 3 NaN 1.139976 NaN 4 NaN NaN NaN 5 1.346426 0.096706 NaN 6 NaN NaN 1.0 7 NaN NaN NaN In [418]: casted.dtypes Out[418]: A float32 B float64 C float64 dtype: object
基於 `dtype` 選擇列
select_dtypes()
方法基於 dtype
選擇列。
首先,創建一個由多種數據類型組成的 DataFrame:
In [419]: df = pd.DataFrame({'string': list('abc'), .....: 'int64': list(range(1, 4)), .....: 'uint8': np.arange(3, 6).astype('u1'), .....: 'float64': np.arange(4.0, 7.0), .....: 'bool1': [True, False, True], .....: 'bool2': [False, True, False], .....: 'dates': pd.date_range('now', periods=3), .....: 'category': pd.Series(list("ABC")).astype('category')}) .....: In [420]: df['tdeltas'] = df.dates.diff() In [421]: df['uint64'] = np.arange(3, 6).astype('u8') In [422]: df['other_dates'] = pd.date_range('20130101', periods=3) In [423]: df['tz_aware_dates'] = pd.date_range('20130101', periods=3, tz='US/Eastern') In [424]: df Out[424]: string int64 uint8 float64 bool1 bool2 dates category tdeltas uint64 other_dates tz_aware_dates 0 a 1 3 4.0 True False 2019-08-22 15:49:01.870038 A NaT 3 2013-01-01 2013-01-01 00:00:00-05:00 1 b 2 4 5.0 False True 2019-08-23 15:49:01.870038 B 1 days 4 2013-01-02 2013-01-02 00:00:00-05:00 2 c 3 5 6.0 True False 2019-08-24 15:49:01.870038 C 1 days 5 2013-01-03 2013-01-03 00:00:00-05:00
該 DataFrame 的數據類型:
In [425]: df.dtypes Out[425]: string object int64 int64 uint8 uint8 float64 float64 bool1 bool bool2 bool dates datetime64[ns] category category tdeltas timedelta64[ns] uint64 uint64 other_dates datetime64[ns] tz_aware_dates datetime64[ns, US/Eastern] dtype: object
select_dtypes()
有兩個參數,include
與 exclude
,用於實現「提取這些數據類型的列」 (include
)或 「提取不是這些數據類型的列」(exclude
)。
選擇 bool
型的列,示例如下:
In [426]: df.select_dtypes(include=[bool]) Out[426]: bool1 bool2 0 True False 1 False True 2 True False
該方法還支援輸入 NumPy 數據類型的名稱:
In [427]: df.select_dtypes(include=['bool']) Out[427]: bool1 bool2 0 True False 1 False True 2 True False
select_dtypes()
還支援通用數據類型。
比如,選擇所有數值型與布爾型的列,同時,排除無符號整數:
In [428]: df.select_dtypes(include=['number', 'bool'], exclude=['unsignedinteger']) Out[428]: int64 float64 bool1 bool2 tdeltas 0 1 4.0 True False NaT 1 2 5.0 False True 1 days 2 3 6.0 True False 1 days
選擇字元串型的列必須要用 object
:
In [429]: df.select_dtypes(include=['object']) Out[429]: string 0 a 1 b 2 c
要查看 numpy.number
等通用 dtype
的所有子類型,可以定義一個函數,返回子類型樹:
In [430]: def subdtypes(dtype): .....: subs = dtype.__subclasses__() .....: if not subs: .....: return dtype .....: return [dtype, [subdtypes(dt) for dt in subs]] .....:
所有 Numpy 數據類型都是 numpy.generic
的子類:
In [431]: subdtypes(np.generic) Out[431]: [numpy.generic, [[numpy.number, [[numpy.integer, [[numpy.signedinteger, [numpy.int8, numpy.int16, numpy.int32, numpy.int64, numpy.int64, numpy.timedelta64]], [numpy.unsignedinteger, [numpy.uint8, numpy.uint16, numpy.uint32, numpy.uint64, numpy.uint64]]]], [numpy.inexact, [[numpy.floating, [numpy.float16, numpy.float32, numpy.float64, numpy.float128]], [numpy.complexfloating, [numpy.complex64, numpy.complex128, numpy.complex256]]]]]], [numpy.flexible, [[numpy.character, [numpy.bytes_, numpy.str_]], [numpy.void, [numpy.record]]]], numpy.bool_, numpy.datetime64, numpy.object_]]
注意:Pandas 支援
category
與datetime64[ns, tz]
類型,但這兩種類型未整合到 Numpy 的架構里,因此,上面的函數沒有顯示。
【完】