用熊猫数据框中的空列表替换 NaN

新手上路,请多包涵

我正在尝试用一个空列表 [] 替换我数据中的一些 NaN 值。但是,该列表表示为 str 并且不允许我正确应用 len() 函数。无论如何用熊猫中的实际空列表替换 NaN 值?

 In [28]: d = pd.DataFrame({'x' : [[1,2,3], [1,2], np.NaN, np.NaN], 'y' : [1,2,3,4]})

In [29]: d
Out[29]:
           x  y
0  [1, 2, 3]  1
1     [1, 2]  2
2        NaN  3
3        NaN  4

In [32]: d.x.replace(np.NaN, '[]', inplace=True)

In [33]: d
Out[33]:
           x  y
0  [1, 2, 3]  1
1     [1, 2]  2
2         []  3
3         []  4

In [34]: d.x.apply(len)
Out[34]:
0    3
1    2
2    2
3    2
Name: x, dtype: int64

原文由 moku 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 535
2 个回答

这使用 isnullloc 来掩盖系列:

 In [90]:
d.loc[d.isnull()] = d.loc[d.isnull()].apply(lambda x: [])
d

Out[90]:
0    [1, 2, 3]
1       [1, 2]
2           []
3           []
dtype: object

In [91]:
d.apply(len)

Out[91]:
0    3
1    2
2    0
3    0
dtype: int64

您必须使用 apply 来执行此操作,以免列表对象被解释为要分配回 df 的数组,df 将尝试将形状对齐回原始系列

编辑

使用您更新的示例,以下工作:

 In [100]:
d.loc[d['x'].isnull(),['x']] = d.loc[d['x'].isnull(),'x'].apply(lambda x: [])
d

Out[100]:
           x  y
0  [1, 2, 3]  1
1     [1, 2]  2
2         []  3
3         []  4

In [102]:
d['x'].apply(len)

Out[102]:
0    3
1    2
2    0
3    0
Name: x, dtype: int64

原文由 EdChum 发布,翻译遵循 CC BY-SA 3.0 许可协议

要扩展已接受的答案, apply 调用可能会特别昂贵 - 通过从头构建一个 numpy 数组,可以在没有它的情况下完成相同的任务。

 isna = df['x'].isna()
df.loc[isna, 'x'] = pd.Series([[]] * isna.sum()).values

快速时序比较:

 def empty_assign_1(s):
    s[s.isna()].apply(lambda x: [])

def empty_assign_2(s):
    [[]] * s.isna().sum()

series = pd.Series(np.random.choice([1, 2, np.nan], 1000000))

%timeit empty_assign_1(series)
>>> 61 ms ± 964 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

%timeit empty_assign_2(series)
>>> 2.17 ms ± 70.5 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

快了将近10倍!

编辑:修复了@valentin 指出的错误

在这种情况下执行赋值时,您必须小心处理数据类型。在上面的示例中,测试系列是浮动的,但是,添加 [] 元素会将整个系列强制为对象。如果你做类似的事情,Pandas 会为你处理

idx = series.isna()
series[isna] = series[isna].apply(lambda x: [])

因为apply的输出本身就是一个series。您可以像这样使用分配开销来测试现场性能(我添加了一个字符串值,因此系列是一个对象,您可以改为使用数字作为替换值而不是空列表以避免强制转换)。

 def empty_assign_1(s):
    idx = s.isna()
    s[idx] = s[idx].apply(lambda x: [])

def empty_assign_2(s):
    idx = s.isna()
    s.loc[idx] = [[]] * idx.sum()

series = pd.Series(np.random.choice([1, 2, np.nan, '2'], 1000000))

%timeit empty_assign_1(series.copy())
>>> 45.1 ms ± 386 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

%timeit empty_assign_2(series.copy())
>>> 24 ms ± 393 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)

其中大约 4 毫秒与副本相关,10 倍到 2 倍,仍然相当不错。

原文由 ieaves 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题