Pandas无法就地使用fillna()填充空值。

23

我正在尝试在数据框的4个字符串/对象类型的特定列中使用空值填充""。当我使用fillna()为这些列分配新变量时,但是当我使用inplace参数进行fillna()操作时,底层数据不会改变。

a_n6 = a_n6[["PROV LAST", "PROV FIRST", "PROV MID", "SPEC NM"]].fillna("")
a_n6

给了我:

<class 'pandas.core.frame.DataFrame'>
Int64Index: 1542 entries, 0 to 3611
Data columns (total 4 columns):
PROV LAST     1542  non-null values
PROV FIRST    1542  non-null values
PROV MID      1542  non-null values
SPEC NM       1542  non-null values
dtypes: object(4)

但是

a_n6[["PROV LAST", "PROV FIRST", "PROV MID", "SPEC NM"]].fillna("", inplace=True)
a_n6

给了我:

<class 'pandas.core.frame.DataFrame'>
Int64Index: 1542 entries, 0 to 3611
Data columns (total 7 columns):
NPI           1103  non-null values
PIN           1542  non-null values
PROV FIRST    1541  non-null values
PROV LAST     1542  non-null values
PROV MID      1316  non-null values
SPEC NM       1541  non-null values
flag          439  non-null values
dtypes: float64(2), int64(1), object(4)

只有一行,但仍然很令人沮丧。我做错了什么?


4
我也遇到一些函数,其中inplace=True似乎被忽略了。虽然这不是你的问题,但在故障排除时值得牢记。 - Zero
5个回答

37

使用dict作为fillna()value参数

正如@Jeff答案中@rhkarls所提到的评论,使用.loc索引到列列表将不支持inplace操作,这也让我感到沮丧。以下是一个解决方法。

示例:

import pandas as pd
import numpy as np

df = pd.DataFrame({'a':[1,2,3,4,np.nan],
                   'b':[6,7,8,np.nan,np.nan],
                   'x':[11,12,13,np.nan,np.nan],
                   'y':[16,np.nan,np.nan,19,np.nan]})
print(df)
#     a    b     x     y
#0  1.0  6.0  11.0  16.0
#1  2.0  7.0  12.0   NaN
#2  3.0  8.0  13.0   NaN
#3  4.0  NaN   NaN  19.0
#4  NaN  NaN   NaN   NaN

假设我们只想为xy进行fillna,而不是ab
我期望使用.loc可以像赋值一样工作,但它并没有,正如之前提到的那样。
# doesn't work
df.loc[:,['x','y']].fillna(0, inplace=True)
print(df) # nothing changed

然而,文档指出fillna()value参数可以是:

或者是一个值的字典/Series/DataFrame,指定每个索引(对于Series)或列(对于DataFrame)使用哪个值。(不在字典/Series/DataFrame中的值将不会被填充)。

事实证明,使用值的字典是可行的:

# works
df.fillna({'x':0, 'y':0}, inplace=True)
print(df)
#     a    b     x     y
#0  1.0  6.0  11.0  16.0
#1  2.0  7.0  12.0   0.0
#2  3.0  8.0  13.0   0.0
#3  4.0  NaN   0.0  19.0
#4  NaN  NaN   0.0   0.0

此外,如果您的子集中有很多列,您可以使用字典推导式,例如:
df.fillna(dict.fromkeys(['x', 'y'], 0), inplace=True) # also works

11

您正在填充一份副本(您无法看到)

要么:

  • 不要就地使用fillna(从就地执行某些操作中没有性能收益)

例如:

a_n6[["PROV LAST", "PROV FIRST", "PROV MID", "SPEC NM"]] = a_n6[["PROV LAST", "PROV FIRST", "PROV MID", "SPEC NM"]].fillna("")

或更好地说

a_n6.fillna({'PROV LAST': '', 'PROV FIRST': '',
            'PROV MID': '', 'SPEC NM': ''}, inplace=True)

这里有一个更详细的解释:Pandas: Chained assignments


9
如果inplace参数不能阻止函数产生一个副本,那么它的目的是什么?我原以为它可以做到这一点。请问能否翻译一下? - wordsforthewise
4
为什么fillna()函数允许inplace参数? - codingknob
3
如果您使用.loc,inplace将起作用。如果您使用副本进行操作,则不应该使用inplace。请查看Jeff提供的链接。它不能用于字段列表(例如df.loc[:,[字段列表]]),但可以用于切片或单个字段。还可以参考https://github.com/pandas-dev/pandas/issues/11984获取更多详细信息。 - rhkarls
我刚刚在一个20Gb的数据集上运行了fillna,并使用inplace参数,但出现了“未实现”的错误。我没有看到关于“没有性能提升”的抱怨。我没有另外的20Gb内存来创建临时副本。使用inplace参数将会非常有益。 - Cowboy Trader

1
一个解决方法是将fillna结果保存在另一个变量中,然后像这样重新赋值:
na_values_filled = X.fillna(0)
X = na_values_filled

我举的一个例子(否则我无法让它起作用)是这样的情况:我想在每个组的第一行中填充fillna。就像这样:

groups = one_train.groupby("installation_id")
first_indexes_filled = one_train.loc[groups.apply(pd.DataFrame.first_valid_index), 'clicks'].fillna(0)
one_train.loc[groups.apply(pd.DataFrame.first_valid_index), 'clicks'] =  first_indexes_filled

我的情况可能过于复杂,但我认为一般的“保存结果,然后重新分配”的方法应该可以作为 inplace=True 失败的解决方法。


我不得不做同样的事情 - 我在有条件地使用bfill和ffill,而这个条件的引入似乎阻止了fillna的工作。 - James_SO

0

“将字典用作值参数”答案对我无效,但一个足够简单的解决方法是使用:

for n in ["PROV LAST", "PROV FIRST", "PROV MID", "SPEC NM"]:    
    a_n6[n].fillna("", inplace=True)
a_n6

0

最佳答案给了我SettingWithCopyWarning: A value is trying to be set on a copy of a slice from a DataFrame,所以这就是我最终得到的结果。它可以正常工作而且不会出现任何警告:

fill_dict = {x: 0 for x in columns_of_interest}
df.loc[:, columns_of_interest].fillna(fill_dict, inplace=True)

这样做确实消除了SettingWithCopyWarning,但是inplace=True却没有起作用(df没有改变)。 - wisbucky

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