如何解决熊猫DataFrame上的NaN替换引发TypeError:找不到匹配的签名
目的
我有一个具有不同dtypes的大型DataFrame,必须执行全局.replace
才能将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¹相关,但是我不知道:
-
在保持所有列especially the categorical ones的完整性的同时,最Python化的方法是什么?
-
大熊猫在.replace
中引发这种明显的通用错误的幕后发生了什么?
¹编辑:
我后来发现,在这种情况下,pandas实现替换达到了Cython编译的方法pandas._libs.algos.pad_inplace
-该方法希望填充除category
之外的任何Series dtype。这就是为什么我的错误提到签名不匹配的原因。我仍然想知道这是否是预期的行为,因为我希望填充在分类列中特别有效。
由于我的数字列已经填充,因此我在此处更改了列a
以反映这一点。因此,我的麻烦仅是category
dtype。
解决方法
方法
对于一次性替换操作,最好避免全局转换为object
,因为这在处理和内存上都是昂贵的。但是,正如@hpaul在评论中提到的,None
是一个对象,而不是原始值,因此Series 必须是包含它的对象类型。例如datetime
系列将始终将None
变成NaT
,因为这是缺少原始日期值的原始表示。对于数字dtype和NaN
,category
也是如此。
鉴于此,我发现这种方法是最好的:
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
之外的所有Series 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 举报,一经查实,本站将立刻删除。