带有NaN支持的Pandas lambda函数

19
我正在尝试在Pandas中编写一个lambda函数,检查Col1是否为NaN,如果是,则使用另一列的数据。我在编译/执行下面的代码时遇到了问题。
import pandas as pd
import numpy as np

df = pd.DataFrame({'Col1': [1, 2, 3, np.NaN], 'Col2': [7, 8, 9, 10]})  
df2 = df.apply(lambda x: x['Col2'] if x['Col1'].isnull() else x['Col1'], axis=1)

有没有人对如何使用lambda函数编写这样的解决方案有好的想法?或者我已经超出了lambda的能力范围?如果没有,你有其他的解决方案吗?

2
你的示例只有一列。如果数据集中没有Col2,那么你就不能从Col2中获取数据;此外,通常情况下,如果没有其他列,你也无法获取“另一列的数据”。 - Arya McCarthy
@aryamccarthy 抱歉。我应该创建一个任意的“Col2”。我会测试并回来。 - Tyler Russell
这在我的大型DataFrame上似乎不起作用,但在示例中可以。这可能是因为我的实际数据集具有不同的数据类型,因此fillna无法正确工作吗?我的实际数据集中的Col1和Col2都是dtype('O'),所以这不应该是个问题。 - Tyler Russell
5个回答

45
你需要使用pandas.isnull来检查一个标量是否为NaN
df = pd.DataFrame({'Col1': [1, 2, 3, np.NaN],
                   'Col2': [8, 9, 7, 10]})  
                 
df2 = df.apply(lambda x: x['Col2'] if pd.isnull(x['Col1']) else x['Col1'], axis=1)

print(df)
   Col1  Col2
0   1.0     8
1   2.0     9
2   3.0     7
3   NaN    10

print(df2)
0     1.0
1     2.0
2     3.0
3    10.0
dtype: float64

但更好的是使用{{link1:Series.combine_first}}:
df['Col1'] = df['Col1'].combine_first(df['Col2'])

print(df)
   Col1  Col2
0   1.0     8
1   2.0     9
2   3.0     7
3  10.0    10

另一个使用Series.update的解决方案:
df['Col1'].update(df['Col2'])
print(df)
   Col1  Col2
0   8.0     8
1   9.0     9
2   7.0     7
3  10.0    10

1
嗯,我认为这是 Col2 - 这意味着如果条件为真,则获取 col2 的值,否则获取 col1 的值。 - jezrael
1
但是如果需要将NaN替换为另一列,我更喜欢其他解决方案。 - jezrael
1
因为它更快且具有向量化函数。但如果数据框很小(100行),那就没有问题。但如果有1M行,差别就非常大了。 - jezrael
为什么要使用combine_first而不是fillna?在这种情况下,combine_first适用是因为这两个系列具有相同的索引,但在其他情况下,你可能会得到一个更长的系列,因为“结果索引将是两个索引的并集”(来自文档)。 - undefined
1
@wjandrea - 是的,使用相同的DataFrame,所以索引也是相同的。一般来说,你是对的,应该使用fillna函数。 - undefined
显示剩余4条评论

7
假设您确实有第二列,即:
df = pd.DataFrame({'Col1':[1,2,3,np.NaN],'Col2':[1,2,3,4]})
解决此问题的正确方法是:
df['Col1'].fillna(df['Col2'], inplace=True)

抱歉,我应该创建一个任意的“Col2”。我会测试并回来。 - Tyler Russell
这在我的大型DataFrame上似乎不起作用,但在示例中可以。这可能是因为我的实际数据集具有不同的数据类型,因此fillna无法正确工作吗?我的实际数据集中的Col1和Col2都是dtype('O'),所以这不应该是个问题。 - Tyler Russell
1
对于对象数据类型,这对我有效。当您使用实际数据集时出现了什么问题? - Gerges

3

你需要使用np.nan()。

#import numpy as np
df2=df.apply(lambda x: 2 if np.isnan(x['Col1']) else 1, axis=1)   

df2
Out[1307]: 
0    1
1    1
2    1
3    2
dtype: int64

我试图对非NaN值进行四舍五入,而这个语句可以工作,而x is np.NaN则不能: df.age.apply(lambda x: x if np.isnan(x) else round(x)) - Rafs
@Rafs 你应该使用Pandas内置的函数,它们可以自动处理NaN:df['age'].round()。而且它们速度更快。其次,一般情况下不要对数字使用is;参见numpy NaN not always recognized(虽然标题听起来是关于NaN的,但答案解释了更一般的问题)。 - undefined

2
在pandas 0.24.2中,我使用:

df.apply(lambda x: x['col_name'] if x[col1] is np.nan else expressions_another, axis=1)

因为 pd.isnull() 无法正常工作。

在我的工作中,我发现了以下现象,

没有运行结果:

df['prop'] = df.apply(lambda x: (x['buynumpday'] / x['cnumpday']) if pd.isnull(x['cnumpday']) else np.nan, axis=1)

已存在结果:

df['prop'] = df.apply(lambda x: (x['buynumpday'] / x['cnumpday']) if x['cnumpday'] is not np.nan else np.nan, axis=1)

到目前为止,我仍然不知道更深层次的原因,但是我有这些经验,例如,对于对象,请使用 [is np.nan()] 或 pd.isna()。对于浮点数,请使用 np.isnan() 或 pd.isna()。


对于is np.nan(),你是指is np.nan,对吗?我不确定你是否可能是指np.isnan() - undefined
pd.isnull()对你来说不起作用,因为你只是打错了字:if pd.isnull(...)应该是if not pd.isnull(...)。关于is np.nan,这并不能保证起作用;参见numpy NaN not always recognized以及dataframe.apply(lambda x: x is np.nan) does not work。如果你的列是object类型,这就是为什么它偶尔会起作用,但是再次强调,这并不是保证的,例如:s = pd.Series([1, np.nan, float('nan')], dtype='object'); s.apply(lambda x: x is np.nan).values[False True False] vs s.isna().values[False True True] - undefined

0
你还可以使用numpy函数where,条件是"Col1是NaN"。
import pandas as pd
import numpy as np

df = pd.DataFrame({'Col1': [1, 2, 3, np.NaN], 'Col2': [7, 8, 9, 10]})

df['Col1'] = np.where(pd.isna(df['Col1']), df['Col2'], df['Col1'])

为什么?你可以直接使用.fillna。即使你不想使用.fillna,为什么要使用NumPy而不是Pandas呢?df['Col1'].mask(pd.isna(df['Col1']), df['Col2']) - undefined
感谢您的纠正。短语“equals NaN”可以更正为“is NaN”,原因是这是一种常见的方法,根据特定条件将一列的值更改为另一列的值。 - undefined
对,我知道这是常见的,但为什么不使用更简单的方法呢? - undefined
1
我建议采用另一种方法来处理填充缺失值的问题。在我的实践中,存在其他类型的缺失值,不仅仅是'n/a'。我的建议更加通用,可以处理各种类型的缺失值或不正确的值。 - undefined
Series.mask 也可以做到这个吗? - undefined
1
是的,可以,但是关于运行时间呢?我的测试显示df掩码的运行时间为132微秒±455,而numpy的运行时间为81.6微秒±267。 - undefined

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