Выбор строк, составляющих наибольшую группу (внутри другой группы) в DataFrame pandas

Основная проблема

Я пытаюсь найти способ выбрать строки, составляющие самую большую подгруппу внутри другой группы в Pandas DataFrame, и у меня возникли некоторые трудности.

Визуальный пример (код ниже)

Вот пример набора данных, который поможет точно объяснить, что я пытаюсь сделать. Ниже приведен код, позволяющий самостоятельно воссоздать этот набор данных, если хотите.

Предположим, я хочу сгруппировать эту таблицу по Col1 и выяснить, какое уникальное значение Col2 имеет наибольшее количество строк (внутри каждой группы Col1). Кроме того, я не хочу просто знать, какая группа самая большая — я хочу найти способ выбрать строки из исходного DataFrame, соответствующие этому описанию.

Итак, в этом случае мы легко можем увидеть, что для Col1= = "Group A" значение Col2 с наибольшим количеством строк равно "Type 3", а для Col1= = "Group B" значение Col2 с наибольшим количеством строк равно "Type 6".

Это означает, что я хочу выбрать строки с RowID in [10005, 10006, 10007, 10008, 10009, 10010, 10011, 10012, 10013, 10014, 10015].

Поэтому результат, который я ищу, будет следующим:

Моя неуклюжая попытка

Я нашел решение, но оно очень запутанное. Вот пошаговое объяснение того, что я сделал:

Шаг 1. Во-первых, для каждой группы Col1 я хочу подсчитать количество строк, существующих для каждого уникального значения Col2. Это довольно просто: я могу просто сделать groupby(['Col1','Col2']) и увидеть размер каждой группы.

Вот как это выглядит:

Обратите внимание, что для Col1= = "Group A", Col2= = "Type 1" имеет 2 наблюдения, Col2= = "Type 2" имеет 2 наблюдения и Col2= = "Type 3" имеет 6 наблюдений — как и ожидалось на основе наших исходных данных.

Шаг 2: Это сложнее: для каждой группы Col1 я хочу найти значение Col2, имеющее наибольшее количество строк из шага 1.

Вот как это выглядит:

Обратите внимание, что мы видим только случаи с «максимальным количеством строк».

Шаг 3. Наконец, я хочу отфильтровать исходные данные, чтобы отображались ТОЛЬКО те строки, которые соответствуют этой конкретной группе: те, которые были найдены на шаге 2.

Воспроизводимый пример и код

Вот код, иллюстрирующий мой пример:

# Importing the relevant library
import pandas as pd

# Creating my small reproducible example
my_df = pd.DataFrame({'RowID':[10001,10002,10003,10004,10005,10006,10007,10008,10009,10010,10011,10012,10013,10014,10015,10016,10017,10018,10019,10020],
                      'Col1':['Group A','Group A','Group A','Group A','Group A','Group A','Group A','Group A','Group A','Group A','Group B','Group B','Group B','Group B','Group B','Group B','Group B','Group B','Group B','Group B'],
                      'Col2':['Type 1','Type 1','Type 2','Type 2','Type 3','Type 3','Type 3','Type 3','Type 3','Type 3','Type 6','Type 6','Type 6','Type 6','Type 6','Type 3','Type 3','Type 2','Type 2','Type 2'],
                      'Col3':[100,200,300,400,500,600,700,800,900,1000,2000,1900,1800,1700,1600,1500,1400,1300,1200,1100],
                      'Col4':['Alice','Bob','Carl','Dave','Earl','Fred','Greg','Henry','Iris','Jasmine','Kris','Lonnie','Manny','Norbert','Otis','Pearl','Quaid','Randy','Steve','Tana']})

# Solving Step 1: finding the unique groupings and their relative sizes
temp1 = my_df.groupby(['Col1','Col2']).agg({'RowID':'count'}).reset_index()

# Solving Step 2: finding which grouping is the largest
temp2 = temp1.groupby(['Col1']).agg({'RowID':'max'}).reset_index()

# Solving Step 3: finding which rows of the original DataFrame match what was 
# found in Step 2

# Step 3 Part 1: Finding the actual combination of `Col1` & `Col2` that let to 
# the largest number of rows
temp3 = (temp1
         .rename(columns = {'RowID':'RowID_count'})
         .merge(temp2
                .rename(columns = {'RowID':'RowID_max'}), 
                how='left', 
                on='Col1')
         .assign(RowID_ismax = lambda _df: _df['RowID_count']== _df['RowID_max'])
         .query('RowID_ismax')
         .drop(columns=['RowID_count','RowID_max']))

# Step 3 Part 2: Finding the matching rows in the original dataset and 
# filtering it down
result = (my_df
          .merge(temp3, 
                 how='left', 
                 on=['Col1','Col2'])
          .assign(RowID_ismax = lambda _df: _df['RowID_ismax'].fillna(False))
          .query('RowID_ismax')
          .reset_index(drop=True)
          .drop(columns=['RowID_ismax']))

Решение, приведенное выше, ОЧЕНЬ запутанное, полное утверждений assign и lambda наряду с несколькими последовательными groupby и reset_index, что наводит на мысль, что я подхожу к этому неправильно.

Любая помощь в этом вопросе будет принята с благодарностью.

Почему в Python есть оператор "pass"?
Почему в Python есть оператор "pass"?
Оператор pass в Python - это простая концепция, которую могут быстро освоить даже новички без опыта программирования.
Некоторые методы, о которых вы не знали, что они существуют в Python
Некоторые методы, о которых вы не знали, что они существуют в Python
Python - самый известный и самый простой в изучении язык в наши дни. Имея широкий спектр применения в области машинного обучения, Data Science,...
Основы Python Часть I
Основы Python Часть I
Вы когда-нибудь задумывались, почему в программах на Python вы видите приведенный ниже код?
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
LeetCode - 1579. Удаление максимального числа ребер для сохранения полной проходимости графа
Алиса и Боб имеют неориентированный граф из n узлов и трех типов ребер:
Оптимизация кода с помощью тернарного оператора Python
Оптимизация кода с помощью тернарного оператора Python
И последнее, что мы хотели бы показать вам, прежде чем двигаться дальше, это
Советы по эффективной веб-разработке с помощью Python
Советы по эффективной веб-разработке с помощью Python
Как веб-разработчик, Python может стать мощным инструментом для создания эффективных и масштабируемых веб-приложений.
2
0
50
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Короткий код для этого может value_counts + idxmax , а затем объединить:

keep = my_df[['Col1', 'Col2']].value_counts().groupby(level='Col1').idxmax()

out = my_df.merge(pd.DataFrame(keep.tolist(), columns=['Col1', 'Col2']))

Выход:

    RowID     Col1    Col2  Col3     Col4
0   10005  Group A  Type 3   500     Earl
1   10006  Group A  Type 3   600     Fred
2   10007  Group A  Type 3   700     Greg
3   10008  Group A  Type 3   800    Henry
4   10009  Group A  Type 3   900     Iris
5   10010  Group A  Type 3  1000  Jasmine
6   10011  Group B  Type 6  2000     Kris
7   10012  Group B  Type 6  1900   Lonnie
8   10013  Group B  Type 6  1800    Manny
9   10014  Group B  Type 6  1700  Norbert
10  10015  Group B  Type 6  1600     Otis

Промежуточные продукты:

# my_df[['Col1', 'Col2']].value_counts()
Col1     Col2  
Group A  Type 3    6
Group B  Type 6    5
         Type 2    3
Group A  Type 1    2
         Type 2    2
Group B  Type 3    2
Name: count, dtype: int64

# my_df[['Col1', 'Col2']].value_counts().groupby(level='Col1').idxmax()
Col1
Group A    (Group A, Type 3)
Group B    (Group B, Type 6)
Name: count, dtype: object

В качестве альтернативы можно использовать groupby.transform (который выберет несколько групп, если их несколько с максимальным размером):

s = my_df.groupby(['Col1', 'Col2']).transform('size')
out = my_df[s.groupby(my_df['Col1']).transform('max').eq(s)]

Это здорово, спасибо вам огромное!

Felipe D. 17.08.2024 23:25

Другие вопросы по теме