如何将数据框中所有非NaN条目替换为1,所有NaN替换为0?

35

我有一个拥有71个列和30597行的数据框。我想将所有非nan值替换为1,将nan值替换为0。

最初,我尝试使用for循环遍历数据框中的每个值,但这花费了太多时间。

然后我使用了data_new=data.subtract(data),意在将数据框的所有值减去自身以使所有非空值变为0。 但由于数据框中存在多个字符串条目,所以出现了一个错误。


10个回答

58
您可以获取df.notnull()的返回值,其中DataFrame包含NaN时为False,否则为True,然后将其转换为整数,这将使得DataFrame中为NaN的位置变为0,其他位置为1:
newdf = df.notnull().astype('int')

如果你真的想要写入原始DataFrame,可以这样做:

df.loc[~df.isnull()] = 1  # not nan
df.loc[df.isnull()] = 0   # nan

抱歉,请不要抄我的答案。我认为你的版本和我的一样,所以我觉得没必要。 - jezrael
5
你注意到我比你先发布了这个回答吗? - fmarc
是的,但不是用 notnull()。你的答案是用 ~isnull()。而且它是相等的,所以我认为你的解决方案和我的一样好。 - jezrael
你说得对,我们两个答案基本上以相同的方式实现了相同的效果。我试图通过去掉不必要的双重倒置来改善我的答案。 - fmarc
是的,我理解你。但不幸的是,如果有更好的答案,我认为抄袭是不好的。您可以给它投赞成票 -;) [在SO上我只做了两次,因为我知道自己排名较低 :( ] - jezrael

24

使用notnull函数,通过astype将布尔值转换为int类型:

print ((df.notnull()).astype('int'))

示例:

import pandas as pd
import numpy as np

df = pd.DataFrame({'a': [np.nan, 4, np.nan], 'b': [1,np.nan,3]})
print (df)
     a    b
0  NaN  1.0
1  4.0  NaN
2  NaN  3.0

print (df.notnull())
       a      b
0  False   True
1   True  False
2  False   True

print ((df.notnull()).astype('int'))
   a  b
0  0  1
1  1  0
2  0  1

1
如果您对此答案的反向感兴趣:(df.isnull()).astype('int') - Chidi

4
我建议创建一个新的列而不是直接替换。如果需要,您可以随时删除以前的列,但通过对另一个列进行操作填充的列具有来源始终是有帮助的。
例如,如果df ['col1']是现有列。
df['col2'] = df['col1'].apply(lambda x: 1 if not pd.isnull(x) else np.nan)

其中col2是新列。如果col2有字符串条目,也应该有效。


4
我经常进行数据分析,对于寻找新的/更快的操作方法很感兴趣。我以前从未听说过jezrael的方法,因此我很想将其与我的常规方法(即通过索引替换)进行比较。注意:这不是对OP问题的回答,而是jezrael方法效率的说明。由于这不是一个答案,如果人们认为它没有用处(并被投票打入冷宫!),我会删除这篇文章。如果您认为我应该删除它,请留下评论。
我创建了一个中等大小的数据框,并使用df.notnull().astype(int)方法和简单索引(我通常会这样做)进行多次替换。结果表明,后者慢了约五倍。这只是提醒任何进行大规模替换的人。
from __future__ import division, print_function

import numpy as np
import pandas as pd
import datetime as dt


# create dataframe with randomly place NaN's
data = np.ones( (1e2,1e2) )
data.ravel()[np.random.choice(data.size,data.size/10,replace=False)] = np.nan

df = pd.DataFrame(data=data)

trials = np.arange(100)


d1 = dt.datetime.now()

for r in trials:
    new_df = df.notnull().astype(int)

print( (dt.datetime.now()-d1).total_seconds()/trials.size )


# create a dummy copy of df.  I use a dummy copy here to prevent biasing the 
# time trial with dataframe copies/creations within the upcoming loop
df_dummy = df.copy()

d1 = dt.datetime.now()

for r in trials:
    df_dummy[df.isnull()] = 0
    df_dummy[df.isnull()==False] = 1

print( (dt.datetime.now()-d1).total_seconds()/trials.size )

这分别产生了0.142秒和0.685秒的时间。 显然获胜者是谁。


2

在DataFrames中有一个方法.fillna(),可以完成你需要的功能。例如:

df = df.fillna(0)  # Replace all NaN values with zero, returning the modified DataFrame

或者

df.fillna(0, inplace=True)   # Replace all NaN values with zero, updating the DataFrame directly

这只是部分回答了问题,问题是关于替换NaN和非NaN的。 - cglacet

2
对于fmarc的回答:
df.loc[~df.isnull()] = 1  # not nan
df.loc[df.isnull()] = 0   # nan

上面的代码对我不起作用,下面的代码可以。

df[~df.isnull()] = 1  # not nan
df[df.isnull()] = 0   # nan

使用 pandas 0.25.3 版本

如果您只想更改特定列中的值,可能需要创建一个临时数据帧,并将其分配给原始数据帧的列:

change_col = ['a', 'b']
tmp = df[change_col]
tmp[tmp.isnull()]='xxx'
df[change_col]=tmp

2
尝试这个:
df.notnull().mul(1)

1
在这里,我提供一个建议,即选取特定的列,如果该列中的行为NaN,则将其替换为0,如果该列中存在值,则将其替换为1。
下面这行代码将把你的列更改为0。
df.YourColumnName.fillna(0,inplace=True)

现在,以下代码将用1替换不是NaN的其余部分。
df["YourColumnName"]=df["YourColumnName"].apply(lambda x: 1 if x!=0 else 0)

同样的方法也可以应用于整个数据框,不需要定义列名。

开始时值为0怎么办? - B. Go
@B.Go,这将把NaN值更改为0以检查第二行的0。我尝试使用第二行作为if 'NaN'进行检查,但那是一个错误。你不能像那样检查NaN,你需要使用Numpy。所以这对我来说是最简单的方法。 - arshad anzar

0
使用:df.fillna(0)来将NaN填充为0。

0

一般来说有两个步骤-先替换所有非NAN值,然后替换所有NAN值。

  1. dataframe.where(~dataframe.notna(), 1) - 这行代码将把所有非nan值替换为1。
  2. dataframe.fillna(0) - 这行代码将把所有NAN替换为0

附注:如果您查看pandas文档,.where会替换所有值,即False-这很重要。因此,我们使用反转来创建一个掩码~dataframe.notna(),用于.where()替换值


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