Pandas porcentaje del total con groupby

148

Esto es obviamente simple, pero como un nuevo numpy me estoy atascando.

Tengo un archivo CSV que contiene 3 columnas, el estado, la identificación de la oficina y las ventas de esa oficina.

Quiero calcular el porcentaje de ventas por oficina en un estado determinado (el total de todos los porcentajes en cada estado es del 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'})

Esto devuelve:

                  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 no puedo entender cómo "alcanzar" el statenivel del groupbytotal para calcular el salestotal statede la fracción.

erikcw
fuente
3
df['sales'] / df.groupby('state')['sales'].transform('sum')Parece ser la respuesta más clara.
Paul Rougieux

Respuestas:

207

La respuesta de Pablo H es justo que usted tendrá que hacer un segundo groupbyobjeto, pero se puede calcular el porcentaje de una manera más simple - sólo groupbyel state_officey dividir la salescolumna por su suma. Copiando el comienzo de la respuesta 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()))

Devoluciones:

                     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
fuente
1
¿Que está pasando aqui? Según tengo entendido, xes una tabla de algún tipo, por lo 100 * xque no tiene sentido intuitivamente (especialmente cuando algunas de las celdas contienen cadenas como AZ, ...).
dhardy
55
@dhardy state_officees una serie con un índice múltiple, por lo que es solo una columna cuyos valores son todos numéricos. Después de hacer el groupby, cada uno xes un subconjunto de esa columna. ¿Tiene sentido?
exp1orer
2
Podría, pero no funcionó para mí. ¿Los pandas en Python 3 funcionan un poco diferente?
dhardy
1
Que level=0significa
van_d39
3
@Veenit significa que está agrupando por el primer nivel del índice, en lugar de por una de las columnas.
exp1orer
54

Debe crear un segundo grupo por objeto que se agrupe por estados y luego usar el 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

el level='state'kwarg divle dice a los pandas que transmitan / se unan a la base de marcos de datos en los valores en el statenivel del índice.

Paul H
fuente
44
¿Funciona este método si tienes 3 índices? Primero hice un groupby en 3 columnas. Luego hice un segundo grupo con solo 2 y calculé la suma. Luego trato de usar divpero con level=["index1", "index2"]pero me dice eso Join on level between two MultiIndex objects is ambiguous.
Ger
@Ger Funciona, pero no hay forma de que pueda adivinar lo que estás haciendo mal con esa descripción. Busque en el sitio un poco más. Si no encuentra nada, cree una nueva pregunta con un ejemplo reproducible que demuestre el problema. stackoverflow.com/questions/20109391/…
Paul H
34

Para ser conciso, usaría 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 varios grupos, debe usar transform (usando Radical's df ):

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

Esto parece ser un poco más eficaz que las otras respuestas (solo menos del doble de la velocidad de la respuesta de Radical, para mí ~ 0.08s).

Andy Hayden
fuente
55
Esto es super rapido. Recomendaría esto como el enfoque preferido de los pandas. Realmente aprovecha la vectorización de numpy y la indexación de pandas.
Charles
Esto también funcionó bien para mí, ya que estoy trabajando con varios grupos. Gracias.
irene
27

Creo que esto necesita una evaluación comparativa. Usando el DataFrame original de 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 comentó su respuesta, Andy aprovecha al máximo la vectorización y la indexación 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 bucle
(media ± desviación estándar de 7 corridas, 100 bucles cada una)


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 bucle
(media ± desviación estándar de 7 corridas, 100 bucles cada una)


3er exp.

Esta es la respuesta más lenta ya que calcula x.sum()para cada uno xen el nivel 0.

Para mí, esta sigue siendo una respuesta útil, aunque no en su forma actual. Para un EDA rápido en conjuntos de datos más pequeños, le applypermite usar el método de encadenamiento para escribir esto en una sola línea. Por lo tanto, eliminamos la necesidad de decidir el nombre de una variable, que en realidad es computacionalmente costoso para su recurso más valioso (¡su cerebro!).

Aquí está la modificación,

(
    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 bucle
(media ± desviación estándar de 7 corridas, 100 bucles cada una)


Así que a nadie le importan unos 6 ms en un pequeño conjunto de datos. Sin embargo, esto es una velocidad 3 veces mayor y, en un conjunto de datos más grande con un grupo de cardinalidad alto, esto hará una gran diferencia.

Agregando al código anterior, creamos un DataFrame con forma (12,000,000, 3) con 14412 categorías estatales y 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 ciclo
(media ± estándar de desarrollo de 7 carreras, 1 ciclo cada una)

y exp1orer

19 s ± 77.1 ms por ciclo
(media ± estándar de desarrollo de 7 carreras, 1 ciclo cada una)

Así que ahora vemos que x10 se acelera en grandes conjuntos de datos de alta cardinalidad.


¡Asegúrate de usar estas tres respuestas en UV si estas es UV!

Mesitas Bobby
fuente
17

(Esta solución está inspirada en este artículo https://pbpython.com/pandas_transform.html )

Creo que la siguiente solución es la más simple (y probablemente la más rápida) usando transformation:

Transformación: si bien la agregación debe devolver una versión reducida de los datos, la transformación puede devolver alguna versión transformada de los datos completos para que se recombinen. Para tal transformación, la salida tiene la misma forma que la entrada.

Entonces transformation, usando , la solución es 1-liner:

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

Y si imprimes:

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
fuente
3
@Cancer Esta es mi respuesta favorita, ya que mantiene el df como un df (sin convertirlo en serie) y simplemente agrega una columna%. Gracias
T.Fung
La variación de esta respuesta funcionó muy bien para mí contransform('max')
Sheldore
11

Sé que esta es una pregunta antigua, pero la respuesta de exp1orer es muy lenta para los conjuntos de datos con un gran número de grupos únicos (probablemente debido a la lambda). Construí a partir de su respuesta para convertirlo en un cálculo de matriz, ¡así que ahora es súper rápido! A continuación se muestra el código de ejemplo:

Cree el marco de datos de prueba con 50,000 grupos únicos

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

Cuando se agrupa se ve así:

                             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 el porcentaje:

# 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 dura aproximadamente ~ 0.15 segundos

Método de respuesta superior (usando la función 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 tarda aproximadamente ~ 21 segundos en producir el mismo resultado.

El 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
Edward radical
fuente
9

Me doy cuenta de que ya hay buenas respuestas aquí.

Sin embargo, me gustaría contribuir con la mía, porque considero que para una pregunta simple y elemental como esta, debería haber una solución corta que sea comprensible de un vistazo.

También debería funcionar de manera que pueda agregar los porcentajes como una nueva columna, dejando el resto del marco de datos intacto. Por último, pero no menos importante, debe generalizarse de manera obvia en el caso de que haya más de un nivel de agrupación (por ejemplo, estado y país en lugar de solo estado).

El siguiente fragmento cumple estos criterios:

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

Tenga en cuenta que si todavía usa Python 2, deberá reemplazar la x en el denominador del término lambda por float (x).

MightyCurious
fuente
Esta es la mejor respuesta de la OMI. Lo único que agregaría sería * 100convertirlo en un porcentaje.
Bouncner
1
@Bouncner: Sí, estrictamente hablando, tendría que multiplicar por 100 para obtener un porcentaje, o cambiar el nombre de la nueva variable de "sales_percentage" a "sales_ratio". Personalmente, prefiero lo último, y edité la respuesta en consecuencia. Gracias por mencionar!
MightyCurious
2
Sin embargo, esto no funciona si tienes varios niveles.
irene
@irene: Buen punto, gracias! Probablemente en ese caso df.reset_index (). Groupby (['state']) ['sales']. Transform (lambda x: x / x.sum ()) funcionaría. ¿O estoy pasando por alto algo?
MightyCurious
1
Esta respuesta es genial. No implica crear un groupbyobjeto temporal , es súper conciso y se lee de forma muy lógica de izquierda a derecha.
C. Braun
7

La forma más elegante de encontrar porcentajes en columnas o índices es usar pd.crosstab.

Data de muestra

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

El marco de datos de salida es así

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

Simplemente especifique el índice, las columnas y los valores para agregar. La palabra clave normalizar calculará el% en el índice o las columnas según el 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
fuente
3

Puedes sumel todo DataFramey dividir por el 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

Devoluciones

    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

Pero tenga en cuenta que esto solo funciona porque todas las columnas que no statesean numéricas, lo que permite la suma de todo el DataFrame. Por ejemplo, si se office_idtrata de un carácter , aparece un error:

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

TypeError: tipos de operandos no compatibles para /: 'str' y 'str'

iggy
fuente
Edité para tener en cuenta que esto solo funciona cuando todas las columnas, excepto la groupbycolumna, son numéricas. Pero por lo demás es bastante elegante. ¿Hay alguna manera de hacerlo funcionar con otras strcolumnas?
Max Ghenis el
2

Creo que esto haría el truco en 1 línea:

df.groupby(['state', 'office_id']).sum().transform(lambda x: x/np.sum(x)*100)
louisD
fuente
Creo que toma todas las columnas del conjunto de datos. en este caso, solo hay uno. Si tiene varias y desea realizar esta operación en una sola, especifíquela después de la expresión groupby: df.groupby (['state', 'office_id']) [[SU NOMBRE DE COLUMNA AQUÍ]]. Etcetc si lo desea para mantener las otras columnas intactas, solo
reasigne
@louisD: Me gusta mucho su enfoque de tratar de mantenerlo breve. Desafortunadamente, cuando trato de reasignar la columna como sugirió, recibo dos errores: "ValueError: No coinciden los tipos de búfer, esperaba el 'objeto Python' pero se hizo 'largo y largo'", y adicionalmente (durante el manejo de la primera excepción): " TypeError: índice incompatible de columna insertada con índice de cuadro "El código que utilicé fue el siguiente: df ['percent'] = df.groupby (['state', 'office_id']). Sum (). Transform (lambda x: x / np.sum (x) * 100) Por lo tanto, publicaré una respuesta por separado para solucionar esto.
MightyCurious
1

La forma simple que he usado es una fusión después de que los 2 groupby hagan una división simple.

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
levantadoLemur
fuente
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()

Devoluciones:

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
fuente
0

Como alguien que también está aprendiendo pandas, encontré las otras respuestas un poco implícitas, ya que los pandas ocultan la mayor parte del trabajo detrás de escena. A saber, cómo funciona la operación haciendo coincidir automáticamente los nombres de las columnas y los índices. Este código debe ser equivalente a una versión paso a paso de la respuesta aceptada de @ exp1orer

Con el df, lo llamaré por el alias 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_salesse state_office_salesagrupa por sumas totales en index level 0(extremo izquierdo).

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

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

Debido a que los dos marcos de datos comparten un nombre de índice y un panda de nombre de columna, encontrarán las ubicaciones apropiadas a través de índices compartidos 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 esto aún mejor, aquí hay un total parcial con un XXque no tiene equivalente. Los pandas coincidirán con la ubicación en función de los nombres de índice y columna, donde no hay superposición de pandas lo ignorarán:

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

Esto se vuelve muy claro cuando no hay índices o columnas compartidas. Aquí missing_index_totalses igual a state_total_salesexcepto que no tiene un nombre 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
fuente
-1

Solución de una línea:

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

Esto devuelve una serie de relaciones por oficina: se puede usar por sí solo o se puede asignar al marco de datos original.

ribitskiyb
fuente