Todas las respuestas proporcionadas hasta ahora dan como resultado un comportamiento potencialmente peligroso, ya que es muy posible que seleccione un valor ficticio que realmente sea parte del conjunto de datos. Esto es cada vez más probable a medida que crea grupos con muchos atributos. En pocas palabras, el enfoque no siempre se generaliza bien.
Una solución menos hacky es usar pd.drop_duplicates () para crear un índice único de combinaciones de valores, cada una con su propia ID, y luego agruparlas en esa identificación. Es más detallado pero hace el trabajo:
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]
Tenga en cuenta que ahora simplemente puede hacer lo siguiente:
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')]))
Esto devolverá el resultado exitoso sin tener que preocuparse por sobrescribir datos reales que se confunden con un valor ficticio.