在 Pandas DataFrame 中用 None 替换无效值
Posted
技术标签:
【中文标题】在 Pandas DataFrame 中用 None 替换无效值【英文标题】:Replace invalid values with None in Pandas DataFrame 【发布时间】:2013-06-10 10:31:51 【问题描述】:有没有什么方法可以在 Python 的 Pandas 中用None
替换值?
您可以使用df.replace('pre', 'post')
并且可以将一个值替换为另一个值,但是如果您想用None
值替换,则无法这样做,如果您尝试这样做,您会得到一个奇怪的结果。
下面是一个例子:
df = DataFrame(['-',3,2,5,1,-5,-1,'-',9])
df.replace('-', 0)
返回一个成功的结果。
但是,
df.replace('-', None)
返回以下结果:
0
0 - // this isn't replaced
1 3
2 2
3 5
4 1
5 -5
6 -1
7 -1 // this is changed to `-1`...
8 9
为什么会返回这么奇怪的结果?
由于我想将此数据框倒入 mysql 数据库中,因此我不能将 NaN
值放入数据框中的任何元素中,而是要放入 None
。当然,您可以先将'-'
更改为NaN
,然后将NaN
转换为None
,但我想知道为什么数据框会以如此糟糕的方式运行。
在 Python 2.7 和 OS X 10.8 上的 pandas 0.12.0 dev 上测试。 Python 是一个 OS X 上的预安装版本,我使用 SciPy 安装了 pandas Superpack 脚本,供您参考。
【问题讨论】:
write_frame
不会将NaN
s 解析为none
s 吗?
是的。您遇到InternalError: (1054, u"Unknown column 'nan' in 'field list'")
错误。除了在执行write_frame
方法之前将NaN
转换为None
之外,我不知道任何解决方案。
你用的是什么版本的熊猫?
Scipy 超级包发布开发?好的,我绝对认为你应该 raise this as an issue on github,不应该太难修复。
如果您从 CSV/Excel 读取此数据,您可以使用 na_values
参数将这些值读取为 NaN。 More information in this answer.
【参考方案1】:
实际上在更高版本的 pandas 中,这会产生 TypeError:
df.replace('-', None)
TypeError: If "to_replace" and "value" are both None then regex must be a mapping
您可以通过传递列表或字典来做到这一点:
In [11]: df.replace('-', df.replace(['-'], [None]) # or .replace('-', 0: None)
Out[11]:
0
0 None
1 3
2 2
3 5
4 1
5 -5
6 -1
7 None
8 9
但我建议使用 NaN 而不是 None:
In [12]: df.replace('-', np.nan)
Out[12]:
0
0 NaN
1 3
2 2
3 5
4 1
5 -5
6 -1
7 NaN
8 9
【讨论】:
或者只是一个列表,例如df.replace(['-'], [None])
,或df.replace('-': None)
,我想。使用None
作为哨兵也排除了将其用作值..
@user2360798 replace 实际上是一个功能非常丰富(读取复杂)的功能,不过(dev)docstring 确实不错。
我不知道这是否明显,但必须将df
分配回自身,例如:df = df.replace('?': np.nan)
@AndyHayden df.replace('-', df.replace(['-'], [None])
看起来很时髦,是错字吗?
@lin_bug 虽然它似乎在最近的熊猫版本中不再有效。 df.where(df!='-', None) 有效【参考方案2】:
where
可能是您正在寻找的。所以
data=data.where(data=='-', None)
来自panda docs:
where
[返回] 一个与 self 形状相同的对象,其对应条目来自 self ,其中 cond 为 True,否则来自 other)。
【讨论】:
这实际上是不准确的。 data=data.where(data=='-', None) 将用 None 替换任何不等于 '-' 的内容。 Pandas 版本 where 保留第一个 arg 的值(在本例中为 data=='-'),并用第二个 arg 替换其他任何内容(在本例中为 None)。这有点令人困惑,因为 np.where 更明确,因为它在第一个 arg 中询问条件,然后在第二个 arg 中询问 if true,然后在第三个 arg 中询问 if false。【参考方案3】:我更喜欢使用replace
和dict
的解决方案,因为它简单而优雅:
df.replace('-': None)
你也可以有更多的替代品:
df.replace('-': None, 'None': None)
即使对于较大的替换,用什么替换什么总是显而易见的——在我看来,这对于长列表来说更难。
【讨论】:
值得注意的是,这种技术之所以有效,部分原因是在to_replace
中使用dict
类型会导致method
参数不被评估,因此method='pad'
默认值没有不良影响。【参考方案4】:
df = pd.DataFrame(['-',3,2,5,1,-5,-1,'-',9])
df = df.where(df!='-', None)
【讨论】:
【参考方案5】:设置空值可以通过np.nan
:
import numpy as np
df.replace('-', np.nan)
优点是df.last_valid_index()
将这些识别为无效。
【讨论】:
【参考方案6】:在继续这篇文章之前,了解the difference between NaN and None很重要。一个是浮点类型,另一个是对象类型。 Pandas 更适合使用标量类型,因为这些类型的许多方法都可以向量化。 Pandas 确实尝试一致地处理 None 和 NaN,但 NumPy 不能。
我的建议 (and Andy's) 是坚持使用 NaN。
但是要回答你的问题...
pandas >= 0.18:使用na_values=['-']
参数和read_csv
如果您从 CSV/Excel 加载此数据,我有个好消息要告诉您。您可以在数据加载期间从根目录取消它,而不必在后续步骤中使用代码编写修复程序。
大多数pd.read_*
函数(例如read_csv
和read_excel
)都接受na_values
属性。
file.csv
A,B
-,1
3,-
2,-
5,3
1,-2
-5,4
-1,-1
-,0
9,0
现在,要将 -
字符转换为 NaN,请执行以下操作,
import pandas as pd
df = pd.read_csv('file.csv', na_values=['-'])
df
A B
0 NaN 1.0
1 3.0 NaN
2 2.0 NaN
3 5.0 3.0
4 1.0 -2.0
5 -5.0 4.0
6 -1.0 -1.0
7 NaN 0.0
8 9.0 0.0
其他函数/文件格式也类似。
P.S.:在 v0.24+ 上,即使您的列有 NaN,您也可以保留整数类型(是的,谈论吃蛋糕和吃蛋糕)。你可以指定dtype='Int32'
df = pd.read_csv('file.csv', na_values=['-'], dtype='Int32')
df
A B
0 NaN 1
1 3 NaN
2 2 NaN
3 5 3
4 1 -2
5 -5 4
6 -1 -1
7 NaN 0
8 9 0
df.dtypes
A Int32
B Int32
dtype: object
dtype 不是传统的 int 类型...而是Nullable Integer Type. 还有其他选项。
处理数字数据:pd.to_numeric
和 errors='coerce
如果您正在处理数字数据,更快的解决方案是使用 pd.to_numeric
和 errors='coerce'
参数,它将无效值(不能转换为数字的值)强制转换为 NaN。
pd.to_numeric(df['A'], errors='coerce')
0 NaN
1 3.0
2 2.0
3 5.0
4 1.0
5 -5.0
6 -1.0
7 NaN
8 9.0
Name: A, dtype: float64
要保留(可为空的)整数 dtype,请使用
pd.to_numeric(df['A'], errors='coerce').astype('Int32')
0 NaN
1 3
2 2
3 5
4 1
5 -5
6 -1
7 NaN
8 9
Name: A, dtype: Int32
要强制多列,请使用apply
:
df[['A', 'B']].apply(pd.to_numeric, errors='coerce').astype('Int32')
A B
0 NaN 1
1 3 NaN
2 2 NaN
3 5 3
4 1 -2
5 -5 4
6 -1 -1
7 NaN 0
8 9 0
...然后将结果分配回去。
更多信息可以在this answer找到。
【讨论】:
【参考方案7】:使用替换并分配一个新的df:
import pandas as pd
df = pd.DataFrame(['-',3,2,5,1,-5,-1,'-',9])
dfnew = df.replace('-', 0)
print(dfnew)
(venv) D:\assets>py teste2.py
0
0 0
1 3
2 2
3 5
4 1
5 -5
【讨论】:
【参考方案8】:df.replace('-', np.nan).astype("object")
这将确保您以后可以在数据帧上使用isnull()
【讨论】:
【参考方案9】:对于 Pandas 版本 ≥1.0.0,我会使用 DataFrame.replace
或 Series.replace
:
df.replace(old_val, pd.NA, inplace=True)
这更好有两个原因:
-
它使用
pd.NA
而不是None
或np.nan
。
它可以选择就地工作,这可能会根据内部实现更有效地使用内存。
【讨论】:
以上是关于在 Pandas DataFrame 中用 None 替换无效值的主要内容,如果未能解决你的问题,请参考以下文章
Pandas Group/Merge Dataframe by Non-Periodic Series
在 Pandas Dataframe 中用字符串交换双精度的优雅方法?
如何使用布尔掩码在 pandas DataFrame 中用 nan 替换“任何字符串”?