У меня есть DataFrame со многими пропущенными значениями в столбцах, которые я хочу сгруппировать:
import pandas as pd
import numpy as np
df = pd.DataFrame({'a': ['1', '2', '3'], 'b': ['4', np.NaN, '6']})
In [4]: df.groupby('b').groups
Out[4]: {'4': [0], '6': [2]}
видите, что Pandas сбросил строки с целевыми значениями NaN. (Я хочу включить эти строки!)
Поскольку мне нужно много таких операций (у многих столбцов отсутствуют значения) и я использую более сложные функции, чем просто медианы (обычно случайные леса), я хочу избежать написания слишком сложных фрагментов кода.
Какие-либо предложения? Стоит ли писать для этого функцию или есть простое решение?
dropna=False
в ,groupby()
чтобы получить желаемый результат. ПодробнееОтветы:
Это упоминается в разделе «Отсутствующие данные» документации :
Один из обходных путей - использовать заполнитель перед выполнением группировки (например, -1):
In [11]: df.fillna(-1) Out[11]: a b 0 1 4 1 2 -1 2 3 6 In [12]: df.fillna(-1).groupby('b').sum() Out[12]: a b -1 2 4 1 6 3
Тем не менее, это выглядит довольно ужасным взломом ... возможно, должна быть возможность включить NaN в groupby (см. Эту проблему с github - в которой используется тот же хак-заполнитель).
Однако, как описано в другом ответе, из pandas 1.1 вы лучше контролируете это поведение, значения NA теперь разрешены в группировщике с использованием dropna = False
источник
панды> = 1.1
Начиная с pandas 1.1, вы лучше контролируете это поведение, значения NA теперь разрешены в группировщике, используя
dropna=False
:pd.__version__ # '1.1.0.dev0+2004.g8d10bfb6f' # Example from the docs df a b c 0 1 2.0 3 1 1 NaN 4 2 2 1.0 3 3 1 2.0 2 # without NA (the default) df.groupby('b').sum() a c b 1.0 2 3 2.0 2 5
# with NA df.groupby('b', dropna=False).sum() a c b 1.0 2 3 2.0 2 5 NaN 1 4
источник
Древняя тема, если кто-то все еще спотыкается, другой обходной путь - преобразовать через .astype (str) в строку перед группировкой. Это сохранит NaN.
df = pd.DataFrame({'a': ['1', '2', '3'], 'b': ['4', np.NaN, '6']}) df['b'] = df['b'].astype(str) df.groupby(['b']).sum()
a b 4 1 6 3 nan 2
источник
sum
Изa
является конкатенация здесь, а не цифровая сумма. Это «работает» только потому, что 'b' состоит из разных записей. Вы должны «а» быть числовыми и «б» быть строкаЯ не могу добавить комментарий к M. Kiewisch, так как у меня недостаточно очков репутации (их всего 41, но мне нужно больше 50 для комментариев).
В любом случае, просто хочу отметить, что решение M. Kiewisch не работает как есть и, возможно, потребуется дополнительная настройка. Рассмотрим, например,
>>> df = pd.DataFrame({'a': [1, 2, 3, 5], 'b': [4, np.NaN, 6, 4]}) >>> df a b 0 1 4.0 1 2 NaN 2 3 6.0 3 5 4.0 >>> df.groupby(['b']).sum() a b 4.0 6 6.0 3 >>> df.astype(str).groupby(['b']).sum() a b 4.0 15 6.0 3 nan 2
который показывает, что для группы b = 4.0 соответствующее значение равно 15 вместо 6. Здесь 1 и 5 просто объединяются как строки, а не складываются как числа.
источник
b
колонкиОдно небольшое замечание по поводу решения Энди Хайдена - оно не работает (больше?), Потому что
np.nan == np.nan
уступаетFalse
, поэтомуreplace
функция на самом деле ничего не делает.Для меня сработало следующее:
df['b'] = df['b'].apply(lambda x: x if not np.isnan(x) else -1)
(По крайней мере, это поведение для Pandas 0.19.2. Извините, что добавляю его в качестве другого ответа, у меня недостаточно репутации, чтобы комментировать.)
источник
df['b'].fillna(-1)
.Все ответы, представленные до сих пор, приводят к потенциально опасному поведению, поскольку вполне возможно, что вы выберете фиктивное значение, которое фактически является частью набора данных. Это становится все более вероятным, поскольку вы создаете группы со многими атрибутами. Проще говоря, этот подход не всегда хорошо обобщается.
Менее хакерское решение - использовать pd.drop_duplicates () для создания уникального индекса комбинаций значений, каждая из которых имеет свой собственный идентификатор, а затем группировать по этому идентификатору. Он более подробный, но выполняет свою работу:
def safe_groupby(df, group_cols, agg_dict): # set name of group col to unique value group_id = 'group_id' while group_id in df.columns: group_id += 'x' # get final order of columns agg_col_order = (group_cols + list(agg_dict.keys())) # create unique index of grouped values group_idx = df[group_cols].drop_duplicates() group_idx[group_id] = np.arange(group_idx.shape[0]) # merge unique index on dataframe df = df.merge(group_idx, on=group_cols) # group dataframe on group id and aggregate values df_agg = df.groupby(group_id, as_index=True)\ .agg(agg_dict) # merge grouped value index to results of aggregation df_agg = group_idx.set_index(group_id).join(df_agg) # rename index df_agg.index.name = None # return reordered columns return df_agg[agg_col_order]
Обратите внимание, что теперь вы можете просто сделать следующее:
data_block = [np.tile([None, 'A'], 3), np.repeat(['B', 'C'], 3), [1] * (2 * 3)] col_names = ['col_a', 'col_b', 'value'] test_df = pd.DataFrame(data_block, index=col_names).T grouped_df = safe_groupby(test_df, ['col_a', 'col_b'], OrderedDict([('value', 'sum')]))
Это вернет успешный результат, не беспокоясь о перезаписи реальных данных, которые были ошибочно приняты за фиктивное значение.
источник
Я уже ответил на это, но почему-то ответ был преобразован в комментарий. Тем не менее, это наиболее эффективное решение:
Неспособность включать (и размножать) NaN в группы довольно неприятно. Цитирование R неубедительно, так как такое поведение не согласуется с множеством других вещей. В любом случае, фиктивный хак тоже довольно плохой. Однако размер (включая NaN) и количество (без учета NaN) группы будут отличаться, если есть NaN.
dfgrouped = df.groupby(['b']).a.agg(['sum','size','count']) dfgrouped['sum'][dfgrouped['size']!=dfgrouped['count']] = None
Если они различаются, вы можете вернуть значение «Нет» для результата функции агрегирования для этой группы.
источник