微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

熊猫DataFrame上的NaN替换引发TypeError:找不到匹配的签名

如何解决熊猫DataFrame上的NaN替换引发TypeError:找不到匹配的签名

目的

我有一个具有不同dtypes的大型DataFrame,必须执行全局.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或日期字段的空字符串),而无需进行某些设置更改(我想避免这种更改)。我认为这种方法比做字典时单独检查每一行要快。

方法

Converting all columns to object由于DataFrame的大小,在替换之前甚至无法运行-我宁愿完全不转换任何列。一种曾经有效的方法

df.fillna('').replace('',None)

但是现在,在其中添加一些类别dtype,它会引发TypeError: No matching signature found

问题

搜索内容,我发现没有任何东西与pandas相关。它显然与类别dtype¹相关,但是我不知道:


¹编辑:

我后来发现,在这种情况下,pandas实现替换达到了Cython编译的方法pandas._libs.algos.pad_inplace -该方法希望填充除category之外的任何Series dtype。这就是为什么我的错误提到签名不匹配的原因。我仍然想知道这是否是预期的行为,因为我希望填充在分类列中特别有效。


由于我的数字列已经填充,因此我在此处更改了列a以反映这一点。因此,我的麻烦仅是category dtype。

解决方法

方法

对于一次性替换操作,最好避免全局转换为object,因为这在处理和内存上都是昂贵的。但是,正如@hpaul在评论中提到的,None是一个对象,而不是原始值,因此Series 必须是包含它的对象类型。例如datetime系列将始终将None变成NaT,因为这是缺少原始日期值的原始表示。对于数字dtype和NaNcategory也是如此。

鉴于此,我发现这种方法是最好的:

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))
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将所需的dtypes(数字和object本身除外)转换为object,但是这种方法更快,因为懒惰完成了转换,并且具有被熊猫隐式处理的优点。演示:

In [5]: df.dtypes
a           float64
b    datetime64[ns]
c          category
d            object
dtype: object

同时,在替换之后

In [6]: df.replace((np.nan,None)).dtypes
a    float64
b     object
c     object
d     object
dtype: object

float64列没有可替换的空值,因此它根本没有变化。

请注意,这与.replace(np.nan,None).replace('',None) 不相同,这将导致相同的TypeError,因为...

为什么

发生TypeError的原因可以追溯到熊猫的默认替换方法的Cython实现,该方法称为填充或正向填充。但这也与API选择有关:

  • Cython问题:在这种情况下(pandas._libs.algos.pad_inplace)调用的方法希望填充除category之外的所有Seri​​es dtype,这就是为什么错误提到 signature 不匹配的原因。
  • API的不确定性:将None用作位置参数可能会产生误导-熊猫将其视为“您将任何内容作为替换值” ”,而不是“您不传递任何内容作为替换值”。

请注意将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('')是相同的,并且熊猫的API已采取了假设上述方法是此操作所寻求的行为方式的一种方法,即纯正向填充。除非有解释,否则不适用于category dtypes。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。