Я работаю над кадром данных, похожим на образец ниже:
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
Код
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
.
Можете ли вы объяснить, что делает часть key
?
@KarthikS Пожалуйста, укажите точный желаемый результат в таблице, а не словами. и проверьте документ pandas, чтобы узнать параметр key
: https://pandas.pydata.org/docs/reference/api/pandas.DataFrame.sort_values.html
Да, я знаю, что делает key
в целом, я больше имел в виду часть df['Rev'].groupby(x).transform('first')
@KarthikS Вы можете проверить параметр key
, поместив каждый столбец в столбце by
в x
. Это легко увидеть. Также предоставьте желаемый результат в виде ТЕКСТОВОЙ ТАБЛИЦЫ.
Добавил ожидаемый результат, он такой же, как ваш, но по какой-то причине он не работает в моих реальных данных.
Не беспокойтесь, понял! В моих фактических данных date
— это index
, я только что сделал df.reset_index
в своем коде, что, вероятно, вызвало проблемы
По моему мнению, самый простой и явный подход к выполнению «сложной»/многоусловной сортировки — использовать 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
'''
Я считаю, что использование
lexsort
— наиболее подходящий подход для такой логики (см. здесь). Этот метод был разработан именно для этого.