df_example = pd.DataFrame({'name': ['a', 'a', 'a', 'b', 'b', 'b'],
'class': [1, 2, 2, 3, 2, 2],
'price': [3, 4, 2, 1, 6, 5]})
Я хочу отфильтровать каждый name
, где price
больше наименьшего price
в подмножестве class==2
внутри name
группы:
df_example.sort_values(['name', 'price'], inplace=True)
df_tem = df_example[df_example['class'] == 2].groupby('name').first()
Ниже приведен псевдокод:
df_example.groupby('name').apply(lambda key, val: val['price'] > df_tem.loc[key]['price']).reset_index()
Есть ли какой-нибудь эффективный способ добиться чего-то вроде фильтра фрейма данных на основе подмножества внутри groupby
результат:
наименьшая цена с class=2
для каждой группы имен df_tem
:
class price
name
a 2 2
b 2 5
Поэтому,
group a: price>2; group b: price>5
выход:
pd.DataFrame({'name': ['a', 'a', 'b'],
'class': [1, 2, 2],
'price': [3, 4, 6]})
Обновлять:
на самом деле у меня есть идея создать новый столбец с именем «самый маленький», а затем отфильтровать
df_example by df_example['price'] > df_example['smallest '].
Знаете ли вы, как быстро создать такой столбец, что-то вроде
df_example['smallest '] = df_example[df_example['class'] == 2].groupby('name')['price'].transform('first')
вышеописанный способ еще есть nan
Вы можете сделать это, используя groupby.transform, чтобы выровнять значение min
для каждого имени where
class==2 и сравнить gt
(больше чем) с ценой строки
df_output = (
df_example
.loc[lambda x:
x['price'].gt(x['price'].where(x['class'].eq(2))
.groupby(x['name']).transform(min))]
)
print(df_output)
# name class price
# 0 a 1 3
# 1 a 2 4
# 4 b 2 6
на самом деле у меня есть идея создать новый столбец с именем smallest
, а затем отфильтровать df_example
по df_example['price'] > df_example['smallest ']
. Знаете ли вы, как быстро создать такой столбец, например df_example['smallest '] = df_example[df_example['class'] == 2].groupby('name')['price'].transform('first')
, приведенный выше способ все еще имеет значение nan
@user6703592 user6703592 да, можете df_example['smallest'] = df_example['price'].where(df_example['class'].eq(2)).groupby(df_example['name']).transform(min)
но я не вижу смысла создавать столбец, если вы не используете это значение, кроме как для фильтрации
Вы можете получить min
цену для класса 2, merge
вставить ее df_example
в новый столбец min_price
, а затем использовать ее для фильтрации:
m = (
df_example[df_example["class"] == 2]
.groupby("name")["price"]
.min()
.reset_index(name = "min_price")
)
df_example = (
df_example.merge(m, how = "left", on = "name")
.query("price > min_price")
.drop(columns = "min_price")
)
name class price
0 a 1 3
1 a 2 4
4 b 2 6
извините, неправильно отредактировал свой ответ
Код
Используйте groupby
, чтобы агрегировать минимум, и используйте map
, чтобы сопоставить результат с name column
для логического индексирования.
m = df_example[df_example['class'] == 2].groupby('name')['price'].min()
out = df_example[df_example['price'] > df_example['name'].map(m)]
вне
name class price
0 a 1 3
1 a 2 4
4 b 2 6
обновить дополнительный вопрос
Кроме того, если вы хотите указать имя, для которого class=2
не существует, используйте код ниже.
m = df_example[df_example['class'] == 2].groupby('name')['price'].min()
cond1 = df_example['price'] > df_example['name'].map(m)
cond2 = ~df_example['name'].isin(m.index)
out = df_example[cond1 | cond2]
новый пример
df_example = pd.DataFrame({'name': ['a', 'a', 'a', 'b', 'b', 'b', 'c'],
'class': [1, 2, 2, 3, 2, 2, 3],
'price': [3, 4, 2, 1, 6, 5, 5]})
вне:
name class price
0 a 1 3
1 a 2 4
4 b 2 6
6 c 3 5
очень хороший ответ, но я могу забыть случай, когда имени нет на карте, скажем name = 'c', class=3, price=5
, я все равно хочу сохранить эти строки.
@user6703592 user6703592 я обновляю свой ответ
@Ben.T, пожалуйста, посмотрите обновление, ожидаемый отфильтрованный фрейм данных