Pandas: как выбрать первую строку в каждой группе GROUP BY?

В принципе то же самое, что Выберите первую строку в каждой группе GROUP BY? только в pandas.

df = pd.DataFrame({'A' : ['foo', 'foo', 'foo', 'foo', 'bar', 'bar', 'bar', 'bar'],
                'B' : ['3', '1', '2', '4','2', '4', '1', '3'],
                    })

Сортировка выглядит многообещающе:

df.sort('B')

     A  B
1  foo  1
6  bar  1
2  foo  2
4  bar  2
0  foo  3
7  bar  3
3  foo  4
5  bar  4

Но тогда сначала не дадут желаемого результата...   df.groupby( 'А'). первый()

     B
A     
bar  2
foo  3

Ответ 1

Как правило, если вы хотите, чтобы ваши данные сортировались по группам, но это не один из столбцов, по которым будет группироваться, то лучше выполнить sort df до выполнения groupby:

In [5]:
df.sort_values('B').groupby('A').first()

Out[5]:
     B
A     
bar  1
foo  1

Ответ 2

Функция pandas groupby может использоваться для того, что вы хотите, но она действительно предназначена для агрегирования. Это простая операция "возьми первым".

На самом деле вам нужна функция pandas drop_duplicates, которая по умолчанию возвращает первую строку. То, что вы обычно рассматриваете как ключ groupby, вы должны передать как переменную subset =

df.drop_duplicates(subset='A')

Должен делать то, что вы хотите.

Кроме того, df.sort('A') не сортирует DataFrame df, а возвращает копию, которая отсортирована. Если вы хотите отсортировать его, вы должны добавить параметр inplace=True.

df.sort('A', inplace=True)

Ответ 3

Вот альтернативный подход с использованием groupby().rank():

df[ df.groupby('A')['B'].rank() == 1 ]

     A  B
1  foo  1
6  bar  1

Это дает тот же ответ, что и @EdChum для образца данных OP, но может дать другой ответ, если у вас есть какие-либо связи во время сортировки, например, с такими данными:

df = pd.DataFrame({'A': ['foo', 'foo', 'bar', 'bar'], 
                   'B': ['2', '1', '1', '1'] })

В этом случае у вас есть несколько опций, использующих необязательный аргумент method, в зависимости от того, как вы хотите обрабатывать связи сортировки:

df[ df.groupby('A')['B'].rank(method='average') == 1 ]   # the default
df[ df.groupby('A')['B'].rank(method='min')     == 1 ]
df[ df.groupby('A')['B'].rank(method='first')   == 1 ]   # doesn't work, not sure why

Ответ 4

Обычно вы используете GroupBy если необходимо выполнить вычисления для каждой группы (см.: шаблон split-apply-Объединить).

Если вы просто хотите сохранить первую строку для каждого уникального значения столбца (или уникальной комбинации столбцов), вы можете отсортировать с помощью .sort_values() (или .sort_index()) и впоследствии сохранять каждый первый .drop_duplicates() с помощью .drop_duplicates(),

df.sort_values('A', ascending=True).drop_duplicates('A', keep='first')

Этот подход дает вам неразрушающий результат, при котором сохраняются исходная структура и индекс DataFrame:

    A   B
4   bar 2
0   foo 3

Ответ 5

Ответ EdChum может не всегда работать так, как задумано. Вместо first() используйте nth(0).

Метод first() подвержен влиянию этой ошибки, которая не решалась в течение нескольких лет. Вместо ожидаемого поведения first() возвращает первый элемент , который не пропущен в каждом столбце в каждой группе, т.е. он игнорирует значения NaN. Например, у вас есть третий столбец с пропущенными значениями:

df = pd.DataFrame({'A' : ['foo', 'foo', 'bar', 'bar', 'bar'],
                   'B' : ['1', '2','2', '4', '1'],
                   'C' : [np.nan, 'X', 'Y', 'Y', 'Y']})

    A   B   C
0   foo 1   NaN
2   foo 2   X
3   bar 2   Y
4   bar 4   Y
5   bar 1   Y

Использование здесь first() (после сортировки, точно так же, как EdChum правильно оценил в своем ответе) пропустит пропущенные значения (обратите внимание, как он смешивает значения из разных строк):

df.sort_values('B').groupby('A').first()

    B   C
A       
bar 1   Y
foo 1   X

Правильный способ получения полной строки, включая пропущенные значения, заключается в использовании nth(0), который выполняет ожидаемую операцию:

df.sort_values('B').groupby('A').nth(0)

    B   C
A       
bar 1   Y
foo 1   NaN

Для полноты картины эта ошибка также влияет на last(), его правильной заменой является nth(-1).

Размещать это в ответ, так как это слишком долго для комментария. Не уверен, что это в рамках вопроса, но я думаю, что это актуально для многих людей, которые ищут этот ответ (например, я, прежде чем писать это), и его очень легко пропустить.