Можно ли сортировать значения по количеству значений суммы каждой группы. без нарушения уровня индекса? Обе попытки, которые я закомментировал, будут сортироваться, но нарушают уровень индекса.
#DataFrame
ff = pd.DataFrame([('P1', 17, 'male'),
('P2', 10, 'female'),
('P3', 10, 'male'),
('P4', 19, 'female'),
('P5', 10, 'male'),
('P6', 12, 'male'),
('P7', 12, 'male'),
('P8', 15, 'female'),
('P9', 15, 'female'),
('P10', 10, 'male')],
columns=['Name', 'Age', 'Sex'])
# Attempts
(
ff
.groupby(['Age', 'Sex'])
.agg(**{
'Count': pd.NamedAgg(column = "Name", aggfunc='count'),
'Who': pd.NamedAgg(column = "Name", aggfunc=lambda x: ', '.join([i for i in x]))})
# .sort_values('Count') <- this breaks the index level
# .sort_values(['Count', 'Age']) <- this too breaks the index level
)
Исходные данные:
Считать | Кто | ||
---|---|---|---|
Возраст | Секс | ||
10 | женский | 1 | р2 |
мужчина | 3 | п3,п5,п10 | |
12 | мужчина | 2 | р6, р7 |
15 | женщина | 2 | п8, п9 |
17 | мужчина | 1 | р1 |
19 | женщина | 1 | п4 |
Желаемый результат: (отсортируйте значения по сумме группы «Возраст», но сохраните сгруппированный индекс)
Считать | Кто | ||
---|---|---|---|
Возраст | Секс | ||
17 | мужчина | 1 | р1 |
19 | женщина | 1 | п4 |
12 | мужчина | 2 | р6, р7 |
15 | женщина | 2 | п8, п9 |
10 | женский | 1 | р2 |
мужчина | 3 | п3,п5,п10 |
Обновлено: вот как я наконец решаю проблему, любые дополнительные советы приветствуются.
# DataFrame -- I update a bit for testcases.
ff = pd.DataFrame([('P1', 19, 'male'),
('P2', 10, 'female'),
('P3', 10, 'male'),
('P4', 19, 'female'),
('P5', 10, 'male'),
('P6', 12, 'male'),
('P7', 12, 'male'),
('P7', 12, 'male'),
('P7', 12, 'male'),
('P7', 12, 'male'),
('P8', 15, 'female'),
('P9', 15, 'female'),
('P10', 10, 'male')],
columns=['Name', 'Age', 'Sex'])
# It works !
(
ff.groupby(['Age', 'Sex']).agg(**{
'Count': pd.NamedAgg(column = "Name", aggfunc='count'),
'Who': pd.NamedAgg(column = "Name", aggfunc=lambda x: ', '.join([i for i in x]))})
# Sort by 'Count' and keep the group adding 'tmp'
.assign(
tmp=lambda x: x.reset_index().groupby('Age')['Count'].transform('sum').to_numpy())
.sort_values(['tmp','Age'])
# drop tmp
.drop('tmp', axis=1)
)
что вы подразумеваете под без нарушения уровня индекса?
Привет, закомментированные строки показывают, что происходит, в основном это означает, что он будет сортироваться по «количеству» и разбивать, например, группу 10-летнего возраста на 2 непоследовательные строки.
Ну вот.
Давайте сохраним переменную temp в data
.
data = ff.groupby(['Age', 'Sex']).agg(**{
'Count': pd.NamedAgg(column = "Name", aggfunc='count'),
'Who': pd.NamedAgg(column = "Name", aggfunc=lambda x: ', '.join([i for i in x]))})
Вы можете написать пользовательскую функцию, чтобы делать то, что вы хотите делать в каждой группе, с помощью функции apply
.
Например.
data.groupby("Age", group_keys=False).apply(lambda x: x.sort_values("Count", ascending=False))
Count Who
Age Sex
10 male 3 P3, P5, P10
female 1 P2
12 male 2 P6, P7
15 female 2 P8, P9
17 male 1 P1
19 female 1 P4
Или измените на ascending order
data.groupby("Age", group_keys=False).apply(lambda x: x.sort_values("Count", ascending=False))
Count Who
Age Sex
10 female 1 P2
male 3 P3, P5, P10
12 male 2 P6, P7
15 female 2 P8, P9
17 male 1 P1
19 female 1 P4
Или, если вы хотите отсортировать по каждому уровню мультииндекса. вы можете сделать так.
Вы можете отсортировать индекс, добавив level
аргументы в функцию sort_index
.
Например:
data.sort_index(level=0, ascending=True)
Отсортируйте первый индекс по возрастанию.
Count Who
Age Sex
19 female 1 P4
17 male 1 P1
15 female 2 P8, P9
12 male 2 P6, P7
10 male 3 P3, P5, P10
female 1 P2
data.sort_index(level=[0,1], ascending=[False, True])
Отсортируйте первый индекс в порядке возрастания, а второй индекс в порядке убывания.
Count Who
Age Sex
19 female 1 P4
17 male 1 P1
15 female 2 P8, P9
12 male 2 P6, P7
10 female 1 P2
male 3 P3, P5, P10
Кстати.
breaking index level
не является особым результатом. Это просто оптимизация отображения
Например.
Вы можете создать его самостоятельно, например:
pd.DataFrame({"a":[1,2,3,4,5]}, index=pd.MultiIndex.from_arrays([[10,10,20,10,10],['F','M','F','M','F']],names=['A','B']))
a
A B
10 F 1
M 2
20 F 3
10 M 4
F 5
Я боюсь, что это только сортирует индекс по «Возрасту» вместо значения «Количество». Я попытался продублировать некоторые столбцы (в моем случае я скопировал прошлый P7, чтобы он имел наибольшее значение счетчика, но все приведенные выше сортировки вернут отсортированные df по возрасту.
Вы можете изменить форму на DataFrame.unstack
и отсортировать индекс по сумме обоих значений Sex
, если они существуют, а затем изменить форму на DataFrame.stack
:
df1 = df.unstack()
df1 = df1.sort_index(key=df1.sum(axis=1, numeric_only=True).get).stack().astype(df.dtypes)
print (df1)
Count Who
Age Sex
17 male 1 P1
19 female 1 P4
12 male 2 P6, P7
15 female 2 P8, P9
10 female 1 P2
male 3 P3, P5, P10
Другая идея — сортировка по сумме обоих значений с помощью GroupBy.transform
:
df['tmp'] = df.groupby('Age')['Count'].transform('sum')
df1 = df.sort_values(['tmp','Age']).drop('tmp', axis=1)
print (df1)
Count Who
Age Sex
17 male 1 P1
19 female 1 P4
12 male 2 P6, P7
15 female 2 P8, P9
10 female 1 P2
male 3 P3, P5, P10
Обновлено: однострочное решение:
df = (
ff
.groupby(['Age', 'Sex'])
.agg(**{
'Count': pd.NamedAgg(column = "Name", aggfunc='count'),
'Who': pd.NamedAgg(column = "Name", aggfunc=', '.join)})
.assign(tmp = lambda x: x.groupby('Age')['Count'].transform('sum'))
.sort_values(['tmp','Age'])
.drop('tmp', axis=1))
print (df)
Count Who
Age Sex
17 male 1 P1
19 female 1 P4
12 male 2 P6, P7
15 female 2 P8, P9
10 female 1 P2
male 3 P3, P5, P10
Второй работает, и если мне нужно сделать его однострочным (для лучшей читаемости, используя .function в каждой строке), я обновил вопрос, чтобы показать, как я это сделал. Есть ли избыточный код или что-то, что можно было бы улучшить?
@Cookie - ответ был отредактирован.
Вау, это именно то, что я делал, здорово знать! Обычно ли создают столбец tmp и удаляют их?
@Cookie - нет, но здесь сортировка по 2 столбцам, поэтому использовал это решение.
Непонятно, по чему вы хотите сортировать?