如何使用空列表[]在pandas中填充数据框中的NaN值?

82
这是我的数据框:
          date                          ids
0     2011-04-23  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...
1     2011-04-24  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...
2     2011-04-25  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...
3     2011-04-26  Nan
4     2011-04-27  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...
5     2011-04-28  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,...

我想用"[]"替换"Nan"。怎么做呢?使用".fillna([])"没有起作用。我甚至尝试了"replace(np.nan, [])",但是出现了错误。
 TypeError('Invalid "to_replace" type: \'float\'',)

你是如何在 ids 中获取列表的? - Anand S Kumar
1
空列表不能被赋值,df.ix[df['ids'].isnull(), 'ids'] = set() 这个设置可以工作吗? - Zero
1
请注意,这很困难的一个原因是因为你并不是真正意义上的在数据框单元格中存储非标量值。尽管你可以这样做,并且有时作为中间步骤很方便(有许多内置方法可以生成列表作为元素),但目前还没有强有力的支持。 - DSM
1
有趣的是,我成功地运行了一个无限循环(达到“RecursionError”),使用以下代码:df.ids.where(df.ids.isnull(), [[]]) - PlasmaBinturong
14个回答

68

我的方法与 @hellpanderrr 类似,但是我测试的是列表而不是使用 isnan 函数:

我的方法与 @hellpanderrr 相似,但不同的是我检查对象是否为列表而不是使用 isnan 函数。
df['ids'] = df['ids'].apply(lambda d: d if isinstance(d, list) else [])

我最初尝试使用pd.isnull(或pd.notnull),但是当给定一个列表时,它会返回每个元素的空值。


2
如果你需要在整个数据框中执行此操作,这个方法对我很有效:df = df.applymap(lambda d: d if isinstance(d, list) else []) - John Sandall

48

一种简单的解决方案是:

df['ids'].fillna("").apply(list)

正如@timgeb所指出的,这需要df ['ids']仅包含列表或NaN。


5
好的,注意这需要df['ids']仅包含列表,除了缺失值(这是OP示例中的情况)。 - timgeb
2
我已经测试了@Nick Edgar的方法和你的方法。你的方法几乎快了2倍。谢谢... - Memin
谢谢你的解决方案。你知道怎么处理一个空列表,其中有4个元素吗? - Thomas LESIEUR
@ThomasLESIEUR 你可以尝试使用.replace({"": whatever})代替.apply(list) - ronkov

40

经过很多头痛的挣扎,我找到了这个方法,应该是最有效的(无需循环,无需apply),只需要将其分配给一个切片:

isnull = df.ids.isnull()

df.loc[isnull, 'ids'] = [ [[]] * isnull.sum() ]

诀窍是构建大小正确的[]列表(isnull.sum()),然后将其封装在列表中:您分配的值是一个2D数组(1列,isnull.sum()行),其中包含空列表元素。


2
这是最高效的答案。 - HaPsantran
请注意,[[]] * isnull.sum() 不会创建 isnull.sum() 个空列表,它只是创建了一个空列表,并在多个引用之间共享。 - timgeb
由于某些原因,这对我不起作用,但是一个简单的 df.loc[isnull, 'ids'] = [[]] 就可以解决问题。可能会随着较新版本的 pandas 发生变化。 - Khris
4
由于避免使用可能导致高昂代价的 apply,我非常喜欢这个答案。但是,当我尝试按照 @Khris 的建议简单地使用 [[]] 时,出现了“必须设置等长键和值”的错误。然而,https://dev59.com/FVwZ5IYBdhLWcg3wWfKl#61944174 看起来是可行的,因此在 pandas==1.2.2 中可以使用以下代码: isna = df[col].isna(); df.loc[isna, [col]] = pd.Series([[]] * isna.sum()).values - low_ghost

26
你可以首先使用loc定位所有ids列中包含nan的行,然后使用at循环遍历这些行并将它们的值设置为空列表:
for row in df.loc[df.ids.isnull(), 'ids'].index:
    df.at[row, 'ids'] = []

>>> df
        date                                             ids
0 2011-04-23  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
1 2011-04-24  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
2 2011-04-25  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
3 2011-04-26                                              []
4 2011-04-27  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]
5 2011-04-28  [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]

11

令人惊讶的是,将值为空列表的字典作为参数传递到Series.fillna中似乎可以正常工作,但是对于DataFrame.fillna来说不行——因此,如果您想要处理单个列,可以使用以下方法:

>>> df
     A    B    C
0  0.0  2.0  NaN
1  NaN  NaN  5.0
2  NaN  7.0  NaN
>>> df['C'].fillna({i: [] for i in df.index})
0    []
1     5
2    []
Name: C, dtype: object

将该解决方案应用于每列,即可将其扩展到数据框。

>>> df.apply(lambda s: s.fillna({i: [] for i in df.index}))
    A   B   C
0   0   2  []
1  []  []   5
2  []   7  []

注意:在缺失值较少的大型Series/DataFrames中,这可能会创建大量不必要的空列表。

已测试使用 pandas 1.0.5。


1
有人会因为我使用这个而要杀了我 :) 很不错的发现! - DannyDannyDanny

4

使用NumPy的另一种解决方案:

df.ids = np.where(df.ids.isnull(), pd.Series([[]]*len(df)), df.ids)

或者使用combine_first:

df.ids = df.ids.combine_first(pd.Series([[]]*len(df)))

3

没有赋值:

1)假设我们的数据框中只有浮点数和整数。

import math
df.apply(lambda x:x.apply(lambda x:[] if math.isnan(x) else x))

2) 对于任何数据框

import math
def isnan(x):
    if isinstance(x, (int, long, float, complex)) and math.isnan(x):
        return True

df.apply(lambda x:x.apply(lambda x:[] if isnan(x) else x))

考虑到numpy已经被导入为np,以下代码行将是足够的...df.apply(lambda x: x.apply(lambda x: [] if x is np.nan else x)) - Ravaging Care

3
也许不是最简短/最优化的解决方案,但我认为它非常易读。
# Masking-in nans
mask = df['ids'].isna()

# Filling nans with a list-like string and literally-evaluating such string
df.loc[mask, 'ids'] = df.loc[mask, 'ids'].fillna('[]').apply(eval)

编辑

根据Swier评论建议:

# Packages
import ast

# Masking-in nans
mask = df['ids'].isna()

# Filling nans with a list-like string and literally-evaluating such string
df.loc[mask, 'ids'] = df.loc[mask, 'ids'].fillna('[]').apply(ast.literal_eval)

1
请使用ast.literal_eval代替eval,它的安全风险要小得多。虽然这段特定的代码是安全的(我认为),但只要掩码有一点不匹配,就可能导致任意代码执行。 - Swier

1
你可以尝试这个:
df.fillna(df.notna().applymap(lambda x: x or []))

1
另一个明确的解决方案是:

,保留HTML标记,不作解释。

# use apply to only replace the nulls with the list  
df.loc[df.ids.isnull(), 'ids'] = df.loc[df.ids.isnull(), 'ids'].apply(lambda x: [])

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