Сохраняйте другие столбцы при групповом
Я использую groupby
на фрейме данных pandas для удаления всех строк, у которых нет минимума определенного столбца. Примерно так:
df1 = df.groupby("item", as_index=False)["diff"].min()
Однако, если у меня больше этих двух столбцов, другие столбцы (например, otherstuff
в моем примере) будут удалены. Могу ли я сохранить эти столбцы, используя groupby
, или мне придется искать другой способ отбрасывания строк?
Мои данные выглядят так:
item diff otherstuff
0 1 2 1
1 1 1 2
2 1 3 7
3 2 -1 0
4 2 1 3
5 2 4 9
6 2 -6 2
7 3 0 0
8 3 2 9
и должно закончиться как:
item diff otherstuff
0 1 1 2
1 2 -6 2
2 3 0 0
но я получаю это:
item diff
0 1 1
1 2 -6
2 3 0
Я просматривал документацию и ничего не могу найти. Я пытался:
df1 = df.groupby(["item", "otherstuff"], as_index=false)["diff"].min()
df1 = df.groupby("item", as_index=false)["diff"].min()["otherstuff"]
df1 = df.groupby("item", as_index=false)["otherstuff", "diff"].min()
Но ни одна из этих работ (с последним я понял, что синтаксис предназначен для агрегирования после создания группы).
Ответы
Ответ 1
Метод # 1: используйте idxmin()
чтобы получить индексы элементов минимальной diff
, а затем выберите те:
>>> df.loc[df.groupby("item")["diff"].idxmin()]
item diff otherstuff
1 1 1 2
6 2 -6 2
7 3 0 0
[3 rows x 3 columns]
Способ № 2: сортировка по diff
, а затем взять первый элемент в каждой группе item
:
>>> df.sort_values("diff").groupby("item", as_index=False).first()
item diff otherstuff
0 1 1 2
1 2 -6 2
2 3 0 0
[3 rows x 3 columns]
Обратите внимание, что результирующие индексы отличаются друг от друга, хотя содержимое строк одинаково.
Ответ 2
Вы можете использовать DataFrame.sort_values
с DataFrame.drop_duplicates
:
df = df.sort_values(by='diff').drop_duplicates(subset='item')
print (df)
item diff otherstuff
6 2 -6 2
7 3 0 0
1 1 1 2
Если возможно, несколько минимальных значений для групп и для всех минимальных строк используйте boolean indexing
с transform
для минимальных значений для групп:
print (df)
item diff otherstuff
0 1 2 1
1 1 1 2 <-multiple min
2 1 1 7 <-multiple min
3 2 -1 0
4 2 1 3
5 2 4 9
6 2 -6 2
7 3 0 0
8 3 2 9
print (df.groupby("item")["diff"].transform('min'))
0 1
1 1
2 1
3 -6
4 -6
5 -6
6 -6
7 0
8 0
Name: diff, dtype: int64
df = df[df.groupby("item")["diff"].transform('min') == df['diff']]
print (df)
item diff otherstuff
1 1 1 2
2 1 1 7
6 2 -6 2
7 3 0 0
Ответ 3
Вышеприведенный ответ отлично подойдет, если вам понадобится 1 мин. В моем случае может быть несколько минут, и мне нужны все строки, равные min, которые .idxmin()
не дает вам. Это сработало
def filter_group(dfg, col):
return dfg[dfg[col] == dfg[col].min()]
df = pd.DataFrame({'g': ['a'] * 6 + ['b'] * 6, 'v1': (list(range(3)) + list(range(3))) * 2, 'v2': range(12)})
df.groupby('g',group_keys=False).apply(lambda x: filter_group(x,'v1'))
В стороне, .filter() также имеет отношение к этому вопросу, но не работает для меня.
Ответ 4
Если вы знаете, что все ваши "предметы" имеют более одной записи, которую вы можете отсортировать, используйте duplicated
:
df.sort_values(by='diff').duplicated(subset='item', keep='first')