簡體   English   中英

NaN 替換 pandas DataFrame 引發 TypeError:找不到匹配的簽名

[英]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 和categoryNaN一樣。

鑒於此,我發現這種方法是最好的:

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 選擇有關:

  • Cython 問題:在這種情況下調用的方法( pandas._libs.algos.pad_inplace )期望填充除category之外的任何 Series dtype,這就是錯誤提到簽名不匹配的原因。
  • API 不確定性:將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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM