[英]NaN replace on pandas DataFrame raises TypeError: No matching signature found
我有一個大型 DataFrame 具有不同的 dtypes,我必須執行全局.replace
以將NaN、NaT 和空字符串都轉換為None
。 DataFrame 看起來像
import pandas as pd
from datetime import datetime
df = pd.DataFrame({
'a': [n*10.0 for n in range(5)],
'b': [datetime.now() if n%3 else None for n in range(5)],
'c': pd.Series([f'D{n}' if n%2 else '' for n in range(5)], dtype='category'),
'd': ['Long text chunk...' if n%3 else None for n in range(5)]
})
哪個打印
a b c d
0 0.0 NaT None
1 10.0 2020-08-13 23:35:55.533189 D1 Long text chunk...
2 20.0 2020-08-13 23:35:55.533189 Long text chunk...
3 30.0 NaT D3 None
4 40.0 2020-08-13 23:35:55.533189 Long text chunk...
我的目的是將行批量上傳到 ElasticSearch 中,它不會接受 NaN - 既不 NaT 也不接受日期字段的空字符串 - 沒有我試圖避免的一些設置更改。 我認為這種方式比在制作字典時單獨檢查每一行要快。
由於 DataFrame 大小,在替換之前將所有列轉換為object
甚至無法運行 - 我寧願根本不轉換任何列。 曾經奏效的一種方法是
df.fillna('').replace('', None)
但是現在,添加一些類別 dtypes,它會引發TypeError: No matching signature found
。
搜索這個,我發現沒有任何東西與pandas
有關。 它顯然與類別 dtype¹相關,但我不知道:
在保持所有列(尤其是分類列)的完整性的同時,最pythonic的方式是什么?
pandas 在
.replace
中引發這個明顯的通用錯誤的幕后會發生什么?
¹ 編輯:
我后來發現 pandas 實現替換在這種情況下達到了 Cython 編譯的方法 - pandas._libs.algos.pad_inplace
- 它期望填充除category
之外的任何 Series dtype。 這就是為什么我的錯誤提到簽名不匹配的原因。 我仍然想知道這是否是預期的行為,因為我希望 ffill 在分類列中特別有效。
由於我的數字列已經填滿,我在這里更改a
列以反映這一點。 所以我的麻煩只是category
dtype。
對於一次性替換操作,最好避免將全局轉換為object
,因為這在處理和內存方面的成本很高。 但是,正如@hpaul 在評論中提到的, None
是 object 而不是原始值,因此 Series必須是 object 類型才能包含它。 例如, datetime
時間系列將始終將None
轉換為NaT
,因為這是缺少原始日期值的原始表示。 與數字 dtypes 和category
的NaN
一樣。
鑒於此,我發現這種方法是最好的:
df.replace((np.nan, ''), (None, None))
結果,我們得到:
a b c d
0 0.0 None None None
1 10.0 2020-08-14 01:09:41.936421 D1 Long text chunk...
2 20.0 2020-08-14 01:09:41.936421 None Long text chunk...
3 30.0 None D3 None
4 40.0 2020-08-14 01:09:41.936421 None Long text chunk...
由於事先也不依賴.astype
或.fillna
,這比其他方法更安全(更好的轉換¹)和更高的性能:
In [2]: %timeit -n 1000 df.replace((np.nan, ''), (None, None))
1.32 ms ± 47.8 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In [3]: %timeit -n 1000 df.replace({np.nan: None, '': None})
# ^ pandas translates this into the first call,
# taking a few more milliseconds
1.36 ms ± 38.9 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In [4]: %timeit -n 1000 df.astype(object).where(df.notnull(), None).where(df != '', None)
2.83 ms ± 78.2 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
¹ pandas converts the dtypes it needs to (anything other than numerics and object
itself) into object
, but this method is faster because conversion is lazily done, and has the advantage of being implicitly handled by pandas. 一個示范:
In [5]: df.dtypes
a float64
b datetime64[ns]
c category
d object
dtype: object
同時,更換后
In [6]: df.replace((np.nan, ''), (None, None)).dtypes
a float64
b object
c object
d object
dtype: object
float64
列沒有任何要替換的空值,所以它根本沒有改變。
請注意,這與.replace(np.nan, None).replace('', None)
不同,這將導致相同的TypeError
,因為...
發生這種TypeError
的原因可以追溯到 pandas 的默認替換方法的 Cython 實現,該方法稱為填充或前向填充。 但這也與 API 選擇有關:
pandas._libs.algos.pad_inplace
)期望填充除category
之外的任何 Series dtype,這就是錯誤提到簽名不匹配的原因。None
作為位置參數傳遞可能會產生誤導 - pandas 將此視為“您沒有將任何內容作為替換值傳遞”而不是“您沒有傳遞任何內容作為替換值”。 注意將 DataFrame 轉換為object
然后使用曾經有效的相同方法時會發生什么:
In [7]: df.astype(object).fillna('').replace('', None)
a b c d
0
1 10.0 2020-08-13 21:18:42.520455 D1 Long text chunk...
2 20.0 2020-08-13 21:18:42.520455 D1 Long text chunk...
3 30.0 2020-08-13 21:18:42.520455 D3 Long text chunk...
4 40.0 2020-08-13 21:18:42.520455 D3 Long text chunk...
值已被前向填充,在c
列中可以更容易地看到。 這是因為,在實踐中, .replace('', None)
與.replace('')
相同,並且 pandas 的 API 采取了假設上述是此操作所尋求的那種行為的路線 - 一個普通的向前填充。 除非,如上所述,這不適用於category
dtype。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.