Как отсортировать фрейм данных pandas, используя группу по

Я работаю над кадром данных, похожим на образец ниже:

import pandas as pd
import numpy as np

np.random.seed(0)
np.random.seed(0)
df = pd.DataFrame({'date' : np.tile(['2024-05-01', '2024-06-01'], 4),
                  'State' : np.repeat(['fl', 'ny', 'mi', 'nc'], 2),
                  'Rev' : [21000, 18200, 51200, 48732, 5676, 6798, 24012, 25005],
                  'Score' : np.random.normal(size = 8),
                  'Value' : np.random.randint(10, 50, size = 8)})
df

    date        State   Rev     Score       Value
0   2024-05-01  fl      21000   1.764052    34
1   2024-06-01  fl      18200   0.400157    22
2   2024-05-01  ny      51200   0.978738    11
3   2024-06-01  ny      48732   2.240893    48
4   2024-05-01  mi       5676   1.867558    49
5   2024-06-01  mi       6798   -0.977278   33
6   2024-05-01  nc      24012   0.950088    34
7   2024-06-01  nc      25005   -0.151357   27

Ожидаемый результат должен представлять собой dataframe, отсортированный по Rev, от большего к меньшему, и внутри каждого State столбец date должен быть отсортирован в порядке возрастания.

Попробовал ниже код:

(df.sort_values(by = ['Rev'], ascending = [False]).
     groupby('State', as_index = False).
     apply(lambda x : x.sort_values('date')).reset_index(drop = True))

Но это не дает мне требуемого результата.

    date        State   Rev     Score               Value
0   2024-05-01  fl      21000   1.764052345967664   34
1   2024-06-01  fl      18200   0.4001572083672233  22
2   2024-05-01  mi       5676   1.8675579901499675  49
3   2024-06-01  mi       6798   -0.977277879876411  33
4   2024-05-01  nc      24012   0.9500884175255894  34
5   2024-06-01  nc      25005   -0.1513572082976979 27
6   2024-05-01  ny      51200   0.9787379841057392  11
7   2024-06-01  ny      48732   2.240893199201458   48

На выходе должны быть Нью-Йорк, Северная Каролина, Флорида и Мичиган в том порядке, который основан на столбцах Rev и date. т. е. для группы State значение Rev для 2024-05-01 будет решать, какое состояние будет иметь приоритет в окончательном порядке вывода.

Может кто-нибудь помочь мне с кодом.

Ожидаемый результат:

df.iloc[[2,3, 6,7, 0,1, 4,5], : ]


    date        State   Rev     Score       Value
2   2024-05-01  ny      51200   0.978738    11
3   2024-06-01  ny      48732   2.240893    48
6   2024-05-01  nc      24012   0.950088    34
7   2024-06-01  nc      25005   -0.151357   27
0   2024-05-01  fl      21000   1.764052    34
1   2024-06-01  fl      18200   0.400157    22
4   2024-05-01  mi       5676   1.867558    49
5   2024-06-01  mi       6798   -0.977278   33

Я считаю, что использование lexsort — наиболее подходящий подход для такой логики (см. здесь). Этот метод был разработан именно для этого.

mozway 29.07.2024 09:41
Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
1
1
91
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Код

out = df.sort_values(
    by=['State', 'date'], 
    ascending=[0, 1], 
    key=lambda x: df['Rev'].groupby(x).transform('first') 
                  if x.name == 'State' else x
)

вне:

         date State    Rev     Score  Value
2  2024-05-01    ny  51200  0.978738     11
3  2024-06-01    ny  48732  2.240893     48
6  2024-05-01    nc  24012  0.950088     34
7  2024-06-01    nc  25005 -0.151357     27
0  2024-05-01    fl  21000  1.764052     34
1  2024-06-01    fl  18200  0.400157     22
4  2024-05-01    mi   5676  1.867558     49
5  2024-06-01    mi   6798 -0.977278     33

или

для удобства чтения, если мы определим вышеуказанную лямбда-функцию как custom_sort, она будет выглядеть так:

def custom_sort(x):
    if x.name == 'State':
        return df['Rev'].groupby(x).transform('first')
    else:
        return x

out = df.sort_values(['State', 'date'], ascending=[0, 1], key=custom_sort)

тот же результат

В моих фактических данных сначала я получаю все 2024-05-01, а затем все 2024-06-01 независимо от State и Rev.

Karthik S 29.07.2024 07:25

Можете ли вы объяснить, что делает часть key?

Karthik S 29.07.2024 07:25

@KarthikS Пожалуйста, укажите точный желаемый результат в таблице, а не словами. и проверьте документ pandas, чтобы узнать параметр key: https://pandas.pydata.org/docs/reference/api/pandas.DataFram‌​e.sort_values.html

Panda Kim 29.07.2024 07:28

Да, я знаю, что делает key в целом, я больше имел в виду часть df['Rev'].groupby(x).transform('first')

Karthik S 29.07.2024 07:29

@KarthikS Вы можете проверить параметр key, поместив каждый столбец в столбце by в x. Это легко увидеть. Также предоставьте желаемый результат в виде ТЕКСТОВОЙ ТАБЛИЦЫ.

Panda Kim 29.07.2024 07:35

Добавил ожидаемый результат, он такой же, как ваш, но по какой-то причине он не работает в моих реальных данных.

Karthik S 29.07.2024 07:38

Не беспокойтесь, понял! В моих фактических данных date — это index, я только что сделал df.reset_index в своем коде, что, вероятно, вызвало проблемы

Karthik S 29.07.2024 07:45
Ответ принят как подходящий

По моему мнению, самый простой и явный подход к выполнению «сложной»/многоусловной сортировки — использовать numpy.lexsort и передавать ограничения в обратном порядке предпочтения:

out = df.iloc[np.lexsort([df['date'],
                          -df.groupby('State')['Rev'].transform('max')])]

Что гласит (в обратном порядке с lexsort):

  • сортировать по приоритету, уменьшая максимальное число оборотов для каждого состояния
  • в случае ничьей сортируйте по возрастанию даты

Если у двух штатов может быть один и тот же максимальный оборот, если вы хотите обеспечить наличие отдельных групп, добавьте df['State'] в качестве промежуточного условия:

  • сортировать по приоритету, уменьшая максимальное число оборотов для каждого состояния
  • в случае равенства максимального количества оборотов сортируйте по названию штата (вы можете использовать другое условие, например общее количество оборотов на штат и т. д.)
  • в случае ничьей сортируйте по возрастанию даты
out = df.iloc[np.lexsort([df['date'],
                          df['State'],
                          -df.groupby('State')['Rev'].transform('max')])]

Выход:

         date State    Rev     Score  Value
2  2024-05-01    ny  51200  0.978738     11
3  2024-06-01    ny  48732  2.240893     48
6  2024-05-01    nc  24012  0.950088     34
7  2024-06-01    nc  25005 -0.151357     27
0  2024-05-01    fl  21000  1.764052     34
1  2024-06-01    fl  18200  0.400157     22
4  2024-05-01    mi   5676  1.867558     49
5  2024-06-01    mi   6798 -0.977278     33
import pandas as pd
import numpy as np

np.random.seed(0)
df = pd.DataFrame({
    'date': np.tile(['1990-05-01', '1990-06-01'], 4),
    'state': np.repeat(['fl', 'ny', 'mi', 'nc'], 2),
    'rev': [21000, 18200, 51200, 48732, 5676, 6798, 24012, 25005],
    'score': np.random.normal(size=8),
    'value': np.random.randint(10, 50, size=8)
})

df['date'] = pd.to_datetime(df['date'])

# Step 1: Determine sort order of states based on rev for '1990-05-01'
rev_0501 = df[df['date'] == '1990-05-01'].set_index('state')['rev']

state_order = rev_0501.sort_values(ascending=False).index
'''
state_order :
Index(['ny', 'nc', 'fl', 'mi'], dtype='object', name='state')
'''
state_order_map = pd.Categorical(df['state'], categories=state_order, ordered=True).codes
'''
state_order_map :
[2 2 0 0 3 3 1 1]
'''
# Convert 'date' to integer for sorting
date_int = df['date'].values.astype('int64')

sorted_indices = np.lexsort((date_int ,state_order_map))
'''
sorted_indices :
[2 3 6 7 0 1 4 5]
'''
sorted_df = df.iloc[sorted_indices].reset_index(drop=True)

print(sorted_df)
'''
        date state    rev     score  value
0 1990-05-01    ny  51200  0.978738     11
1 1990-06-01    ny  48732  2.240893     48
2 1990-05-01    nc  24012  0.950088     34
3 1990-06-01    nc  25005 -0.151357     27
4 1990-05-01    fl  21000  1.764052     34
5 1990-06-01    fl  18200  0.400157     22
6 1990-05-01    mi   5676  1.867558     49
7 1990-06-01    mi   6798 -0.977278     33
'''

Другие вопросы по теме