Obviamente, isso é simples, mas como um novato entorpecido, estou ficando preso.
Eu tenho um arquivo CSV que contém 3 colunas, o Estado, o ID do escritório e as vendas desse escritório.
Desejo calcular a porcentagem de vendas por escritório em um determinado estado (o total de todas as porcentagens em cada estado é 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'})
Isso retorna:
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
Parece que não consigo descobrir como "alcançar" o state
nível do groupby
total para calcular o sales
total state
da fração.
df['sales'] / df.groupby('state')['sales'].transform('sum')
parece ser a resposta mais clara.Respostas:
A resposta de Paul H é certo que você terá que fazer um segundo
groupby
objeto, mas você pode calcular a porcentagem de uma forma mais simples - apenasgroupby
ostate_office
e dividir asales
coluna pela sua soma. Copiando o início da resposta de Paul H:Devoluções:
fonte
x
é uma tabela de algum tipo, portanto100 * x
, intuitivamente, não faz sentido (especialmente quando algumas das células contêm seqüências de caracteres comoAZ
...).state_office
é uma série com um índice múltiplo - portanto, é apenas uma coluna cujos valores são todos numéricos. Depois de fazer o agrupamento, cadax
um é um subconjunto dessa coluna. Isso faz sentido?level=0
significa isso ?Você precisa criar um segundo objeto groupby que agrupe pelos estados e, em seguida, use o
div
método:o
level='state'
kwarg indiv
diz aos pandas para transmitir / juntar-se à base de quadros de dados com base nos valores nostate
nível do índice.fonte
div
mas comlevel=["index1", "index2"]
mas me diz issoJoin on level between two MultiIndex objects is ambiguous
.Por concisão, eu usaria o SeriesGroupBy:
Para vários grupos, você deve usar a transformação (usando o df do Radical ):
Isso parece ser um pouco mais eficiente do que as outras respostas (pouco menos do que o dobro da velocidade da resposta de Radical, para mim ~ 0,08s).
fonte
Eu acho que isso precisa de benchmarking. Usando o DataFrame original do OP,
1st Andy Hayden
Como comentado em sua resposta, Andy aproveita ao máximo a vetorização e a indexação de pandas.
3,42 ms ± 16,7 µs por loop
(média ± desvio padrão de 7 corridas, 100 loops cada)
2nd Paul H
4,66 ms ± 24,4 µs por loop
(média ± desvio padrão de 7 corridas, 100 loops cada)
Terceiro exp1orer
Essa é a resposta mais lenta, pois calcula
x.sum()
para cada umax
no nível 0.Para mim, essa ainda é uma resposta útil, embora não em sua forma atual. Para um rápido EDA em conjuntos de dados menores,
apply
você pode usar o encadeamento de métodos para escrever isso em uma única linha. Portanto, removemos a necessidade de decidir o nome de uma variável, que na verdade é muito caro para o seu recurso mais valioso (seu cérebro !!).Aqui está a modificação,
10,6 ms ± 81,5 µs por loop
(média ± desvio padrão de 7 corridas, 100 loops cada)
Portanto, ninguém vai se importar com 6ms em um pequeno conjunto de dados. No entanto, isso é 3x mais rápido e, em um conjunto de dados maior com grupos de alta cardinalidade, isso fará uma diferença enorme.
Adicionando ao código acima, criamos um DataFrame com a forma (12.000.000, 3) com 14412 categorias de estado e 600 office_ids,
Usando Andy,
2 s ± 10,4 ms por loop
(média ± desvio padrão de 7 corridas, 1 loop cada)
e exp1orer
19 s ± 77,1 ms por loop
(média ± desvio padrão de 7 corridas, 1 loop cada)
Então agora vemos x10 acelerar em conjuntos de dados grandes e de alta cardinalidade.
Certifique-se de UV estas três respostas se você UV este !!
fonte
(Esta solução foi inspirada neste artigo https://pbpython.com/pandas_transform.html )
Acho a seguinte solução a mais simples (e provavelmente a mais rápida) usando
transformation
:Então
transformation
, usando , a solução é 1-liner:E se você imprimir:
fonte
transform('max')
Eu sei que essa é uma pergunta antiga, mas a resposta do exportador é muito lenta para conjuntos de dados com um grande número de grupos únicos (provavelmente por causa do lambda). Eu construí sua resposta para transformá-lo em um cálculo de matriz, agora é super rápido! Abaixo está o código de exemplo:
Crie o quadro de dados de teste com 50.000 grupos exclusivos
Quando agrupados, parece com:
Método de matriz para encontrar a porcentagem:
Este método leva cerca de 0,15 segundos
Método de resposta principal (usando a função lambda):
Este método leva cerca de 21 segundos para produzir o mesmo resultado.
O resultado:
fonte
Sei que já existem boas respostas aqui.
Não obstante, gostaria de contribuir com a minha, porque, para uma pergunta simples e elementar como essa, deve haver uma solução curta que seja compreensível à primeira vista.
Também deve funcionar de maneira que eu possa adicionar as porcentagens como uma nova coluna, deixando o restante do quadro de dados intocado. Por último, mas não menos importante, deve generalizar de maneira óbvia o caso em que há mais de um nível de agrupamento (por exemplo, estado e país em vez de apenas estado).
O seguinte snippet atende a estes critérios:
Observe que, se você ainda estiver usando o Python 2, precisará substituir o x no denominador do termo lambda por float (x).
fonte
* 100
torná-lo uma porcentagem.groupby
objeto temporário , é super conciso e lê muito logicamente da esquerda para a direita.A maneira mais elegante de encontrar porcentagens entre colunas ou índice é usar
pd.crosstab
.Dados de amostra
O dataframe de saída é assim
Basta especificar o índice, as colunas e os valores a serem agregados. A palavra-chave normalize calculará% no índice ou nas colunas, dependendo do contexto.
fonte
Você pode
sum
o todoDataFrame
e dividir pelostate
total:Devoluções
Mas observe que isso funciona apenas porque todas as colunas que não
state
são numéricas, permitindo a soma de todo o DataFrame. Por exemplo, seoffice_id
for um caractere, você receberá um erro:fonte
groupby
coluna, são numéricas. Mas, de outra forma, é bastante elegante. Existe uma maneira de fazê-lo funcionar com outrasstr
colunas?Eu acho que isso faria o truque em 1 linha:
fonte
A maneira simples que usei é uma mesclagem após os 2 groupby's que estão fazendo a divisão simples.
fonte
Devoluções:
fonte
Como alguém que também está aprendendo pandas, achei as outras respostas um pouco implícitas, pois os pandas escondem a maior parte do trabalho nos bastidores. Ou seja, como a operação funciona, correspondendo automaticamente os nomes de colunas e índices. Este código deve ser equivalente a uma versão passo a passo da resposta aceita do @ exp1orer
Com o
df
, chamarei pelo apelidostate_office_sales
:state_total_sales
éstate_office_sales
agrupado por somas totais emindex level 0
(mais à esquerda).Como os dois quadros de dados compartilham um nome de índice e um panda de nome de coluna, eles encontrarão os locais apropriados por meio de índices compartilhados, como:
Para ilustrar isso ainda melhor, aqui está um total parcial com um
XX
que não tem equivalente. O Pandas corresponderá ao local com base nos nomes de índice e coluna, onde não há sobreposição, o pandas o ignorará:Isso fica muito claro quando não há índices ou colunas compartilhados. Aqui
missing_index_totals
é igual a,state_total_sales
exceto que não possui um nome de índice.fonte
Solução de uma linha:
Isso retorna uma série de taxas por escritório - pode ser usado sozinho ou atribuído ao Dataframe original.
fonte