Это очевидно просто, но как новичок я застрял.
У меня есть CSV-файл, который содержит 3 столбца: штат, идентификатор офиса и продажи для этого офиса.
Я хочу рассчитать процент продаж на офис в данном состоянии (общее количество всех процентов в каждом штате составляет 100%).
df = pd.DataFrame({'state': ['CA', 'WA', 'CO', 'AZ'] * 3,
'office_id': range(1, 7) * 2,
'sales': [np.random.randint(100000, 999999)
for _ in range(12)]})
df.groupby(['state', 'office_id']).agg({'sales': 'sum'})
Это возвращает:
sales
state office_id
AZ 2 839507
4 373917
6 347225
CA 1 798585
3 890850
5 454423
CO 1 819975
3 202969
5 614011
WA 2 163942
4 369858
6 959285
Кажется, я не могу понять, как «дотянуться» до state
уровня, groupby
чтобы подвести итог sales
для всего, state
чтобы вычислить долю.
df['sales'] / df.groupby('state')['sales'].transform('sum')
кажется, самый ясный ответ.Ответы:
Ответ Павла Эйч это правильно , что вы должны сделать второй
groupby
объект, но вы можете вычислить процент более простым способом - просто и разделить колонку по ее сумме. Копирование начала ответа Пола Х:groupby
state_office
sales
Возвращает:
источник
x
это какая-то таблица, поэтому100 * x
она не имеет смысла (особенно когда некоторые ячейки содержат строки типаAZ
...).state_office
- это серия с мультииндексом, так что это всего лишь один столбец, все значения которого являются числовыми. После того, как вы выполните групповую работу, каждыйx
является подмножеством этого столбца. Имеет ли это смысл?level=0
значит?Вам нужно создать второй объект groupby, который группирует по состояниям, а затем использовать
div
метод:level='state'
kwarg вdiv
говорит панд широковещательного / присоединиться к базе dataframes на значения вstate
уровне индекса.источник
div
но с,level=["index1", "index2"]
но это говорит мне об этомJoin on level between two MultiIndex objects is ambiguous
.Для краткости я бы использовал SeriesGroupBy:
Для нескольких групп вы должны использовать transform (используя Radical's df ):
Похоже, что это немного более эффективно, чем другие ответы (чуть менее, чем в два раза быстрее ответа Радикала, для меня ~ 0,08 с).
источник
Я думаю, что это требует сравнительного анализа. Используя оригинальный DataFrame OP,
1-й Энди Хейден
Как прокомментировал его ответ, Энди в полной мере использует векторизацию и индексацию панд.
3,42 мс ± 16,7 мкс на цикл
(среднее ± стандартное отклонение из 7 циклов, по 100 циклов в каждом)
2-й Пол Н
4,66 мс ± 24,4 мкс на цикл
(среднее ± стандартное отклонение из 7 циклов, по 100 циклов в каждом)
3-й exp1orer
Это самый медленный ответ, так как он рассчитывается
x.sum()
для каждогоx
на уровне 0.Для меня это все еще полезный ответ, хотя и не в его нынешнем виде. Для быстрого EDA для небольших наборов данных
apply
позволяет использовать цепочку методов, чтобы записать это в одну строку. Поэтому мы убираем необходимость выбора имени переменной, которая на самом деле очень затратна в вычислительном отношении для вашего самого ценного ресурса (вашего мозга !!).Вот модификация,
10,6 мс ± 81,5 мкс на цикл
(среднее ± стандартное отклонение из 7 циклов, по 100 циклов в каждом)
Таким образом, никто не собирается заботиться о 6 мс на небольшом наборе данных. Тем не менее, это в 3 раза быстрее, и для больших наборов данных с большим количеством групповых пользователей это будет иметь огромное значение.
В дополнение к приведенному выше коду мы создаем DataFrame с формой (12 000 000, 3) с 14412 категориями состояний и 600 office_ids,
Используя Энди,
2 с ± 10,4 мс на цикл
(среднее ± стандартное отклонение из 7 циклов, по 1 циклу каждый)
и exp1orer
19 с ± 77,1 мс на цикл
(среднее ± стандартное отклонение из 7 циклов, по 1 циклу каждый)
Так что теперь мы видим ускорение в 10 раз на больших наборах данных с большим количеством элементов.
Обязательно УФ эти три ответа, если вы УФ этот !!
источник
(Это решение вдохновлено этой статьей https://pbpython.com/pandas_transform.html )
Я считаю, что следующее решение является самым простым (и, вероятно, самым быстрым) при использовании
transformation
:Таким образом, используя
transformation
решение, 1-лайнер:И если вы печатаете:
источник
transform('max')
Я знаю, что это старый вопрос, но ответ exp1orer очень медленный для наборов данных с большим количеством уникальных групп (вероятно, из-за лямбды). Я построил их ответ, чтобы превратить его в вычисление массива, так что теперь это очень быстро! Ниже приведен пример кода:
Создайте тестовый фрейм с 50 000 уникальных групп
При группировании это выглядит так:
Массив метод нахождения процента:
Этот метод занимает около ~ 0,15 секунд
Метод верхнего ответа (с использованием лямбда-функции):
Этот метод занимает около ~ 21 секунды, чтобы получить тот же результат.
Результат:
источник
Я понимаю, что здесь уже есть хорошие ответы.
Я, тем не менее, хотел бы внести свой вклад, потому что я чувствую к простому, простому вопросу, как этот, должно быть краткое решение, которое понятно с первого взгляда.
Он также должен работать таким образом, чтобы я мог добавить проценты в качестве нового столбца, оставив остальную часть кадра данных нетронутой. И последнее, но не менее важное: оно должно обобщаться очевидным образом на случай, когда существует более одного уровня группировки (например, штат и страна, а не только штат).
Следующий фрагмент кода соответствует этим критериям:
Обратите внимание, что если вы все еще используете Python 2, вам придется заменить x в знаменателе лямбда-термина на float (x).
источник
* 100
сделать процент.groupby
объекта, является очень кратким и очень логично читает слева направо.Самый элегантный способ найти проценты по столбцам или индексам - использовать
pd.crosstab
.Образец данных
Выходной кадр данных выглядит следующим образом
Просто укажите индекс, столбцы и значения для агрегирования. Ключевое слово normalize будет рассчитывать% по индексу или столбцам в зависимости от контекста.
источник
Вы можете
sum
целикомDataFrame
и поделить наstate
общее:Возвращает
Но обратите внимание, что это работает только потому, что все столбцы, кроме
state
числовых, позволяют суммировать весь DataFrame. Например, еслиoffice_id
вместо этого есть символ, вы получите ошибку:источник
groupby
столбца, являются числовыми. Но в остальном это довольно элегантно. Есть ли способ заставить его работать с другимиstr
столбцами?Я думаю, что это бы сработало в 1 строку:
источник
Простой способ, которым я воспользовался, - это слияние после того, как 2 groupby'а выполняют простое деление.
источник
Возвращает:
источник
Как человек, который также изучает панд, я нашел другие ответы несколько неявными, поскольку панды скрывают большую часть работы за кулисами. А именно в том, как работает операция, автоматически сопоставляя имена столбцов и индексов. Этот код должен быть эквивалентен пошаговой версии принятого ответа @ exp1orer
С
df
, я буду называть его псевдонимstate_office_sales
:state_total_sales
этоstate_office_sales
сгруппировано по общим суммам вindex level 0
(крайнем левом).Поскольку два фрейма данных совместно используют индексное имя и панды с именем столбца, они найдут подходящие местоположения через общие индексы, такие как:
Чтобы проиллюстрировать это еще лучше, вот частичное итоговое значение с
XX
эквивалентом. Панды будут сопоставлять местоположение на основе имен индексов и столбцов, где нет перекрывающихся панд, будут игнорировать его:Это становится очень ясно, когда нет общих индексов или столбцов. Здесь
missing_index_totals
равно, заstate_total_sales
исключением того, что он не имеет индексного имени.источник
Однолинейное решение:
Это возвращает ряд коэффициентов для каждого офиса - может использоваться отдельно или назначаться исходному кадру данных.
источник