Как условно выбрать первые N групп на основе значений столбца?

Это мой DataFrame:

import pandas as pd 
df = pd.DataFrame(
    {
        'a': [10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 10, 22],
        'b': [1, 1, 1, -1, -1, -1, -1, 2, 2, 2, 2, -1, -1, -1, -1],
        'c': [25, 25, 25, 45, 45, 45, 45, 65, 65, 65, 65, 40, 40, 30, 30]
    }
)

Ожидаемый результат: группировка df по c и условие:

     a  b   c
0   10  1  25
1   15  1  25
2   20  1  25
3   25 -1  45
4   30 -1  45
5   35 -1  45
6   40 -1  45
11  65 -1  40
12  70 -1  40

Процесс выглядит следующим образом:

а) Выбор группы, в которой все значения b относятся к 1. По моим данным и этому df есть только одна группа с таким заболеванием.

б) Выбор первых двух групп (сверху df), все их значения b равны -1.

Например:

а) Выбрана группа 25.

б) Есть три группы с этим заболеванием. Первые две группы: Группа 45 и 40.

Обратите внимание, что в моих данных существует вероятность того, что нет групп с условием a или b. В этом случае можно вернуть все, что соответствует критериям. Например, на выходе может быть только одна группа или вообще не быть групп.

Группы, которые мне нужны, показаны ниже:

Это мои попытки, которые подошли очень близко:

df1 = df.groupby('c').filter(lambda g: g.b.eq(1).all())
gb = df.groupby('c')
new_gb = pd.concat([gb.get_group(group) for i, group in enumerate(gb.groups) if i < 2])
Почему в 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 может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
4
0
88
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Вы можете использовать собственные маски для логического индексирования:

# identify groups with all 1
m1 = df['b'].eq(1).groupby(df['c']).transform('all')
# identify groups with all -1
m2 = df['b'].eq(-1).groupby(df['c']).transform('all')
# keep rows of first 2 groups with all -1
m3 = df['c'].isin(df.loc[m2, 'c'].unique()[:2])

# select m1 OR m3
out = df[m1 | m3]

Или, для варианта без groupby, используя операции set:

# identify rows with 1/-1
m1 = df['b'].eq(1)
m2 = df['b'].eq(-1)

# drop c that have values other that 1/-1:  {65}
# drop -1 groups after 2nd occurrence:      {30}
drop = set(df.loc[~(m1|m2), 'c']) | set(df.loc[m2, 'c'].unique()[2:])

out = df[~df['c'].isin(drop)]

Выход:

     a  b   c
0   10  1  25
1   15  1  25
2   20  1  25
3   25 -1  45
4   30 -1  45
5   35 -1  45
6   40 -1  45
11  65 -1  40
12  70 -1  40

Промежуточные (первый подход):

     a  b   c     m1     m2     m3
0   10  1  25   True  False  False
1   15  1  25   True  False  False
2   20  1  25   True  False  False
3   25 -1  45  False   True   True
4   30 -1  45  False   True   True
5   35 -1  45  False   True   True
6   40 -1  45  False   True   True
7   45  2  65  False  False  False
8   50  2  65  False  False  False
9   55  2  65  False  False  False
10  60  2  65  False  False  False
11  65 -1  40  False   True   True
12  70 -1  40  False   True   True
13  10 -1  30  False   True  False
14  22 -1  30  False   True  False

Промежуточные (второй подход):

     a  b   c     m1     m2  ~isin(drop)
0   10  1  25   True  False         True
1   15  1  25   True  False         True
2   20  1  25   True  False         True
3   25 -1  45  False   True         True
4   30 -1  45  False   True         True
5   35 -1  45  False   True         True
6   40 -1  45  False   True         True
7   45  2  65  False  False        False
8   50  2  65  False  False        False
9   55  2  65  False  False        False
10  60  2  65  False  False        False
11  65 -1  40  False   True         True
12  70 -1  40  False   True         True
13  10 -1  30  False   True        False
14  22 -1  30  False   True        False

Большое спасибо. Если я хочу сгруппировать по двум столбцам, что будет m3? m3 = df['c'].isin(df.loc[m2, ['c', 'x']].unique()[:2]) ?

AmirX 05.04.2024 10:23

@AmirX это было бы немного сложнее, возможно, с использованием слияния. Можете ли вы создать MRE? может быть, в качестве дополнительного вопроса?

mozway 05.04.2024 10:24

Все то же самое. Только с двумя группами groupby: stackoverflow.com/q/78278705/10200497

AmirX 05.04.2024 10:39
import pandas as pd
import numpy as np


# Apply the function to your DataFrame
df = pd.DataFrame(
    {
        'a': [10, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 10, 22],
        'b': [1, 1, 1, -1, -1, -1, -1, 2, 2, 2, 2, -1, -1, -1, -1],
        'c': [25, 25, 25, 45, 45, 45, 45, 65, 65, 65, 65, 40, 40, 30, 30],
        'main': ['x', 'x', 'x', 'x', 'x', 'x', 'x', 'y', 'y', 'y', 'y', 'y', 'y', 'y', 'y']
    }
)


# Identify groups with all 1s and all -1s
all_one_mask = df.groupby(['main', 'c'])['b'].transform(lambda x: (x == 1).all())
all_minusone_mask = df.groupby(['main', 'c'])['b'].transform(lambda x: (x == -1).all())
all_one = df[all_one_mask]
all_minusone = df[all_minusone_mask]


first_two_minusone_groups = df[all_minusone_mask].drop_duplicates('c').head(2)

print(first_two_minusone_groups )
"""
     a  b   c main
3   25 -1  45    x
11  65 -1  40    y
"""
c_in_top_two_minus_one_groups_boolean = df['c'].isin(first_two_minusone_groups['c'])

res = df[all_one_mask | c_in_top_two_minus_one_groups_boolean]
print(res)
"""
     a  b   c main
0   10  1  25    x
1   15  1  25    x
2   20  1  25    x
3   25 -1  45    x
4   30 -1  45    x
5   35 -1  45    x
6   40 -1  45    x
11  65 -1  40    y
12  70 -1  40    y
"""
#OR
# Combine conditions using np.where
condition = np.where(all_one_mask, True, df['c'].isin(first_two_minusone_groups['c']))
out = df[condition]
print(out)
"""
     a  b   c main
0   10  1  25    x
1   15  1  25    x
2   20  1  25    x
3   25 -1  45    x
4   30 -1  45    x
5   35 -1  45    x
6   40 -1  45    x
11  65 -1  40    y
12  70 -1  40    y
"""

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

Как проанализировать текстовый файл с заголовками, состоящими из нескольких строк в Python
Создайте Dataframe из серии и, в частности, как переименовать столбец в нем (пример: с помощью NA/NaN)
Сделать глобальные переменные локальными переменными
Pandas.DataFrame.fillna с логическими значениями?
Заполнение пустого фрейма данных или массива значениями из столбца другого фрейма данных, если соблюдены некоторые условия pandas
Самый быстрый способ опубликовать фрейм данных pandas из Jupyter Notebooks в проблему переполнения стека?
Панды подсчитывают количество значений True/False в одном столбце для уникального набора/группировки другого столбца
Задача оценки цены дома r2 с помощью множественной линейной регрессии
Pandas Pivot/присоединение строк к столбцам
Преобразование в столбец с плавающей запятой, строковый столбец pandas со смешанными тысячными и десятичными разделителями