呆鳥雲:「喜歡的話就請關注 Python大咖談。」
本節介紹 pandas 數據結構的基礎用法。下列代碼建立示例數據對象:html
In [1]: index = pd.date_range('1/1/2000', periods=8) In [2]: s = pd.Series(np.random.randn(5), index=['a', 'b', 'c', 'd', 'e']) In [3]: df = pd.DataFrame(np.random.randn(8, 3), index=index, ...: columns=['A', 'B', 'C']) ...:
head()
與 tail()
用於快速預覽 Series 與 DataFrame,默認顯示 5 條數據,也能夠指定要顯示的數量。python
In [4]: long_series = pd.Series(np.random.randn(1000)) In [5]: long_series.head() Out[5]: 0 -1.157892 1 -1.344312 2 0.844885 3 1.075770 4 -0.109050 dtype: float64 In [6]: long_series.tail(3) Out[6]: 997 -0.289388 998 -1.020544 999 0.589993 dtype: float64
Pandas 能夠經過多個屬性訪問元數據:api
shape:數組
軸標籤緩存
注意: 爲屬性賦值是安全的!安全
In [7]: df[:2] Out[7]: A B C 2000-01-01 -0.173215 0.119209 -1.044236 2000-01-02 -0.861849 -2.104569 -0.494929 In [8]: df.columns = [x.lower() for x in df.columns] In [9]: df Out[9]: a b c 2000-01-01 -0.173215 0.119209 -1.044236 2000-01-02 -0.861849 -2.104569 -0.494929 2000-01-03 1.071804 0.721555 -0.706771 2000-01-04 -1.039575 0.271860 -0.424972 2000-01-05 0.567020 0.276232 -1.087401 2000-01-06 -0.673690 0.113648 -1.478427 2000-01-07 0.524988 0.404705 0.577046 2000-01-08 -1.715002 -1.039268 -0.370647
Pandas 對象(Index
, Series
, DataFrame
)至關於數組的容器,用於存儲數據,並執行計算。大部分類型的底層數組都是 numpy.ndarray
")。不過,pandas 與第三方支持庫通常都會擴展 Numpy 類型系統,添加自定義數組(見數據類型)。數據結構
獲取 Index
或 Series
裏的數據,請用 .array
屬性。框架
In [10]: s.array Out[10]: <PandasArray> [ 0.4691122999071863, -0.2828633443286633, -1.5090585031735124, -1.1356323710171934, 1.2121120250208506] Length: 5, dtype: float64 In [11]: s.index.array Out[11]: <PandasArray> ['a', 'b', 'c', 'd', 'e'] Length: 5, dtype: object
array
通常指 ExtensionArray
。至於什麼是 ExtensionArray
及 pandas 爲何要用 ExtensionArray
不是本節要說明的內容。更多信息請參閱數據類型。less
提取 Numpy 數組,用 to_numpy()
或 numpy.asarray()
。dom
In [12]: s.to_numpy() Out[12]: array([ 0.4691, -0.2829, -1.5091, -1.1356, 1.2121]) In [13]: np.asarray(s) Out[13]: array([ 0.4691, -0.2829, -1.5091, -1.1356, 1.2121])
Series
與 Index
的類型是 ExtensionArray
時, to_numpy()
會複製數據,並強制轉換值。詳情見數據類型。
to_numpy()
能夠控制 numpy.ndarray
") 生成的數據類型。以帶時區的 datetime 爲例,Numpy 未提供時區信息的 datetime 數據類型,pandas 則提供了兩種表現形式:
Timestamp
的 numpy.ndarray
"),提供了正確的 tz
信息。datetime64[ns]
,這也是 numpy.ndarray
"),值被轉換爲 UTC,但去掉了時區信息。時區信息能夠用 dtype=object
保存。
In [14]: ser = pd.Series(pd.date_range('2000', periods=2, tz="CET")) In [15]: ser.to_numpy(dtype=object) Out[15]: array([Timestamp('2000-01-01 00:00:00+0100', tz='CET', freq='D'), Timestamp('2000-01-02 00:00:00+0100', tz='CET', freq='D')], dtype=object)
或用 dtype='datetime64[ns]'
去除。
In [16]: ser.to_numpy(dtype="datetime64[ns]") Out[16]: array(['1999-12-31T23:00:00.000000000', '2000-01-01T23:00:00.000000000'], dtype='datetime64[ns]')
獲取 DataFrame
裏的原數據略顯複雜。DataFrame 裏全部列的數據類型都同樣時,DataFrame.to_numpy()
返回底層數據:
In [17]: df.to_numpy() Out[17]: array([[-0.1732, 0.1192, -1.0442], [-0.8618, -2.1046, -0.4949], [ 1.0718, 0.7216, -0.7068], [-1.0396, 0.2719, -0.425 ], [ 0.567 , 0.2762, -1.0874], [-0.6737, 0.1136, -1.4784], [ 0.525 , 0.4047, 0.577 ], [-1.715 , -1.0393, -0.3706]])
DataFrame 爲同質型數據時,pandas 直接修改原始 ndarray
,所作修改會直接反應在數據結構裏。對於異質型數據,即 DataFrame 列的數據類型不同時,就不是這種操做模式了。與軸標籤不一樣,不能爲值的屬性賦值。
::: tip 注意
處理異質型數據時,輸出結果 ndarray
的數據類型適用於涉及的各種數據。若 DataFrame 裏包含字符串,輸出結果的數據類型就是 object
。要是隻有浮點數或整數,則輸出結果的數據類型是浮點數。
:::
之前,pandas 推薦用 Series.values
或 DataFrame.values
從 Series 或 DataFrame 裏提取數據。舊有代碼庫或在線教程裏仍在用這種操做,但其實 pandas 已經對此作出了改進,如今推薦用 .array
或 to_numpy
這兩種方式提取數據,別再用 .values
了。.values
有如下幾個缺點:
array
,仍是返回 ExtensionArray
。而 Series.array
則只返回 ExtensionArray
,且不會複製數據。Series.to_numpy()
則返回 Numpy 數組,其代價是須要複製、並強制轉換數據的值。DataFrame.values
會複製數據,並將數據的值強制轉換同一種數據類型,這是一種代價較高的操做。DataFrame.to_numpy()
則返回 Numpy 數組,這種方式更清晰,也不會把 DataFrame 裏的數據都看成一種類型。藉助 numexpr
與 bottleneck
支持庫,pandas 能夠加速特定類型的二進制數值與布爾操做。
處理大型數據集時,這兩個支持庫特別有用,加速效果也很是明顯。 numexpr
使用智能分塊、緩存與多核技術。bottleneck
是一組專屬 cython 例程,處理含 nans
值的數組時,特別快。
請看下面這個例子(DataFrame
包含 100 列 X 10 萬行數據):
操做 | 0.11.0版 (ms) | 舊版 (ms) | 提高比率 |
---|---|---|---|
df1 > df2 |
13.32 | 125.35 | 0.1063 |
df1 * df2 |
21.71 | 36.63 | 0.5928 |
df1 + df2 |
22.04 | 36.50 | 0.6039 |
強烈建議安裝這兩個支持庫,瞭解更多信息,請參閱推薦支持庫。
這兩個支持庫默認爲啓用狀態,可用如下選項設置:
0.20.0 版新增
pd.set_option('compute.use_bottleneck', False) pd.set_option('compute.use_numexpr', False)
pandas 數據結構之間執行二進制操做,要注意下列兩個關鍵點:
這兩個問題能夠同時處理,但下面先介紹怎麼分開處理。
DataFrame 支持 add()
、sub()
、mul()
、div()
及 radd()
、rsub()
等方法執行二進制操做。廣播機制重點關注輸入的 Series。經過 axis
關鍵字,匹配 index 或 columns 便可調用這些函數。
In [18]: df = pd.DataFrame({ ....: 'one': pd.Series(np.random.randn(3), index=['a', 'b', 'c']), ....: 'two': pd.Series(np.random.randn(4), index=['a', 'b', 'c', 'd']), ....: 'three': pd.Series(np.random.randn(3), index=['b', 'c', 'd'])}) ....: In [19]: df Out[19]: one two three a 1.394981 1.772517 NaN b 0.343054 1.912123 -0.050390 c 0.695246 1.478369 1.227435 d NaN 0.279344 -0.613172 In [20]: row = df.iloc[1] In [21]: column = df['two'] In [22]: df.sub(row, axis='columns') Out[22]: one two three a 1.051928 -0.139606 NaN b 0.000000 0.000000 0.000000 c 0.352192 -0.433754 1.277825 d NaN -1.632779 -0.562782 In [23]: df.sub(row, axis=1) Out[23]: one two three a 1.051928 -0.139606 NaN b 0.000000 0.000000 0.000000 c 0.352192 -0.433754 1.277825 d NaN -1.632779 -0.562782 In [24]: df.sub(column, axis='index') Out[24]: one two three a -0.377535 0.0 NaN b -1.569069 0.0 -1.962513 c -0.783123 0.0 -0.250933 d NaN 0.0 -0.892516 In [25]: df.sub(column, axis=0) Out[25]: one two three a -0.377535 0.0 NaN b -1.569069 0.0 -1.962513 c -0.783123 0.0 -0.250933 d NaN 0.0 -0.892516
還能夠用 Series 對齊多重索引 DataFrame 的某一層級。
In [26]: dfmi = df.copy() In [27]: dfmi.index = pd.MultiIndex.from_tuples([(1, 'a'), (1, 'b'), ....: (1, 'c'), (2, 'a')], ....: names=['first', 'second']) ....: In [28]: dfmi.sub(column, axis=0, level='second') Out[28]: one two three first second 1 a -0.377535 0.000000 NaN b -1.569069 0.000000 -1.962513 c -0.783123 0.000000 -0.250933 2 a NaN -1.493173 -2.385688
Series 與 Index 還支持 divmod()
") 內置函數,該函數同時執行向下取整除與模運算,返回兩個與左側類型相同的元組。示例以下:
In [29]: s = pd.Series(np.arange(10)) In [30]: s Out[30]: 0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 dtype: int64 In [31]: div, rem = divmod(s, 3) In [32]: div Out[32]: 0 0 1 0 2 0 3 1 4 1 5 1 6 2 7 2 8 2 9 3 dtype: int64 In [33]: rem Out[33]: 0 0 1 1 2 2 3 0 4 1 5 2 6 0 7 1 8 2 9 0 dtype: int64 In [34]: idx = pd.Index(np.arange(10)) In [35]: idx Out[35]: Int64Index([0, 1, 2, 3, 4, 5, 6, 7, 8, 9], dtype='int64') In [36]: div, rem = divmod(idx, 3) In [37]: div Out[37]: Int64Index([0, 0, 0, 1, 1, 1, 2, 2, 2, 3], dtype='int64') In [38]: rem Out[38]: Int64Index([0, 1, 2, 0, 1, 2, 0, 1, 2, 0], dtype='int64')
divmod()
") 還支持元素級運算:
In [39]: div, rem = divmod(s, [2, 2, 3, 3, 4, 4, 5, 5, 6, 6]) In [40]: div Out[40]: 0 0 1 0 2 0 3 1 4 1 5 1 6 1 7 1 8 1 9 1 dtype: int64 In [41]: rem Out[41]: 0 0 1 1 2 2 3 0 4 0 5 1 6 1 7 2 8 2 9 3 dtype: int64
Series 與 DataFrame 的算數函數支持 fill_value
選項,即用指定值替換某個位置的缺失值。好比,兩個 DataFrame 相加,除非兩個 DataFrame 裏同一個位置都有缺失值,其相加的和仍爲 NaN
,若是隻有一個 DataFrame 裏存在缺失值,則能夠用 fill_value
指定一個值來替代 NaN
,固然,也能夠用 fillna
把 NaN
替換爲想要的值。
下面的第 43 條代碼裏,Pandas 官檔沒有寫 df2 是哪裏來的,這裏補上,與 df 相似。
df2 = pd.DataFrame({ ....: 'one': pd.Series(np.random.randn(3), index=['a', 'b', 'c']), ....: 'two': pd.Series(np.random.randn(4), index=['a', 'b', 'c', 'd']), ....: 'three': pd.Series(np.random.randn(3), index=['a', 'b', 'c', 'd'])}) ....:
In [42]: df Out[42]: one two three a 1.394981 1.772517 NaN b 0.343054 1.912123 -0.050390 c 0.695246 1.478369 1.227435 d NaN 0.279344 -0.613172 In [43]: df2 Out[43]: one two three a 1.394981 1.772517 1.000000 b 0.343054 1.912123 -0.050390 c 0.695246 1.478369 1.227435 d NaN 0.279344 -0.613172 In [44]: df + df2 Out[44]: one two three a 2.789963 3.545034 NaN b 0.686107 3.824246 -0.100780 c 1.390491 2.956737 2.454870 d NaN 0.558688 -1.226343 In [45]: df.add(df2, fill_value=0) Out[45]: one two three a 2.789963 3.545034 1.000000 b 0.686107 3.824246 -0.100780 c 1.390491 2.956737 2.454870 d NaN 0.558688 -1.226343
與上一小節的算數運算相似,Series 與 DataFrame 還支持 eq
、ne
、lt
、gt
、le
、ge
等二進制比較操做的方法:
序號 | 縮寫 | 英文 | 中文 |
---|---|---|---|
1 | eq | equal to | 等於 |
2 | ne | not equal to | 不等於 |
3 | lt | less than | 小於 |
4 | gt | greater than | 大於 |
5 | le | less than or equal to | 小於等於 |
6 | ge | greater than or equal to | 大於等於 |
In [46]: df.gt(df2) Out[46]: one two three a False False False b False False False c False False False d False False False In [47]: df2.ne(df) Out[47]: one two three a False False True b False False False c False False False d True False False
這些操做生成一個與左側輸入對象類型相同的 pandas 對象,即,dtype 爲 bool
。這些 boolean
對象可用於索引操做,參閱布爾索引小節。
empty
、any()
、all()
、bool()
能夠把數據彙總簡化至單個布爾值。
In [48]: (df > 0).all() Out[48]: one False two True three False dtype: bool In [49]: (df > 0).any() Out[49]: one True two True three True dtype: bool
還能夠進一步把上面的結果簡化爲單個布爾值。
In [50]: (df > 0).any().any() Out[50]: True
經過 empty
屬性,能夠驗證 pandas 對象是否爲空。
In [51]: df.empty Out[51]: False In [52]: pd.DataFrame(columns=list('ABC')).empty Out[52]: True
用 bool()
方法驗證單元素 pandas 對象的布爾值。
In [53]: pd.Series([True]).bool() Out[53]: True In [54]: pd.Series([False]).bool() Out[54]: False In [55]: pd.DataFrame([[True]]).bool() Out[55]: True In [56]: pd.DataFrame([[False]]).bool() Out[56]: False
::: danger 警告
如下代碼:
>>> if df: ... pass
或
>>> df and df2
上述代碼試圖比對多個值,所以,這兩種操做都會觸發錯誤:
ValueError: The truth value of an array is ambiguous. Use a.empty, a.any() or a.all().
:::
瞭解詳情,請參閱各類坑小節的內容。
通常狀況下,多種方式都能得出相同的結果。以 df + df
與 df * 2
爲例。應用上一小節學到的知識,測試這兩種計算方式的結果是否一致,通常人都會用 (df + df == df * 2).all()
,不過,這個表達式的結果是 False
:
In [57]: df + df == df * 2 Out[57]: one two three a True True False b True True True c True True True d False True True In [58]: (df + df == df * 2).all() Out[58]: one False two True three False dtype: bool
注意:布爾型 DataFrame df + df == df * 2
中有 False
值!這是由於兩個 NaN
值的比較結果爲不等:
In [59]: np.nan == np.nan Out[59]: False
爲了驗證數據是否等效,Series 與 DataFrame 等 N 維框架提供了 equals()
方法,,用這個方法驗證 NaN
值的結果爲相等。
In [60]: (df + df).equals(df * 2) Out[60]: True
注意:Series 與 DataFrame 索引的順序必須一致,驗證結果才能爲 True
:
In [61]: df1 = pd.DataFrame({'col': ['foo', 0, np.nan]}) In [62]: df2 = pd.DataFrame({'col': [np.nan, 0, 'foo']}, index=[2, 1, 0]) In [63]: df1.equals(df2) Out[63]: False In [64]: df1.equals(df2.sort_index()) Out[64]: True
用標量值與 pandas 數據結構對比數據元素很是簡單:
In [65]: pd.Series(['foo', 'bar', 'baz']) == 'foo' Out[65]: 0 True 1 False 2 False dtype: bool In [66]: pd.Index(['foo', 'bar', 'baz']) == 'foo' Out[66]: array([ True, False, False])
pandas 還能對比兩個等長 array 對象裏的數據元素:
In [67]: pd.Series(['foo', 'bar', 'baz']) == pd.Index(['foo', 'bar', 'qux']) Out[67]: 0 True 1 True 2 False dtype: bool In [68]: pd.Series(['foo', 'bar', 'baz']) == np.array(['foo', 'bar', 'qux']) Out[68]: 0 True 1 True 2 False dtype: bool
對比不等長的 Index
或 Series
對象會觸發 ValueError
:
In [55]: pd.Series(['foo', 'bar', 'baz']) == pd.Series(['foo', 'bar']) ValueError: Series lengths must match to compare In [56]: pd.Series(['foo', 'bar', 'baz']) == pd.Series(['foo']) ValueError: Series lengths must match to compare
注意: 這裏的操做與 Numpy 的廣播機制不一樣:
In [69]: np.array([1, 2, 3]) == np.array([2]) Out[69]: array([False, True, False])
Numpy 沒法執行廣播操做時,返回 False
:
In [70]: np.array([1, 2, 3]) == np.array([1, 2]) Out[70]: False
有時會合並兩個近似數據集,兩個數據集中,其中一個的數據比另外一個多。好比,展現特定經濟指標的兩個數據序列,其中一個是「高質量」指標,另外一個是「低質量」指標。通常來講,低質量序列可能包含更多的歷史數據,或覆蓋更廣的數據。所以,要合併這兩個 DataFrame 對象,其中一個 DataFrame 中的缺失值將按指定條件用另外一個 DataFrame 裏相似標籤中的數據進行填充。要實現這一操做,請用下列代碼中的 combine_first()
函數。
In [71]: df1 = pd.DataFrame({'A': [1., np.nan, 3., 5., np.nan], ....: 'B': [np.nan, 2., 3., np.nan, 6.]}) ....: In [72]: df2 = pd.DataFrame({'A': [5., 2., 4., np.nan, 3., 7.], ....: 'B': [np.nan, np.nan, 3., 4., 6., 8.]}) ....: In [73]: df1 Out[73]: A B 0 1.0 NaN 1 NaN 2.0 2 3.0 3.0 3 5.0 NaN 4 NaN 6.0 In [74]: df2 Out[74]: A B 0 5.0 NaN 1 2.0 NaN 2 4.0 3.0 3 NaN 4.0 4 3.0 6.0 5 7.0 8.0 In [75]: df1.combine_first(df2) Out[75]: A B 0 1.0 NaN 1 2.0 2.0 2 3.0 3.0 3 5.0 4.0 4 3.0 6.0 5 7.0 8.0
上述 combine_first()
方法調用了更普適的 DataFrame.combine()
方法。該方法提取另外一個 DataFrame 及合併器函數,並將之與輸入的 DataFrame 對齊,再傳遞與 Series 配對的合併器函數(好比,名稱相同的列)。
下面的代碼復現了上述的 combine_first()
函數:
In [76]: def combiner(x, y): ....: return np.where(pd.isna(x), y, x) ....: