Класс Groupby и подсчет пропущенных значений в элементах

У меня есть проблема, и я не могу найти никакого решения в Интернете или документации, даже если я думаю, что это очень тривиально.

Что я хочу сделать?

У меня есть такой фрейм

CLASS FEATURE1 FEATURE2 FEATURE3
  X      A       NaN      NaN
  X     NaN       A       NaN
  B      A        A        A

Я хочу сгруппировать по метке (CLASS) и отобразить количество NaN-значений, которые учитываются в каждой функции, чтобы она выглядела следующим образом. Цель этого - получить общее представление о том, как пропущенные значения распределены по различным классам.

CLASS FEATURE1 FEATURE2 FEATURE3
  X      1        1        2
  B      0        0        0

Я знаю, как получить количество df.groupby['CLASS'].count() значений - df.groupby['CLASS'].count()

Есть ли что-то подобное для NaN-значений?

Я пытался вычесть count() из size(), но он вернул неформатированный вывод, заполненный значением NaN

Ответ 1

Вычислите маску с isna, затем сгруппируйте и найдите сумму:

df.drop('CLASS', 1).isna().groupby(df.CLASS, sort=False).sum().reset_index()

  CLASS  FEATURE1  FEATURE2  FEATURE3
0     X       1.0       1.0       2.0
1     B       0.0       0.0       0.0

Другим вариантом является вычитание size из count с использованием rsub вдоль 0- й оси для вычитания с выравниванием по индексу:

df.groupby('CLASS').count().rsub(df.groupby('CLASS').size(), axis=0)

Или же,

g = df.groupby('CLASS')
g.count().rsub(g.size(), axis=0)

       FEATURE1  FEATURE2  FEATURE3
CLASS                              
B             0         0         0
X             1         1         2

Есть довольно много хороших ответов, поэтому вот некоторые timeits для вашего прочтения:

df_ = df
df = pd.concat([df_] * 10000)

%timeit df.drop('CLASS', 1).isna().groupby(df.CLASS, sort=False).sum()
%timeit df.set_index('CLASS').isna().sum(level=0)    
%%timeit
g = df.groupby('CLASS')
g.count().rsub(g.size(), axis=0)

11.8 ms ± 108 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
9.47 ms ± 379 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
6.54 ms ± 81.6 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

Фактическая производительность зависит от ваших данных и настроек, поэтому пробег может отличаться.

Ответ 2

Вы можете использовать set_index и sum:

df.set_index('CLASS').isna().sum(level=0)

Выход:

       FEATURE1  FEATURE2  FEATURE3
CLASS                              
X           1.0       1.0       2.0
B           0.0       0.0       0.0

Ответ 3

Использование разницы между count и size

g=df.groupby('CLASS')

-g.count().sub(g.size(),0)

          FEATURE1  FEATURE2  FEATURE3
CLASS                              
B             0         0         0
X             1         1         2

И мы можем преобразовать этот вопрос в более общий вопрос, как посчитать, сколько NaN в цикле данных с циклом for

pd.DataFrame({x: y.isna().sum()for x , y in g }).T.drop('CLASS',1)
Out[468]: 
   FEATURE1  FEATURE2  FEATURE3
B         0         0         0
X         1         1         2