Группировка фильтра кадров данных на основе подмножества

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

🤔 А знаете ли вы, что...
Python - это универсальный язык программирования.


1
69
3

Ответы:

Вы можете сделать это, используя 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

Вы можете получить 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