Прочитав все ответы, я придумал следующее:
def __my_flatten_cols(self, how="_".join, reset_index=True):
how = (lambda iter: list(iter)[-1]) if how == "last" else how
self.columns = [how(filter(None, map(str, levels))) for levels in self.columns.values] \
if isinstance(self.columns, pd.MultiIndex) else self.columns
return self.reset_index() if reset_index else self
pd.DataFrame.my_flatten_cols = __my_flatten_cols
С учетом кадра данных:
df = pd.DataFrame({"grouper": ["x","x","y","y"], "val1": [0,2,4,6], 2: [1,3,5,7]}, columns=["grouper", "val1", 2])
grouper val1 2
0 x 0 1
1 x 2 3
2 y 4 5
3 y 6 7
df.groupby(by="grouper").agg("min").my_flatten_cols()
То же, что и df.groupby(by="grouper",
as_index = False )
или .agg(...)
.reset_index () ----- before -----
val1 2
grouper
------ after -----
grouper val1 2
0 x 0 1
1 y 4 5
df.groupby(by="grouper").agg({"val1": [min,max]}).my_flatten_cols("last")
То же, что и a = df.groupby(..).agg(..); a.columns = a.columns.droplevel(0); a.reset_index()
. ----- before -----
val1
min max
grouper
------ after -----
grouper min max
0 x 0 2
1 y 4 6
df.groupby(by="grouper").agg({"val1": min, 2:[sum, "size"]}).my_flatten_cols()
# you can combine the names in other ways too, e.g. use a different delimiter:
#df.groupby(by="grouper").agg({"val1": min, 2:[sum, "size"]}).my_flatten_cols(" ".join)
Запускает a.columns = ["_".join(filter(None, map(str, levels))) for levels in a.columns.values]
под капотом (так как эта форма agg()
приводит к MultiIndex
по столбцам). Если у вас нет помощника my_flatten_cols
, может быть проще ввести решение, предложенное @Seigi : a.columns = ["_".join(t).rstrip("_") for t in a.columns.values]
, которое работает аналогично в этом случае (но сбой, если у вас есть числовые метки для столбцов). Чтобы обрабатывать числовые метки в столбцах, вы можете использовать решение, предложенное @jxstanford и @Nolan Conaway (a.columns = ["_".join(tuple(map(str, t))).rstrip("_") for t in a.columns.values]
), но я не понимаю, почему вызов tuple()
и я полагаю, что rstrip()
требуется только в том случае, если некоторые столбцы имеют дескриптор типа ("colname", "")
(что может произойти, если вы reset_index()
перед попыткой исправить .columns
) ----- before -----
val1 2
min sum size
grouper
------ after -----
grouper val1_min 2_sum 2_size
0 x 0 4 2
1 y 4 12 2
df.groupby(by="grouper").agg({"val1": {"sum_of_val1": "sum", "count_of_val1": "count"},
2: {"sum_of_2": "sum", "count_of_2": "count"}}).my_flatten_cols("last")
Другое включают : установка столбцов вручную: res.columns = ['A_sum', 'B_sum', 'count']
или .join()
с несколькими операторами groupby
. ----- before -----
val1 2
count_of_val1 sum_of_val1 count_of_2 sum_of_2
grouper
------ after -----
grouper count_of_val1 sum_of_val1 count_of_2 sum_of_2
0 x 2 2 2 4
1 y 2 10 2 12
map(str, ..)
filter(None, ..)
columns.values
возвращает имена (str
, а не кортежи) .agg()
, вам может понадобиться чтобы удерживать нижнюю метку для столбца или конкатенировать несколько ярлыков reset_index()
мог работать с группой- по столбцам в обычном порядке, поэтому он делает это по умолчанию