在Pandas DataFrame中将无效值替换为None


80

None在Python的Pandas中,是否有任何方法可以替换值?

您可以使用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,然后再转换NaNNone,但是我想知道为什么数据框以这种可怕的方式起作用。

已在Python 2.7和OS X 10.8的pandas 0.12.0开发人员上进行了测试。Python是OS X上的预装版本,我通过使用SciPy Superpack脚本安装了熊猫,以供参考。


不将swrite_frame解析NaNnones吗?
Andy Hayden 2013年

对。您遇到InternalError: (1054, u"Unknown column 'nan' in 'field list'")错误。除了执行方法之前转换NaNNone,我不知道其他解决方案write_frame
Blaszard

您使用的是哪个版本的熊猫?
Andy Hayden

Scipy Super Pack发布了dev?好的,我绝对认为您应该在github上将此问题提出来,不应太难解决。
Andy Hayden

如果您要从CSV / Excel读取此数据,则可以使用na_values参数以NaN的形式读取这些值。此答案中的更多信息。
cs95

Answers:


110

实际上,在更高版本的熊猫中,这将产生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

15
或只是一个列表,例如df.replace(['-'], [None]),或者df.replace({'-': None}),我想。使用的None是用它定点排除了作为值太..
帝斯曼

@ user2360798 replace实际上是一个功能非常丰富(读取复杂)的函数,但是(dev)docstring确实很好。
Andy Hayden 2013年

4
我不知道它是否显而易见,但必须df像这样分配给自己:df = df.replace({'?': np.nan})
luckyging3r

3
@AndyHaydendf.replace('-', df.replace(['-'], [None])看起来很时髦,是错字吗?
lin_bug

2
@lin_bug尽管在最近的熊猫版本中似乎不再起作用。df.where(DF =! ' - ',无)的作品
安迪·海登

16

我更喜欢使用replace带有a的解决方案,dict因为它既简单又优雅:

df.replace({'-': None})

您还可以有更多替代品:

df.replace({'-': None, 'None': None})

即使是较大的替换,也总是显而易见的,什么替换了-我认为对于长列表来说,这要困难得多。


1
值得注意的是,此技术起作用的部分原因是使用dictin类型to_replace会导致method不对参数求值,因此method='pad'默认值不会产生不良影响。
bsplosion,

15

where可能正是您想要的。所以

data=data.where(data=='-', None) 

熊猫文档

where [返回]一个与self形状相同的对象,并且其对应的条目来自cond为True的self,否则来自其他)。


5
这实际上是不准确的。data = data.where(data =='-',None)会将所有不等于'-'的内容替换为None。Pandas版本的where保留第一个arg的值(在这种情况下,data =='-'),并用第二个arg替换其他任何内容(在本例中为None)。np有点令人困惑。where更明确,因为它在第一个arg中询问条件,然后在第二个arg中询问是否为true,然后在第三个arg中询问是否为false。
clg4

7

在继续本文之前,重要的是要了解NaN和None之间的区别。一种是浮点类型,另一种是对象类型。熊猫更适合处理标量类型,因为可以对这些类型的许多方法进行矢量化处理。熊猫确实尝试一致地处理None和NaN,但是NumPy无法。

我的建议(和Andy的建议)是坚持使用NaN。

但是要回答你的问题...

熊猫> = 0.18:将na_values=['-']参数与read_csv

如果您是从CSV / Excel加载此数据的,那么我对您来说是个好消息。您可以在数据加载期间从根本上解决此问题,而不必在后续步骤中编写代码修复程序。

大多数pd.read_*函数(例如read_csvread_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

与其他功能/文件格式类似。

PS:在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类型。还有其他选择。


处理数值数据:pd.to_numericwitherrors='coerce

如果你正在处理的数字数据,更快的解决方案是使用pd.to_numericerrors='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

...然后将结果分配回去。

此答案中可以找到更多信息。


2
df = pd.DataFrame(['-',3,2,5,1,-5,-1,'-',9])
df = df.where(df!='-', None)

0

可以通过以下方式设置空值np.nan

import numpy as np
df.replace('-', np.nan)

优点是将df.last_valid_index()这些识别为无效。


0

使用replace并分配一个新的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

0
df.replace('-', np.nan).astype("object")

这将确保您isnull()以后可以在数据框上使用


0

在Pandas版本≥1.0.0的情况下,我将使用DataFrame.replaceSeries.replace

df.replace(old_val, pd.NA, inplace=True)

这样做有两个更好的原因:

  1. 它使用pd.NA代替Nonenp.nan
  2. 它替换了就地这可以提高内存效率。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.