Держите самый высокий одиночный матч для каждой группы - PullRequest
1 голос
/ 21 мая 2019

Я пытаюсь использовать pandas для фильтрации на основе условий 2 groupby

Допустим, у меня есть следующие данные:

  • каждая строка представляет сравнения вещей (id1, id2) соответственно из 2 источников данных (src1, src2)
  • оценка показывает, насколько похожи вещи сдруг друга (чем выше, тем лучше)
data = [
    {'src1': 'A', 'id1': '111', 'src2': 'B', 'id2': '111', 'score': 10},
    {'src1': 'A', 'id1': '222', 'src2': 'B', 'id2': '222', 'score': 9},
    {'src1': 'A', 'id1': '111', 'src2': 'B', 'id2': '222', 'score': 2},
    {'src1': 'A', 'id1': '222', 'src2': 'B', 'id2': '111', 'score': 4},
    {'src1': 'A', 'id1': 'default', 'src2': 'B', 'id2': '111', 'score': 3},
    {'src1': 'A', 'id1': 'default', 'src2': 'B', 'id2': '222', 'score': 3},
]

То, что я пытаюсь сделать, это groupby src1 + id1 + src2 и хранить только строки с наивысшим счетом И количество = 1

И вот мой код:

df = pd.DataFrame(data)
df['count'] = 1
groups = df.groupby(['src1', 'id1', 'src2', 'score']).agg(
    {'id2': 'unique', 'count': 'sum'})
print(groups)

И я получаю следующее:

                                id2  count
src1 id1     src2 score                   
A    111     B    2           [222]      1 # DISCARD because below has higher score (10>2)
                  10          [111]      1 # KEEP
     222     B    4           [111]      1 # DISCARD because below has higher score (9>4)
                  9           [222]      1 # KEEP
     default B    3      [111, 222]      2 # DISCARD because count=2

Проблемы, которые у меня возникают:

  • WITHOUT reset_index(): если я не использую reset_index(), всякий раз, когда я пытаюсь получить доступ к счету или счету для фильтрации, я получаю KeyError
groups = df.groupby(['src1', 'id1', 'src2', 'score']).agg(
    {'id2': 'unique', 'count': 'sum'})
groups[groups['score'] == groups['score'].max()]

KeyError: 'score'
  • reset_index(): если я его использую, то я «теряю» свой групповой режим (т. Е. Каждая строка становится новой отдельной строкой, а результат моей фильтрации приводит только к 1 строке *)1049 *
reset = groups.reset_index()
reset[reset['score'] == reset['score'].max()]

  src1  id1 src2  score  count    id2
1    A  111    B     10      1  [111]

Как мне продлитьd ниже выражения, так что для EACH GROUP I ONLY KEEP групповых строк с MAX score и count=1?

groups = df.groupby(['src1', 'id1', 'src2', 'score']).agg(
    {'id2': 'unique', 'count': 'sum'})

Ответы [ 2 ]

2 голосов
/ 21 мая 2019

Используйте GroupBy.transform для получения счетчика без нового вспомогательного столбца с GroupBy.size, затем аналогичные значения max для разных столбцов группировки, сравнение и фильтрация по boolean indexing с цепочкой условий на & для bitwise AND:

m1 = df.groupby(['src1', 'id1', 'src2', 'score'])['id1'].transform('size') <= 1
m2 = df.groupby(['src1', 'id1', 'src2'])['score'].transform('max') == df['score']


df = df[m1 & m2]
print (df)
   id1  id2  score src1 src2
0  111  111     10    A    B
1  222  222      9    A    B

Ваше решение должно быть изменено:

df['count'] = 1
groups = df.groupby(['src1', 'id1', 'src2', 'score']).agg(
    {'id2': 'unique', 'count': 'sum'})

m1 = groups['count'] <= 1
df = groups.reset_index(level=3)
m2 = (df.groupby(level=[0,1,2])['score'].transform('max') == df['score']).values

groups = groups[m1 & m2]
print (groups)
                       id2  count
src1 id1 src2 score              
A    111 B    10     [111]      1
     222 B    9      [222]      1
1 голос
/ 21 мая 2019

Один из способов

#your code
df = pd.DataFrame(data)
df['count'] = 1
groups = df.groupby(['src1', 'id1', 'src2', 'score']).agg(
    {'id2': 'unique', 'count': 'sum'})

# Then do
groups = groups.sort_values(by=['src1', 'id1', 'src2', 'score'], ascending=False)
groups = groups.groupby(['src1', 'id1', 'src2',]).head(1)[groups['count']==1]

                      id2  count
src1 id1 src2 score              
A    222 B    9      [222]      1
     111 B    10     [111]      1
...