Это мой 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])
Вы можете использовать собственные маски для логического индексирования:
# 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
@AmirX это было бы немного сложнее, возможно, с использованием слияния. Можете ли вы создать MRE? может быть, в качестве дополнительного вопроса?
Все то же самое. Только с двумя группами groupby
: stackoverflow.com/q/78278705/10200497
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
"""
Большое спасибо. Если я хочу сгруппировать по двум столбцам, что будет
m3
?m3 = df['c'].isin(df.loc[m2, ['c', 'x']].unique()[:2])
?