#python #pandas #pandas-groupby
#python #pandas #pandas-groupby
Вопрос:
Учитывая фрейм данных
Column1 Column2 Column3
0 a foo 1
1 a bar 2
2 b baz 12
3 b foo 4
4 c bar 6
5 c foo 3
6 c baz 7
7 d foo 9
Я хотел бы сгруппировать по столбцу 1, используя произвольный порядок приоритета, значения которого следует сохранить из столбца 3.
Например, если порядок приоритета равен:
- база
- панель
- foo
тогда я бы ожидал, что результат будет отображаться как
Column2
Column1
a 2
b 12
c 7
d 9
при этом группа «a» сохраняет значение «bar», поскольку для группы «a» нет «baz», группа «b» сохраняет значение «baz» и так далее.
Какой самый элегантный способ сделать это? Прямо сейчас я применяю серию apply lambda для работы с каждым элементом, но это кажется неаккуратным.
РЕДАКТИРОВАТЬ: Что, если преценденция проходит через несколько столбцов?
Пример.
Column1 Column2 Column3 Column4
0 a foo john 1
1 a bar jim 2
2 b baz jack 12
3 b foo jim 4
4 c bar john 6
5 c foo john 3
6 c baz jack 7
7 d foo jack 9
Если порядок приоритета как для Column2, так и для Column3 равен:
- джим
- база
- foo
тогда я бы ожидал, что результат будет отображаться как
Column2 Column3
Column1
a jim 2
b jim 4
c baz 7
d foo 9
Ответ №1:
Вы можете попробовать использовать приведенную ниже логику с map
затем groupby transform
order = ['baz','bar','foo']
d = {v:k for k,v in dict(enumerate(order)).items()}
out = df.assign(k=df['Column2'].map(d))
print(df[out['k'].eq(out.groupby("Column1")['k'].transform("min"))])
Column1 Column2 Column3
1 a bar 2
2 b baz 12
6 c baz 7
7 d foo 9
РЕДАКТИРОВАТЬ, для нескольких столбцов, используя ту же логику, что и выше, вот способ:
order = ['jim','baz','foo']
d = {i:e for e,i in enumerate(order)}
s = df[['Column2','Column3']].replace(d).apply(pd.to_numeric,errors='coerce').min(1)
out = (s[s.eq(s.groupby(df['Column1']).transform("min"))]
.replace(dict(enumerate(order))).rename("Col"))
df.loc[out.index,["Column1","Column4"]].join(out)
Column1 Column4 Col
1 a 2 jim
3 b 4 jim
6 c 7 baz
7 d 9 foo
Комментарии:
1. Спасибо за это! Что вы думаете о дополнительной сложности упорядоченных элементов, хранящихся в двух разных столбцах? Может быть, создать и дополнительный столбец с элементом наивысшего приоритета в нем, а затем запустить эту логику в этом новом столбце?
2. Привет, Анки, спасибо, что поделились хорошим решением, не могли бы вы подробнее объяснить,
v:k for k,v in dict(enumerate(order)).items()
пожалуйста, буду благодарен вам.3. @RavinderSingh13 мы создаем перечислитель в виде словаря и меняем местами ключи и значения, чтобы мы могли использовать сопоставление с существующей серией, вы также можете напрямую использовать что-то вроде
{i:e for e,i in enumerate(order)}
4. @bcalc вы можете попробовать то, что предложил Allolz в своем ответе, я также отредактировал другой способ, используя
replace
и аналогичную логику
Ответ №2:
Если у вас есть порядок для всех значений в ‘Column2’, вы можете использовать loc
после установки индекса, чтобы наложить свой пользовательский порядок, а затем drop_duplicates
сохранить только наивысший приоритет.
order = ['baz', 'bar', 'foo']
df.set_index('Column2').loc[order].drop_duplicates('Column1')
Column1 Column3
Column2
baz b 12
baz c 7
bar a 2
foo d 9
Во втором случае, если вам нужно сделать это в нескольких столбцах, мы сначала melt
складываем столбцы 2 и Column3 в один длинный ряд, а остальные — так же, как указано выше:
order = ['jim', 'baz', 'foo']
(df.melt(id_vars=['Column4', 'Column1'], value_vars=['Column2', 'Column3'])
.drop(columns='variable')
.set_index('value')
.loc[order]
.drop_duplicates('Column1')
)
Column4 Column1
value
jim 2 a
jim 4 b
baz 7 c
foo 9 d
Комментарии:
1. Спасибо за это! Что вы думаете о дополнительной сложности упорядоченных элементов, хранящихся в двух разных столбцах? Может быть, создать и дополнительный столбец с элементом наивысшего приоритета в нем, а затем запустить эту логику в этом новом столбце?
2. @bcalc В этом случае я бы сначала
melt
затем вы можете применить любое из решений, как и в первом случае.
Ответ №3:
Вы можете попробовать преобразовать Column2
в категориальный:
df['Column2'] = pd.Categorical(df['Column2'], ordered=True, categories=['baz','bar','foo'])
df.sort_values(['Column1','Column2']).drop_duplicates('Column1')
Вывод:
Column1 Column2 Column3
1 a bar 2
2 b baz 12
6 c baz 7
7 d foo 9
Комментарии:
1.Спасибо за это! Что вы думаете о дополнительной сложности упорядоченных элементов, хранящихся в двух разных столбцах? Может быть, создать и дополнительный столбец с элементом наивысшего приоритета в нем, а затем запустить эту логику в этом новом столбце?