Toutes les réponses fournies jusqu'à présent entraînent un comportement potentiellement dangereux car il est tout à fait possible que vous sélectionniez une valeur fictive qui fait en fait partie de l'ensemble de données. Cela est de plus en plus probable lorsque vous créez des groupes avec de nombreux attributs. En termes simples, l'approche ne se généralise pas toujours bien.
Une solution moins hacky consiste à utiliser pd.drop_duplicates () pour créer un index unique de combinaisons de valeurs, chacune avec son propre identifiant, puis grouper sur cet identifiant. Il est plus verbeux mais fait le travail:
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]
Notez que vous pouvez maintenant simplement faire ce qui suit:
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')]))
Cela renverra le résultat réussi sans avoir à vous soucier d'écraser des données réelles qui sont confondues avec une valeur fictive.