Pandas如何使用groupby统计每个列的缺失值数量

4

这个问题展示了如何计算数据框中特定列C的NA值。如何计算所有列(不包括分组列)中的NA值?

以下是一些测试代码,但它并不能正常工作:

#!/usr/bin/env python3

import pandas as pd
import numpy as np

df = pd.DataFrame({'a':[1,1,2,2], 
                   'b':[1,np.nan,2,np.nan],
                   'c':[1,np.nan,2,3]})

# result = df.groupby('a').isna().sum()
# AttributeError: Cannot access callable attribute 'isna' of 'DataFrameGroupBy' objects, try using the 'apply' method

# result = df.groupby('a').transform('isna').sum()
# AttributeError: Cannot access callable attribute 'isna' of 'DataFrameGroupBy' objects, try using the 'apply' method

result = df.isna().groupby('a').sum()
print(result)
# result:
#          b    c
# a
# False  2.0  1.0

result = df.groupby('a').apply(lambda _df: df.isna().sum())
print(result)
# result:
#    a  b  c
# a
# 1  0  2  1
# 2  0  2  1

预期输出:

     b    c
a
1    1    1
2    1    0
8个回答

3

在涉及到许多组的情况下,最好避免使用groupby.apply,而是选择基本函数,这些函数被编译成cython,因此可以更好地扩展性。这样会大大提高性能。在这种情况下,首先在整个DataFrame上检查isnull(),然后使用groupby+sum

df[df.columns.difference(['a'])].isnull().groupby(df.a).sum().astype(int)
#   b  c
#a      
#1  1  1
#2  1  0

为了说明性能的提升:
import pandas as pd
import numpy as np

N = 50000
df = pd.DataFrame({'a': [*range(N//2)]*2,
                   'b': np.random.choice([1, np.nan], N),
                   'c': np.random.choice([1, np.nan], N)})

%timeit df[df.columns.difference(['a'])].isnull().groupby(df.a).sum().astype(int)
#7.89 ms ± 187 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

%timeit df.groupby('a')[['b', 'c']].apply(lambda x: x.isna().sum())
#9.47 s ± 111 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

2

使用 apply 方法配合 isnasum 函数。此外,我们还需要选择正确的列,以避免获取不必要的 a 列:

注意: apply 方法可能会比较慢,建议使用其中一个向量化的解决方案,参见WenYoBenAnky 或者 ALollz 的答案。

df.groupby('a')[['b', 'c']].apply(lambda x: x.isna().sum())

输出

   b  c
a      
1  1  1
2  1  0

2

你的问题已经有了答案(你把_df错写成了df):

result = df.groupby('a')['b', 'c'].apply(lambda _df: _df.isna().sum())
result
   b  c
a      
1  1  1
2  1  0

2
另一种方法是在a上使用set_index(),然后按索引分组并求和:
df.set_index('a').isna().groupby(level=0).sum()*1

或者:

df.set_index('a').isna().groupby(level=0).sum().astype(int)

或者不使用groupby,感谢@WenYoBen的提示:

df.set_index('a').isna().sum(level=0).astype(int)

   b  c
a      
1  1  1
2  1  0

1
df.set_index('a').isna().sum(level=0).astype(int) - BENY

1
我将使用count然后用value_counts进行替换,我之所以不使用apply是因为它通常性能较差。
df.groupby('a')[['b','c']].count().rsub(df.a.value_counts(dropna=False),axis=0)
Out[78]: 
   b  c
1  1  1
2  1  0

替代方案
df.isna().drop('a',1).astype(int).groupby(df['a']).sum()
Out[83]: 
   b  c
a      
1  1  1
2  1  0

1
您需要在使用apply之后删除该列。
df.groupby('a').apply(lambda x: x.isna().sum()).drop('a',1)

输出:

    b   c
a       
1   1   1
2   1   0

1
另一个肮脏的工作:
df.set_index('a').isna().astype(int).groupby(level=0).sum()

输出:

    b   c
a       
1   1   1
2   1   0

0
您可以按照以下方式编写自己的聚合函数:
df.groupby('a').agg(lambda x: x.isna().sum())

这将导致

     b    c
a          
1  1.0  1.0
2  1.0  0.0

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