Esta é uma extensão deste post .
Meu DataFrame é:
import pandas as pd
df = pd.DataFrame(
{
'main': ['x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'y', 'y', 'y', 'y', 'y', 'y', 'y'],
'sub': ['c', 'c', 'c', 'd', 'd', 'e', 'e', 'e', 'e', 'f', 'f', 'f', 'f', 'g', 'g', 'g'],
'num_1': [97, 90, 105, 2100, 1000, 101, 110, 222, 90, 100, 99, 90, 2, 92, 95, 93],
'num_2': [100, 100, 100, 102, 102, 209, 209, 209, 209, 100, 100, 100, 100, 90, 90, 90],
'num_3': [99, 110, 110, 110, 110, 222, 222, 222, 222, 150, 101, 200, 5, 95, 95, 100],
'label': ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p']
}
)
E este é o resultado esperado. Quero criar uma coluna result
:
main sub num_1 num_2 num_3 label result
0 x c 97 100 99 a b
1 x c 90 100 110 b b
2 x c 105 100 110 c b
3 x d 2100 102 110 d f
4 x d 1000 102 110 e f
5 x e 101 209 222 f f
6 x e 110 209 222 g f
7 x e 222 209 222 h f
8 x e 90 209 222 i f
9 y f 100 100 150 j k
10 y f 99 100 101 k k
11 y f 90 100 200 l k
12 y f 2 100 5 m k
13 y g 92 90 95 n NaN
14 y g 95 90 95 o NaN
15 y g 93 90 100 p NaN
A máscara é:
mask = (
(df.num_1 < df.num_2) &
(df.num_2 < df.num_3)
)
O processo começa assim:
a) A coluna groupby ésub
b) Encontrar a primeira linha que atenda à condição da máscara para cada grupo.
c) Coloque o valor de label
no resultado
Se não houver linhas que atendam à condição da máscara, a coluna groupby será alterada para main
para encontrar a primeira linha da máscara. Há condição para esta fase:
Os s anteriores sub
não devem ser considerados ao usar main
como groupby
coluna.
Um exemplo das etapas acima para grupo d
na subcoluna:
a) sub
é a coluna groupby.
b) Não há linhas no d
grupo que df.num_2
está entre df.num_1
e df.num_3
(a condição de mask
)
Então agora para group d
, seu grupo principal é pesquisado. No entanto, o grupo c
também está neste grupo principal. Como é anterior a group d
, group c
não deve contar para esta etapa. Portanto, no x
grupo, a primeira linha do rótulo mask
possui f
(101 <102 <222).
Uma coisa a notar é que para cada sub
grupo num_2
não muda em todo o grupo. Por exemplo, para todo o grupo c
num_2
é 100.
Esta é minha tentativa baseada nesta resposta , mas não funciona:
def find(g):
# get sub as 0,1,2…
sub = pd.factorize(g['sub'])[0]
# convert inputs to numpy
a = g['num_1'].to_numpy()
b = g.loc[~g['sub'].duplicated(), 'num_2'].to_numpy()
c = g['num_3'].to_numpy()
# form mask
# (a[:, None] > b) -> num_1 > num_2
# (sub[:, None] >= np.arange(len(b))) -> exclude previous groups
m = (a[:, None] < b) & (a[:, None] > c) & (sub[:, None] >= np.arange(len(b)))
# find first True per column
return pd.Series(np.where(m.any(0), a[m.argmax(0)], np.nan)[sub],
index=g.index)
df['result'] = df.groupby('main', group_keys=False).apply(find)
Você pode atualizar meu código anterior para usar duas comparações, preste atenção ao usar num_2 como colunas. Além disso, você precisa alterar a coluna de referência de saída para "label":
Saída: