Porcentagem de pandas do total com groupby

148

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 statenível do groupbytotal para calcular o salestotal stateda fração.

erikcw
fonte
3
df['sales'] / df.groupby('state')['sales'].transform('sum')parece ser a resposta mais clara.
Paul Rougieux

Respostas:

207

A resposta de Paul H é certo que você terá que fazer um segundo groupbyobjeto, mas você pode calcular a porcentagem de uma forma mais simples - apenas groupbyo state_officee dividir a salescoluna pela sua soma. Copiando o início da resposta de Paul H:

# From Paul H
import numpy as np
import pandas as pd
np.random.seed(0)
df = pd.DataFrame({'state': ['CA', 'WA', 'CO', 'AZ'] * 3,
                   'office_id': list(range(1, 7)) * 2,
                   'sales': [np.random.randint(100000, 999999)
                             for _ in range(12)]})
state_office = df.groupby(['state', 'office_id']).agg({'sales': 'sum'})
# Change: groupby state_office and divide by sum
state_pcts = state_office.groupby(level=0).apply(lambda x:
                                                 100 * x / float(x.sum()))

Devoluções:

                     sales
state office_id           
AZ    2          16.981365
      4          19.250033
      6          63.768601
CA    1          19.331879
      3          33.858747
      5          46.809373
CO    1          36.851857
      3          19.874290
      5          43.273852
WA    2          34.707233
      4          35.511259
      6          29.781508
exp1orer
fonte
1
O que está acontecendo aqui? Pelo que entendi, xé uma tabela de algum tipo, portanto 100 * x, intuitivamente, não faz sentido (especialmente quando algumas das células contêm seqüências de caracteres como AZ...).
Dhardy
5
@dhardy 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, cada xum é um subconjunto dessa coluna. Isso faz sentido?
Exp1orer
2
Pode, mas não funcionou para mim. Os pandas no Python 3 funcionam de maneira um pouco diferente?
Dhardy
1
O que level=0significa isso ?
van_d39
3
@Veenit significa que você está agrupando pelo primeiro nível do índice, e não por uma das colunas.
Exp1orer 23/11/16
54

Você precisa criar um segundo objeto groupby que agrupe pelos estados e, em seguida, use o divmétodo:

import numpy as np
import pandas as pd
np.random.seed(0)
df = pd.DataFrame({'state': ['CA', 'WA', 'CO', 'AZ'] * 3,
               'office_id': list(range(1, 7)) * 2,
               'sales': [np.random.randint(100000, 999999) for _ in range(12)]})

state_office = df.groupby(['state', 'office_id']).agg({'sales': 'sum'})
state = df.groupby(['state']).agg({'sales': 'sum'})
state_office.div(state, level='state') * 100


                     sales
state office_id           
AZ    2          16.981365
      4          19.250033
      6          63.768601
CA    1          19.331879
      3          33.858747
      5          46.809373
CO    1          36.851857
      3          19.874290
      5          43.273852
WA    2          34.707233
      4          35.511259
      6          29.781508

o level='state'kwarg in divdiz aos pandas para transmitir / juntar-se à base de quadros de dados com base nos valores no statenível do índice.

Paul H
fonte
4
Este método funciona se você tiver 3 índices? Primeiro fiz um groupby em 3 colunas. Então eu fiz um segundo grupo com apenas 2 e calculei a soma. Então eu tento usar, divmas com level=["index1", "index2"]mas me diz isso Join on level between two MultiIndex objects is ambiguous.
Ger
@ Gerer Funciona, mas não há como eu adivinhar o que você está fazendo de errado nessa descrição. Pesquise um pouco mais no site. Se você não encontrar nada, crie uma nova pergunta com um exemplo reproduzível que demonstre o problema. stackoverflow.com/questions/20109391/…
Paul H
34

Por concisão, eu usaria o SeriesGroupBy:

In [11]: c = df.groupby(['state', 'office_id'])['sales'].sum().rename("count")

In [12]: c
Out[12]:
state  office_id
AZ     2            925105
       4            592852
       6            362198
CA     1            819164
       3            743055
       5            292885
CO     1            525994
       3            338378
       5            490335
WA     2            623380
       4            441560
       6            451428
Name: count, dtype: int64

In [13]: c / c.groupby(level=0).sum()
Out[13]:
state  office_id
AZ     2            0.492037
       4            0.315321
       6            0.192643
CA     1            0.441573
       3            0.400546
       5            0.157881
CO     1            0.388271
       3            0.249779
       5            0.361949
WA     2            0.411101
       4            0.291196
       6            0.297703
Name: count, dtype: float64

Para vários grupos, você deve usar a transformação (usando o df do Radical ):

In [21]: c =  df.groupby(["Group 1","Group 2","Final Group"])["Numbers I want as percents"].sum().rename("count")

In [22]: c / c.groupby(level=[0, 1]).transform("sum")
Out[22]:
Group 1  Group 2  Final Group
AAHQ     BOSC     OWON           0.331006
                  TLAM           0.668994
         MQVF     BWSI           0.288961
                  FXZM           0.711039
         ODWV     NFCH           0.262395
...
Name: count, dtype: float64

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).

Andy Hayden
fonte
5
Isso é super rápido. Eu recomendaria isso como a abordagem preferida dos pandas. Realmente aproveita a vetorização de numpy e a indexação de pandas.
Charles
Isso funcionou bem para mim também, pois estou trabalhando com vários grupos. Obrigado.
irene
27

Eu acho que isso precisa de benchmarking. Usando o DataFrame original do OP,

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)]
})

1st Andy Hayden

Como comentado em sua resposta, Andy aproveita ao máximo a vetorização e a indexação de pandas.

c = df.groupby(['state', 'office_id'])['sales'].sum().rename("count")
c / c.groupby(level=0).sum()

3,42 ms ± 16,7 µs por loop
(média ± desvio padrão de 7 corridas, 100 loops cada)


2nd Paul H

state_office = df.groupby(['state', 'office_id']).agg({'sales': 'sum'})
state = df.groupby(['state']).agg({'sales': 'sum'})
state_office.div(state, level='state') * 100

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 uma xno 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, applyvocê 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,

(
    df.groupby(['state', 'office_id'])
    .agg({'sales': 'sum'})
    .groupby(level=0)
    .apply(lambda x: 100 * x / float(x.sum()))
)

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,

import string

import numpy as np
import pandas as pd
np.random.seed(0)

groups = [
    ''.join(i) for i in zip(
    np.random.choice(np.array([i for i in string.ascii_lowercase]), 30000),
    np.random.choice(np.array([i for i in string.ascii_lowercase]), 30000),
    np.random.choice(np.array([i for i in string.ascii_lowercase]), 30000),
                       )
]

df = pd.DataFrame({'state': groups * 400,
               'office_id': list(range(1, 601)) * 20000,
               'sales': [np.random.randint(100000, 999999)
                         for _ in range(12)] * 1000000
})

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 !!

Little Bobby Tables
fonte
17

(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:

Transformação: Enquanto a agregação deve retornar uma versão reduzida dos dados, a transformação pode retornar uma versão transformada dos dados completos para recombinar. Para essa transformação, a saída tem a mesma forma que a entrada.

Então transformation, usando , a solução é 1-liner:

df['%'] = 100 * df['sales'] / df.groupby('state')['sales'].transform('sum')

E se você imprimir:

print(df.sort_values(['state', 'office_id']).reset_index(drop=True))

   state  office_id   sales          %
0     AZ          2  195197   9.844309
1     AZ          4  877890  44.274352
2     AZ          6  909754  45.881339
3     CA          1  614752  50.415708
4     CA          3  395340  32.421767
5     CA          5  209274  17.162525
6     CO          1  549430  42.659629
7     CO          3  457514  35.522956
8     CO          5  280995  21.817415
9     WA          2  828238  35.696929
10    WA          4  719366  31.004563
11    WA          6  772590  33.298509
Caner
fonte
3
@ Cancer Esta é a minha resposta fav, pois mantém o df como um df (sem converter para série) e apenas adiciona uma coluna%. Obrigado
T.Fung
Variação desta resposta funcionou muito bem para mim comtransform('max')
Sheldore 08/06
11

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

import random
import string
import pandas as pd
import numpy as np
np.random.seed(0)

# This is the total number of groups to be created
NumberOfGroups = 50000

# Create a lot of groups (random strings of 4 letters)
Group1     = [''.join(random.choice(string.ascii_uppercase) for _ in range(4)) for x in range(NumberOfGroups/10)]*10
Group2     = [''.join(random.choice(string.ascii_uppercase) for _ in range(4)) for x in range(NumberOfGroups/2)]*2
FinalGroup = [''.join(random.choice(string.ascii_uppercase) for _ in range(4)) for x in range(NumberOfGroups)]

# Make the numbers
NumbersForPercents = [np.random.randint(100, 999) for _ in range(NumberOfGroups)]

# Make the dataframe
df = pd.DataFrame({'Group 1': Group1,
                   'Group 2': Group2,
                   'Final Group': FinalGroup,
                   'Numbers I want as percents': NumbersForPercents})

Quando agrupados, parece com:

                             Numbers I want as percents
Group 1 Group 2 Final Group                            
AAAH    AQYR    RMCH                                847
                XDCL                                182
        DQGO    ALVF                                132
                AVPH                                894
        OVGH    NVOO                                650
                VKQP                                857
        VNLY    HYFW                                884
                MOYH                                469
        XOOC    GIDS                                168
                HTOY                                544
AACE    HNXU    RAXK                                243
                YZNK                                750
        NOYI    NYGC                                399
                ZYCI                                614
        QKGK    CRLF                                520
                UXNA                                970
        TXAR    MLNB                                356
                NMFJ                                904
        VQYG    NPON                                504
                QPKQ                                948
...
[50000 rows x 1 columns]

Método de matriz para encontrar a porcentagem:

# Initial grouping (basically a sorted version of df)
PreGroupby_df = df.groupby(["Group 1","Group 2","Final Group"]).agg({'Numbers I want as percents': 'sum'}).reset_index()
# Get the sum of values for the "final group", append "_Sum" to it's column name, and change it into a dataframe (.reset_index)
SumGroup_df = df.groupby(["Group 1","Group 2"]).agg({'Numbers I want as percents': 'sum'}).add_suffix('_Sum').reset_index()
# Merge the two dataframes
Percents_df = pd.merge(PreGroupby_df, SumGroup_df)
# Divide the two columns
Percents_df["Percent of Final Group"] = Percents_df["Numbers I want as percents"] / Percents_df["Numbers I want as percents_Sum"] * 100
# Drop the extra _Sum column
Percents_df.drop(["Numbers I want as percents_Sum"], inplace=True, axis=1)

Este método leva cerca de 0,15 segundos

Método de resposta principal (usando a função lambda):

state_office = df.groupby(['Group 1','Group 2','Final Group']).agg({'Numbers I want as percents': 'sum'})
state_pcts = state_office.groupby(level=['Group 1','Group 2']).apply(lambda x: 100 * x / float(x.sum()))

Este método leva cerca de 21 segundos para produzir o mesmo resultado.

O resultado:

      Group 1 Group 2 Final Group  Numbers I want as percents  Percent of Final Group
0        AAAH    AQYR        RMCH                         847               82.312925
1        AAAH    AQYR        XDCL                         182               17.687075
2        AAAH    DQGO        ALVF                         132               12.865497
3        AAAH    DQGO        AVPH                         894               87.134503
4        AAAH    OVGH        NVOO                         650               43.132050
5        AAAH    OVGH        VKQP                         857               56.867950
6        AAAH    VNLY        HYFW                         884               65.336290
7        AAAH    VNLY        MOYH                         469               34.663710
8        AAAH    XOOC        GIDS                         168               23.595506
9        AAAH    XOOC        HTOY                         544               76.404494
Radical Edward
fonte
9

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:

df['sales_ratio'] = df.groupby(['state'])['sales'].transform(lambda x: x/x.sum())

Observe que, se você ainda estiver usando o Python 2, precisará substituir o x no denominador do termo lambda por float (x).

MightyCurious
fonte
Esta é a melhor resposta para IMO. A única coisa a acrescentar seria * 100torná-lo uma porcentagem.
Bouncner 4/06/19
1
@ Bouncner: Sim, estritamente falando, você teria que multiplicar por 100 para obter uma porcentagem - ou renomear a nova variável de "sales_percentage" para "sales_ratio". Pessoalmente, prefiro o último e editei a resposta de acordo. Obrigado por mencionar!
MightyCurious
2
Isso não funciona se você tiver vários níveis.
irene
@irene: Bom ponto, obrigado! Provavelmente nesse caso df.reset_index (). Groupby (['state']) ['sales']. Transform (lambda x: x / x.sum ()) funcionaria. Ou estou negligenciando alguma coisa?
MightyCurious
1
Esta resposta é ótima. Não envolve a criação de um groupbyobjeto temporário , é super conciso e lê muito logicamente da esquerda para a direita.
C. Braun
7

A maneira mais elegante de encontrar porcentagens entre colunas ou índice é usar pd.crosstab.

Dados de amostra

df = pd.DataFrame({'state': ['CA', 'WA', 'CO', 'AZ'] * 3,
               'office_id': list(range(1, 7)) * 2,
               'sales': [np.random.randint(100000, 999999) for _ in range(12)]})

O dataframe de saída é assim

print(df)

        state   office_id   sales
    0   CA  1   764505
    1   WA  2   313980
    2   CO  3   558645
    3   AZ  4   883433
    4   CA  5   301244
    5   WA  6   752009
    6   CO  1   457208
    7   AZ  2   259657
    8   CA  3   584471
    9   WA  4   122358
    10  CO  5   721845
    11  AZ  6   136928

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.

result = pd.crosstab(index=df['state'], 
                     columns=df['office_id'], 
                     values=df['sales'], 
                     aggfunc='sum', 
                     normalize='index').applymap('{:.2f}%'.format)




print(result)
office_id   1   2   3   4   5   6
state                       
AZ  0.00%   0.20%   0.00%   0.69%   0.00%   0.11%
CA  0.46%   0.00%   0.35%   0.00%   0.18%   0.00%
CO  0.26%   0.00%   0.32%   0.00%   0.42%   0.00%
WA  0.00%   0.26%   0.00%   0.10%   0.00%   0.63%
ajknzhol
fonte
3

Você pode sumo todo DataFramee dividir pelo statetotal:

# Copying setup from Paul H answer
import numpy as np
import pandas as pd
np.random.seed(0)
df = pd.DataFrame({'state': ['CA', 'WA', 'CO', 'AZ'] * 3,
               'office_id': list(range(1, 7)) * 2,
               'sales': [np.random.randint(100000, 999999) for _ in range(12)]})
# Add a column with the sales divided by state total sales.
df['sales_ratio'] = (df / df.groupby(['state']).transform(sum))['sales']

df

Devoluções

    office_id   sales state  sales_ratio
0           1  405711    CA     0.193319
1           2  535829    WA     0.347072
2           3  217952    CO     0.198743
3           4  252315    AZ     0.192500
4           5  982371    CA     0.468094
5           6  459783    WA     0.297815
6           1  404137    CO     0.368519
7           2  222579    AZ     0.169814
8           3  710581    CA     0.338587
9           4  548242    WA     0.355113
10          5  474564    CO     0.432739
11          6  835831    AZ     0.637686

Mas observe que isso funciona apenas porque todas as colunas que não statesão numéricas, permitindo a soma de todo o DataFrame. Por exemplo, se office_idfor um caractere, você receberá um erro:

df.office_id = df.office_id.astype(str)
df['sales_ratio'] = (df / df.groupby(['state']).transform(sum))['sales']

TypeError: tipo (s) de operando não suportado por /: 'str' e 'str'

iggy
fonte
Editei para observar que isso só funciona quando todas as colunas, exceto a groupbycoluna, são numéricas. Mas, de outra forma, é bastante elegante. Existe uma maneira de fazê-lo funcionar com outras strcolunas?
precisa saber é o seguinte
Não é tão longe quanto eu sei: stackoverflow.com/questions/34099684/…
iggy
2

Eu acho que isso faria o truque em 1 linha:

df.groupby(['state', 'office_id']).sum().transform(lambda x: x/np.sum(x)*100)
louisD
fonte
Eu acredito que leva todas as colunas do conjunto de dados. neste caso, existe apenas um. Se você possui vários e deseja executar esta operação em uma única, especifique-a após a expressão groupby: df.groupby (['state', 'office_id']) [[SEU NOME DA COLUNA AQUI]]. Etcetc, se desejar para manter as outras colunas intocada, apenas re-atribuído as colunas específicas
louisD
@ louisD: Eu gosto muito da sua abordagem de tentar mantê-la curta. Infelizmente, quando tento reatribuir a coluna como você sugeriu, recebo dois erros: "ValueError: Incompatibilidade de tipo de buffer, esperado 'objeto Python' mas ficou 'longo' '" e, adicionalmente (durante o tratamento da primeira exceção): " TypeError: índice incompatível da coluna inserida com índice de quadros "O código que usei foi o seguinte: df ['percent'] = df.groupby (['state', 'office_id']). Sum (). Transform (lambda x: x / np.sum (x) * 100) Portanto, postarei uma resposta separada para corrigir isso.
MightyCurious
1

A maneira simples que usei é uma mesclagem após os 2 groupby's que estão fazendo a divisão simples.

import numpy as np
import pandas as pd
np.random.seed(0)
df = pd.DataFrame({'state': ['CA', 'WA', 'CO', 'AZ'] * 3,
               'office_id': list(range(1, 7)) * 2,
               'sales': [np.random.randint(100000, 999999) for _ in range(12)]})

state_office = df.groupby(['state', 'office_id'])['sales'].sum().reset_index()
state = df.groupby(['state'])['sales'].sum().reset_index()
state_office = state_office.merge(state, left_on='state', right_on ='state', how = 'left')
state_office['sales_ratio'] = 100*(state_office['sales_x']/state_office['sales_y'])

   state  office_id  sales_x  sales_y  sales_ratio
0     AZ          2   222579  1310725    16.981365
1     AZ          4   252315  1310725    19.250033
2     AZ          6   835831  1310725    63.768601
3     CA          1   405711  2098663    19.331879
4     CA          3   710581  2098663    33.858747
5     CA          5   982371  2098663    46.809373
6     CO          1   404137  1096653    36.851857
7     CO          3   217952  1096653    19.874290
8     CO          5   474564  1096653    43.273852
9     WA          2   535829  1543854    34.707233
10    WA          4   548242  1543854    35.511259
11    WA          6   459783  1543854    29.781508
upliftedLemur
fonte
1
df = pd.DataFrame({'state': ['CA', 'WA', 'CO', 'AZ'] * 3,
               'office_id': list(range(1, 7)) * 2,
               'sales': [np.random.randint(100000, 999999)
                         for _ in range(12)]})

grouped = df.groupby(['state', 'office_id'])
100*grouped.sum()/df[["state","sales"]].groupby('state').sum()

Devoluções:

sales
state   office_id   
AZ  2   54.587910
    4   33.009225
    6   12.402865
CA  1   32.046582
    3   44.937684
    5   23.015735
CO  1   21.099989
    3   31.848658
    5   47.051353
WA  2   43.882790
    4   10.265275
    6   45.851935
Alessandro
fonte
0

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 apelido state_office_sales:

                  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_total_salesé state_office_salesagrupado por somas totais em index level 0(mais à esquerda).

In:   state_total_sales = df.groupby(level=0).sum()
      state_total_sales

Out: 
       sales
state   
AZ     2448009
CA     2832270
CO     1495486
WA     595859

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:

In:   state_office_sales / state_total_sales

Out:  

                   sales
state   office_id   
AZ      2          0.448640
        4          0.125865
        6          0.425496
CA      1          0.288022
        3          0.322169
        5          0.389809
CO      1          0.206684
        3          0.357891
        5          0.435425
WA      2          0.321689
        4          0.346325
        6          0.331986

Para ilustrar isso ainda melhor, aqui está um total parcial com um XXque 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á:

In:   partial_total = pd.DataFrame(
                      data   =  {'sales' : [2448009, 595859, 99999]},
                      index  =             ['AZ',    'WA',   'XX' ]
                      )
      partial_total.index.name = 'state'


Out:  
         sales
state
AZ       2448009
WA       595859
XX       99999
In:   state_office_sales / partial_total

Out: 
                   sales
state   office_id   
AZ      2          0.448640
        4          0.125865
        6          0.425496
CA      1          NaN
        3          NaN
        5          NaN
CO      1          NaN
        3          NaN
        5          NaN
WA      2          0.321689
        4          0.346325
        6          0.331986

Isso fica muito claro quando não há índices ou colunas compartilhados. Aqui missing_index_totalsé igual a, state_total_salesexceto que não possui um nome de índice.

In:   missing_index_totals = state_total_sales.rename_axis("")
      missing_index_totals

Out:  
       sales
AZ     2448009
CA     2832270
CO     1495486
WA     595859
In:   state_office_sales / missing_index_totals 

Out:  ValueError: cannot join with no overlapping index names
Anders Solberg
fonte
-1

Solução de uma linha:

df.join(
    df.groupby('state').agg(state_total=('sales', 'sum')),
    on='state'
).eval('sales / state_total')

Isso retorna uma série de taxas por escritório - pode ser usado sozinho ou atribuído ao Dataframe original.

ribitskiyb
fonte