數據分析篇 | Pandas基礎用法6【完結篇】

  • 2019 年 12 月 11 日
  • 筆記

以下文章來源於Python大咖談,作者吱吱不倦的呆鳥

數據類型

大多數情況下,pandas 使用 Numpy 數組、Series 或 DataFrame 里某列的數據類型。Numpy 支援 floatintbooltimedelta[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 關鍵字直接傳遞,還是通過 ndarraySeries 傳遞,都會保存至 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.NaTdatetimetimedelta),或 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() 有兩個參數,includeexclude,用於實現「提取這些數據類型的列」 (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 支援 categorydatetime64[ns, tz] 類型,但這兩種類型未整合到 Numpy 的架構里,因此,上面的函數沒有顯示。

【完】