Минимум на группу с условием - PullRequest
0 голосов
/ 21 апреля 2020

Рассмотрим dataframe d ниже:

d = pd.DataFrame({
    'id': ['A', 'A', 'A', 'B', 'B', 'B'],
    'name': ['a', 'b', 'd', 'a', 'c', 'e'],
    'value': [10, 8, 9, 11, 3, 5],
})

Я добавил пустой столбец d['col'] = pd.Series(), поэтому d выглядит так:

    id  name    value   col
0   A   a       10      NaN
1   A   b       8       NaN
2   A   d       9       NaN
3   B   a       11      NaN
4   B   c       3       NaN
5   B   e       5       NaN

Я хочу сейчас установить d.col в True, если для каждой группы, определенной d.id, если выполняется это условие:

d.value[d.name!='a'].min()

Конечный результат будет выглядеть следующим образом:

    id  name    value   col
0   A   a       10      NaN
1   A   b       8       True
2   A   d       9       NaN
3   B   a       11      NaN
4   B   c       3       True
5   B   e       5       NaN

Я подозреваю, что мне нужно каким-то образом использовать d.groupby('id').apply(), но я не могу понять, как правильно установить условие.

РЕДАКТИРОВАТЬ: Я открыт для любого аккуратного решения. Важно то, что я хочу иметь возможность для каждой группы выбрать «победителя» (где d.col==True) на основе условий, исключающих name=='a'.

Ответы [ 3 ]

3 голосов
/ 21 апреля 2020

Одним из способов является использование map:

min_values = d[d['name']!='a'].groupby('id')['value'].min()

d['col'] = d['value'] == d['id'].map(min_values)

Вывод:

  id name  value  col
0  A    a     10  NaN
1  A    b      8  1.0
2  A    d      9  NaN
3  B    a     11  NaN
4  B    c      3  1.0
5  B    e      5  NaN
3 голосов
/ 21 апреля 2020

Вы можете groupby и преобразовать с помощью min, проверяя также, что name не a, цепочкой результата с побитовым &:

# updated, thx @piR and @quang
d['col'] = d.groupby('id').value.transform('min').eq(d.value) & d.name.ne('a')

Исходя из вашего обновления, если вы хотите просто «выбрать победителя», давайте просто оставим первый матч простым. Поэтому возьмите idxmin, сравните с индексом (предполагая уникальный индекс) и составьте цепочку с другим условием, как указано выше:

d['col'] = d.groupby('id').value.transform('idxmin').eq(d.index) & d.name.ne('a')

print(d)

  id name  value    col
0  A    a     10  False
1  A    b      8   True
2  A    d      9  False
3  B    a     11  False
4  B    c      3   True
5  B    e      5  False
1 голос
/ 21 апреля 2020

вы можете сделать это с помощью sort_values и drop_duplicates после того, как выбраны все строки, в которых имя не равно ne 'a', например:

d.loc[d[d['name'].ne('a')].sort_values('value').drop_duplicates('id').index, 'col'] = 1
print (d)
  id name  value  col
0  A    a     10  NaN
1  A    b      8  1.0
2  A    d      9  NaN
3  B    a     11  NaN
4  B    c      3  1.0
5  B    e      5  NaN

Вот краткий пример того, как пока четыре решения (от @ Quang , @ Yatu и мое) дают другие результаты для других случаев, чем исходные входные данные из OP:

# add id C and D with specific values
d = pd.DataFrame({
    'id': ['A', 'A', 'A', 'B', 'B', 'B', 'C', 'C', 'C', 'D', 'D', 'D'],
    'name': ['a', 'b', 'd', 'a', 'c', 'e', 'a', 'f', 'g', 'a', 'h', 'i'],
    'value': [10, 8, 9, 11, 3, 5, 2, 5, 10, 12, 6, 6],
})

#Quang
min_values = d[d['name']!='a'].groupby('id')['value'].min()
d['col_Quang'] = d['value'] == d['id'].map(min_values)
#Yatu
d['col_Yatu_min'] = d.groupby('id').value.transform('min').eq(d.value) & d.name.ne('a')
d['col_Yatu_idx'] = d.groupby('id').value.transform('idxmin').eq(d.index) & d.name.ne('a')
# mine
d.loc[d[d['name'].ne('a')].sort_values('value').drop_duplicates('id').index, 'col_ben'] = True

print (d)
   id name  value  col_Quang  col_Yatu_min  col_Yatu_idx col_ben
0   A    a     10      False         False         False     NaN
1   A    b      8       True          True          True    True
2   A    d      9      False         False         False     NaN
3   B    a     11      False         False         False     NaN
4   B    c      3       True          True          True    True
5   B    e      5      False         False         False     NaN
6   C    a      2      False         False         False     NaN
7   C    f      5       True         False         False    True
8   C    g     10      False         False         False     NaN
9   D    a     12      False         False         False     NaN
10  D    h      6       True          True          True    True
11  D    i      6       True          True         False     NaN
...