За элементами NaN в группе панд следует агрегация, включая ожидаемое поведение столбца категории? - PullRequest
4 голосов
/ 01 ноября 2019

Я агрегировал DataFrame панд, который содержал столбец категории. Вывод содержал несколько NaN, которые я не ожидал и не понимаю. Пример кода:

import pandas as pd

d = pd.DataFrame({'a': [1, 1, 2, 2], 'b': [1, 1, 2, 2], 'c': [1, 2, 3, 4]})
d.astype({'b': 'category'}).groupby('a').agg({'b':'first', 'c':['mean', 'max']})

Я получаю следующий вывод:

      b    c     
  first mean  max
0     1  NaN  NaN
1     2  1.5  2.0
2   NaN  3.5  4.0

Я бы ожидал следующий вывод:

      b    c    
  first mean max
a               
1     1  1.5   2
2     2  3.5   4

Может кто-нибудь объяснить NaN?

Следующие два варианта кода, один без столбца категории и один без нескольких агрегаций столбца c, дают ожидаемый результат.

d.groupby('a').agg({'b':'first', 'c':['mean', 'max']})
d.astype({'b': 'category'}).groupby('a').agg({'b':'first', 'c':'mean'})

Я использую pandas 0.25.2 и python3.7.4.

Ответы [ 2 ]

2 голосов
/ 01 ноября 2019

Отправной точкой моего объяснения является наличие (и сравнение поведения) двух фреймов данных:

  • d - оригинала фрейма данных,
  • d2 = d.astype({'b': 'category'}) - родной элемент DataFrame, столбец b изменен на Категориальный .

Создатьгруппировка этих обоих DataFrames:

gr = d.groupby('a')
gr2 = d2.groupby('a')

Пока они выглядят одинаково, например, если вы запустите:

for key, grp in gr:
    print(f'\nGroup: {key}\n{grp}')

и тот же код для gr2 , вы будетеполучите точно тот же результат.

Но результаты станут другими, когда вы попытаетесь агрегировать b столбец.

Когда вы запустите gr.b.agg('first') (на исходном DataFrame), вы получите:

a
1    1
2    2
Name: b, dtype: int64

Результат - Series и, как ожидается, от d content:

  • для группы строк с a == 1 первое значение b равно 1 ,
  • для группы строкс a == 2 первым значением b is 2 .

Но если вы запустите gr2.b.agg('first') (на дочернем фрейме данных), вы получите:

[1, 2]
Categories (2, int64): [1, 2]

Результат будет pandas.core.arrays.categorical.Categorical тип и, что более важно, не имеет отношения с ключами группировки.

Вместо этого что-то действует как группирующими клавишами являются последовательных чисел (начальная форма 0 ).

Вы можете подтвердить это, выполнив gr2.agg({'b':'first'}) и получите:

   b
0  1
1  2

Просто это источник вашей проблемы. При запуске:

gr2.agg({'b':'first', 'c':['mean', 'max']})

объединение агрегации и категориальных и "нормальных" столбцов, тогда:

  • выше искусственный ключ группировки, полученный в результате агрегирования b ,
  • , объединяется с фактическим ключом группировки из агрегации c .

Для (искусственного) ключа группировки 0 :

  • значение для b / first равно 1 (см. выше),
  • для с / среднее и с / макс значения отсутствуют, поэтому результат содержит 2 NaN с.

Для группового ключа 1 :

  • для искусственный ключ, значение для b / first is 2 ,
  • для true key, значения для c / mean и c / max are 1,5 и 2.0 ,
  • , но Панды помещает их всех в в одном ряду.

Дляключ группировки 2 :

  • нет «искусственного» ключа группировки с этим значением, поэтому в столбце b / first есть NaN ,
  • для c / среднее и c / max там являются соответствующими значениями, поэтому они печатаются там.

Вывод: В таком случае не рекомендуется преобразовывать какой-либо столбец в Категориальный тип.

Использовать только оригинал DataFrame с «оригинальными» (не категориальными) типами, и вы получите ожидаемый результат.

Отредактируйте следующий комментарий по состоянию на 09: 14: 38Z

Существует противоречие междуто, что вы написали в своем посте и в упомянутом комментарии.

Ваше сообщение содержит запись:

I get the following output:

      b    c     
  first mean  max
0     1  NaN  NaN
1     2  1.5  2.0
2   NaN  3.5  4.0

поэтому распечатка содержит содержит NaN значения.

Но в комментарии, который вы написали Я не получаю NaN .

Чтобы проверить это противоречие, я снова сделал тест:

d = pd.DataFrame({'a': [1, 1, 2, 2], 'b': [1, 1, 2, 2], 'c': [1, 2, 3, 4]})
d2 = d.astype({'b': 'category'})
d2.groupby('a').agg({'b':'first', 'c':['mean', 'max']})

и получил:

      b    c     
  first mean  max
0     1  NaN  NaN
1     2  1.5  2.0
2   NaN  3.5  4.0

так же, как в вашем посте с NaN значениями.

1 голос
/ 03 ноября 2019

Я думаю, что это ошибка , но возможное решение - использовать функцию с Series.iat для первого значения группы, потому что Series.first работает иначе:

Удобный метод для подстановки начальных периодов данных временных рядов на основе смещения даты.

def first(x):
    return x.iat[0]

d = pd.DataFrame({'a': [1, 1, 2, 2], 'b': [1, 1, 2, 2], 'c': [1, 2, 3, 4]})
d = d.astype({'b': 'category'}).groupby('a').agg({'b':first, 'c':['mean', 'max']})
print(d)
      b    c    
  first mean max
a               
1     1  1.5   2
2     2  3.5   4

Если использовать лямбда-функцию, тогда получите <lambda> строку для второго уровняиз MultiIndex:

d = d.astype({'b': 'category'}).groupby('a').agg({'b':lambda x: x.iat[0], 'c':['mean', 'max']})
print(d)
         b    c    
  <lambda> mean max
a                  
1        1  1.5   2
2        2  3.5   4
...