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