在pandas中用NaN替换空值(空格)

269

我想在Pandas数据框中查找包含空格(任意数量)的所有值,并用NaN替换这些值。

有什么更好的想法吗?

基本上,我希望将这个:

                   A    B    C
2000-01-01 -0.532681  foo    0
2000-01-02  1.490752  bar    1
2000-01-03 -1.387326  foo    2
2000-01-04  0.814772  baz     
2000-01-05 -0.222552         4
2000-01-06 -1.176781  qux     

进入这个:

                   A     B     C
2000-01-01 -0.532681   foo     0
2000-01-02  1.490752   bar     1
2000-01-03 -1.387326   foo     2
2000-01-04  0.814772   baz   NaN
2000-01-05 -0.222552   NaN     4
2000-01-06 -1.176781   qux   NaN

我已经使用以下代码成功实现了,但是太丑陋了。它不符合Python的规范,我也确定这不是pandas最有效的用法。我循环遍历每个列,并针对应用了一个函数生成的列掩码进行布尔替换,该函数对每个值执行正则表达式搜索,在空格上匹配。

for i in df.columns:
    df[i][df[i].apply(lambda i: True if re.search('^\s*$', str(i)) else False)]=None
它可以通过仅迭代可能包含空字符串的字段来进行优化:
if df[i].dtype == np.dtype('object')
但这并没有太大的改进。 最后,此代码将目标字符串设置为“None”,可以与Pandas的函数(如fillna())配合使用,但如果我能直接插入NaN而不是None,那就更完整了。

2
你真正想要的是能够使用正则表达式与 replace 一起使用...(也许应该将此作为一个功能请求)。 - Andy Hayden
3
我为这个功能创建了一个Github问题链接:https://github.com/pydata/pandas/issues/2285 。如果可以的话,欢迎提供PR(拉取请求)! :) - Chang She
对于那些想要将单个空格字符转换为缺失值的人,请参见下面的这个简单解决方案 - Ted Petrou
13个回答

335

我认为 df.replace() 能够胜任这项工作,因为自pandas 0.13以来:

df = pd.DataFrame([
    [-0.532681, 'foo', 0],
    [1.490752, 'bar', 1],
    [-1.387326, 'foo', 2],
    [0.814772, 'baz', ' '],     
    [-0.222552, '   ', 4],
    [-1.176781,  'qux', '  '],         
], columns='A B C'.split(), index=pd.date_range('2000-01-01','2000-01-06'))

# replace field that's entirely space (or empty) with NaN
print(df.replace(r'^\s*$', np.nan, regex=True))

生成:

                   A    B   C
2000-01-01 -0.532681  foo   0
2000-01-02  1.490752  bar   1
2000-01-03 -1.387326  foo   2
2000-01-04  0.814772  baz NaN
2000-01-05 -0.222552  NaN   4
2000-01-06 -1.176781  qux NaN
Temak 所指出的,如果您的有效数据包含空格,请使用df.replace(r'^\s+$', np.nan, regex=True)

1
正则表达式是一个布尔标志。也许你的意思是 pd.Series(["1", "#", "9", " .", None]).replace(r"( +\.)|#", "X", regex=True).values,它会返回 ['1', 'X', '9', 'X', None] - patricksurry
2
两年过去了,现在我已经修改了接受的答案,因为 pandas 已经支持它。谢谢! - Chris Clark
45
注意:如果您不想将中间包含空格的元素替换为NaN,请使用df.replace(r'^\s+$', np.nan, regex=True) - Temak
9
我尝试使用这个,但发现应该使用r'^\s*$'。没有^和$,它将匹配任何具有两个连续空格的字符串。还将+更改为*,以便在要转换为NaN的内容列表中包括空字符串""。 - Master Yogurt
2
我正在尝试在我的代码中使用您的解决方案,但它没有任何效果。我正在尝试“energy["Energy Supply"].replace(to_replace = "...", value = np.NaN)”将字符串“...”更改为NaN值,但它什么也没做,并返回相同的数据帧。 - CuriousLearner
显示剩余5条评论

118

如果你想要替换空字符串和仅包含空格的记录,正确答案是什么!:

df = df.replace(r'^\s*$', np.nan, regex=True)

被接受的答案

df.replace(r'\s+', np.nan, regex=True)

不能替换空字符串!您可以尝试使用稍作修改的示例:

df = pd.DataFrame([
    [-0.532681, 'foo', 0],
    [1.490752, 'bar', 1],
    [-1.387326, 'fo o', 2],
    [0.814772, 'baz', ' '],     
    [-0.222552, '   ', 4],
    [-1.176781,  'qux', ''],         
], columns='A B C'.split(), index=pd.date_range('2000-01-01','2000-01-06'))
注意,'fo o'中包含一个空格,但不会被替换为NaN。 另请注意,一个简单的:<\p>
df.replace(r'', np.NaN)

仍然无法工作-请试一下。


40

这样如何:

d = d.applymap(lambda x: np.nan if isinstance(x, basestring) and x.isspace() else x)

applymap函数会将一个函数应用到数据框中的每个单元格。


真是个不错的改进!我应该回过头来想到这一点,但由于某些原因卡在了布尔替换上。有一个问题 - 相对于只是使用str(x).isspace(),进行basestring检查是否有优势? - Chris Clark
1
@ChrisClark:两种方法都可以,不过我猜isinstance会稍微快一些。 - BrenBarn
15
上述代码中的“basestring”引用在Python 3中将无法工作...在这种情况下,请尝试改用“str”。 - Spike Williams
5
请注意,此解决方案不会替换空字符串 ''。如果还要考虑空字符串,请使用以下方式:d = d.applymap(lambda x: np.nan if isinstance(x, basestring) and (not x or x.isspace()) else x) - tuomastik
在Python 3.x中,isinstance(x, basestring)的正确等价物是isinstance(x, str) - KiriSakow

40

我做了这件事:

df = df.apply(lambda x: x.str.strip()).replace('', np.nan)
或者
df = df.apply(lambda x: x.str.strip() if isinstance(x, str) else x).replace('', np.nan)
你可以删除所有的字符串,然后用np.nan替换空字符串。

lambda x: x.str.strip() 应该改为 lambda x: x.strip()? 小建议:在前面加上 .astype(str),这可以解决我遇到的其他数据问题。这个对我有用:df=df.apply['column'].astype(str).apply(lambda x: x.strip()).replace('', np.nan) - Wouter
1
代码的第二行处理了整型/浮点型和字符串类型的列。很好。谢谢! - Kate Stohr

15

如果你要从CSV文件中导出数据,这可能就像这样简单:

df = pd.read_csv(file_csv, na_values=' ')

这将创建数据框并将空值替换为Na


7
另一个选项是使用 skipinitialspace=True,它还会删除分隔符后的任何空格,这将导致任何长度的空格和空字符串被读取为 nan。但是,如果您想保留初始空格,那么这个选项就不是一个好选择。 - Rajshekar Reddy

7

最简单的解决方案:

df = df.replace(r'^\s+$', np.nan, regex=True)

5

如果您需要对单个值进行比较,可以使用mask方法来快速简单地解决问题。

df.mask(df == ' ')

3
print(df.isnull().sum()) # check numbers of null value in each column

modifiedDf=df.fillna("NaN") # Replace empty/null values with "NaN"

# modifiedDf = fd.dropna() # Remove rows with empty values

print(modifiedDf.isnull().sum()) # check numbers of null value in each column

3
这对我很有帮助。 当我导入我的csv文件时,我添加了na_values = ' '。空格不包括在默认的NaN值中。
df= pd.read_csv(filepath,na_values = ' ')

2
这些都接近正确答案,但我不能说它们解决了问题,同时又最易于他人阅读您的代码。我认为答案是BrenBarn的答案和tuomasttik在下面的评论中提到的答案的结合体。BrenBarn的答案利用了isspace内置函数,但不支持删除空字符串,正如OP所请求的那样,我倾向于将其归因于用null替换字符串的标准用例。
我使用.apply对其进行了重写,因此您可以在pd.Seriespd.DataFrame上调用它。
Python 3:
替换空字符串或全部由空格组成的字符串:
df = df.apply(lambda x: np.nan if isinstance(x, str) and (x.isspace() or not x) else x)

替换完全为空格的字符串:
df = df.apply(lambda x: np.nan if isinstance(x, str) and x.isspace() else x)

要在Python 2中使用此代码,您需要将str替换为basestring
Python 2:
要替换空字符串或完全由空格组成的字符串:
df = df.apply(lambda x: np.nan if isinstance(x, basestring) and (x.isspace() or not x) else x)

替换全是空格的字符串:
df = df.apply(lambda x: np.nan if isinstance(x, basestring) and x.isspace() else x)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接