Pourcentage de pandas du total avec groupby

148

C'est évidemment simple, mais en tant que nouveau, je suis bloqué.

J'ai un fichier CSV qui contient 3 colonnes, l'état, l'ID du bureau et les ventes de ce bureau.

Je veux calculer le pourcentage des ventes par bureau dans un état donné (le total de tous les pourcentages dans chaque état est de 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'})

Cela renvoie:

                  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

Je n'arrive pas à comprendre comment «atteindre» le stateniveau du groupbypour totaliser salesle tout statepour calculer la fraction.

erikcw
la source
3
df['sales'] / df.groupby('state')['sales'].transform('sum')semble être la réponse la plus claire.
Paul Rougieux

Réponses:

207

La réponse de Paul H est juste que vous devrez faire un deuxième groupbyobjet, mais vous pouvez calculer le pourcentage d'une manière plus simple - groupbyla state_officeet diviser la salescolonne par sa somme. Copie du début de la réponse 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()))

Retour:

                     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
la source
1
Que se passe t-il ici? Si je comprends bien, il xs'agit d'un tableau quelconque, donc cela 100 * xn'a pas de sens intuitivement (surtout lorsque certaines cellules contiennent des chaînes comme AZ, ...).
dhardy
5
@dhardy state_officeest une série avec un index multiple - c'est donc juste une colonne dont les valeurs sont toutes numériques. Après avoir effectué le groupby, chacun xest un sous-ensemble de cette colonne. Cela a-t-il du sens?
exp1orer
2
Cela pourrait, mais cela n'a pas fonctionné pour moi. Les pandas dans Python 3 fonctionnent-ils un peu différemment?
dhardy
1
Que veut level=0dire?
van_d39
3
@Veenit cela signifie que vous groupez par le premier niveau de l'index, plutôt que par l'une des colonnes.
exp1orer
54

Vous devez créer un deuxième objet groupby qui regroupe par états, puis utiliser la divméthode:

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

le level='state'kwarg in divdit aux pandas de diffuser / rejoindre les dataframes basés sur les valeurs au stateniveau de l'index.

Paul H
la source
4
Cette méthode fonctionne-t-elle si vous avez 3 index? J'ai d'abord fait un groupby sur 3 colonnes. Ensuite, j'ai fait un deuxième groupby sur seulement 2 et calculer la somme. Alors j'essaye d'utiliser divmais avec level=["index1", "index2"]mais ça me dit ça Join on level between two MultiIndex objects is ambiguous.
Ger
@Ger Cela fonctionne, mais je ne peux pas deviner ce que vous faites de mal à partir de cette description. Cherchez un peu plus sur le site. Si vous ne trouvez rien, créez une nouvelle question avec un exemple reproductible qui illustre le problème. stackoverflow.com/questions/20109391/…
Paul H
34

Pour plus de concision, j'utiliserais 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

Pour plusieurs groupes, vous devez utiliser transform (en utilisant le df de 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

Cela semble être légèrement plus performant que les autres réponses (un peu moins de deux fois la vitesse de la réponse de Radical, pour moi ~ 0,08 s).

Andy Hayden
la source
5
C'est super rapide. Je recommanderais ceci comme approche préférée des pandas. Profite vraiment de la vectorisation de numpy et de l'indexation des pandas.
Charles
Cela a bien fonctionné pour moi aussi, car je travaille avec plusieurs groupes. Merci.
irene
27

Je pense que cela nécessite une analyse comparative. En utilisant le DataFrame original d'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)]
})

1er Andy Hayden

Comme commenté sa réponse, Andy profite pleinement de la vectorisation et de l'indexation des pandas.

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

3,42 ms ± 16,7 µs par boucle
(moyenne ± écart type de 7 analyses, 100 boucles chacune)


2e 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 par boucle
(moyenne ± écart-type de 7 analyses, 100 boucles chacune)


3ème exp1orateur

C'est la réponse la plus lente car il calcule x.sum()pour chacun xau niveau 0.

Pour moi, c'est toujours une réponse utile, mais pas dans sa forme actuelle. Pour une EDA rapide sur des ensembles de données plus petits, applyvous permet d'utiliser le chaînage de méthodes pour écrire cela sur une seule ligne. Nous supprimons donc le besoin de décider du nom d'une variable, ce qui est en fait très coûteux en calcul pour votre ressource la plus précieuse (votre cerveau !!).

Voici la modification,

(
    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 par boucle
(moyenne ± écart type de 7 analyses, 100 boucles chacune)


Donc personne ne va se soucier d'environ 6 ms sur un petit ensemble de données. Cependant, il s'agit d'une vitesse 3x et, sur un ensemble de données plus grand avec des groupbys à cardinalité élevée, cela fera une énorme différence.

En ajoutant au code ci-dessus, nous créons un DataFrame avec une forme (12 000 000, 3) avec 14412 catégories d'état et 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
})

En utilisant Andy's,

2 s ± 10,4 ms par boucle
(moyenne ± écart standard de 7 courses, 1 boucle chacune)

et exp1orer

19 s ± 77,1 ms par boucle
(moyenne ± écart standard de 7 courses, 1 boucle chacune)

Nous voyons maintenant une vitesse x10 sur de grands ensembles de données à cardinalité élevée.


Assurez-vous de UV ces trois réponses si vous UV celle-ci !!

Petites tables Bobby
la source
17

(Cette solution est inspirée de cet article https://pbpython.com/pandas_transform.html )

Je trouve que la solution suivante est la plus simple (et probablement la plus rapide) en utilisant transformation:

Transformation: alors que l'agrégation doit renvoyer une version réduite des données, la transformation peut renvoyer une version transformée des données complètes à recombiner. Pour une telle transformation, la sortie a la même forme que l'entrée.

Donc, en utilisant transformation, la solution est 1-liner:

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

Et si vous imprimez:

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
la source
3
@Cancer C'est ma réponse préférée car elle conserve le df en tant que df (sans conversion en série) et ajoute simplement une colonne%. Merci
T.Fung
La variation de cette réponse a très bien fonctionné pour moi avectransform('max')
Sheldore
11

Je sais que c'est une vieille question, mais la réponse d'exp1orer est très lente pour les ensembles de données avec un grand nombre de groupes uniques (probablement à cause du lambda). J'ai construit à partir de leur réponse pour la transformer en un calcul de tableau, alors maintenant c'est super rapide! Voici l'exemple de code:

Créez la trame de données de test avec 50 000 groupes uniques

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

Lorsqu'il est groupé, il ressemble à:

                             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éthode de tableau pour trouver le pourcentage:

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

Cette méthode prend environ 0,15 seconde

Méthode de réponse supérieure (en utilisant la fonction 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()))

Cette méthode prend environ 21 secondes pour produire le même résultat.

Le résultat:

      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
la source
9

Je me rends compte qu'il y a déjà de bonnes réponses ici.

Je voudrais néanmoins apporter la mienne, car je pense qu’une question élémentaire et simple comme celle-ci devrait trouver une solution courte et compréhensible en un coup d’œil.

Cela devrait également fonctionner de manière à pouvoir ajouter les pourcentages en tant que nouvelle colonne, en laissant le reste du dataframe intact. Dernier point mais non le moindre, il devrait se généraliser de manière évidente au cas où il y a plus d'un niveau de regroupement (par exemple, État et pays au lieu d'un seul État).

L'extrait suivant remplit ces critères:

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

Notez que si vous utilisez toujours Python 2, vous devrez remplacer le x dans le dénominateur du terme lambda par float (x).

MightyCurious
la source
C'est la meilleure réponse de l'OMI. La seule chose à ajouter serait * 100de faire un pourcentage.
Bouncner le
1
@Bouncner: Oui, à proprement parler, il faudrait multiplier par 100 pour obtenir un pourcentage - ou renommer la nouvelle variable de "sales_percentage" à "sales_ratio". Personnellement, je préfère ce dernier et j'ai édité la réponse en conséquence. Merci d'avoir mentionné!
MightyCurious
2
Cela ne fonctionne pas si vous avez plusieurs niveaux.
irene
@irene: Bon point, merci! Probablement dans ce cas, df.reset_index (). Groupby (['state']) ['sales']. Transform (lambda x: x / x.sum ()) fonctionnerait. Ou est-ce que je néglige quelque chose?
MightyCurious
1
Cette réponse est excellente. Cela n'implique pas de créer un groupbyobjet temporaire , est super concis et se lit très logiquement de gauche à droite.
C. Braun
7

La manière la plus élégante de trouver des pourcentages dans les colonnes ou les index est d'utiliser pd.crosstab.

Exemple de données

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

La trame de données de sortie est comme ça

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

Spécifiez simplement l'index, les colonnes et les valeurs à agréger. Le mot-clé normalize calculera le% sur l'index ou les colonnes en fonction du contexte.

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
la source
3

Vous pouvez sumle tout DataFrameet diviser par le 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

Retour

    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

Mais notez que cela ne fonctionne que parce que toutes les colonnes autres que statenumériques, permettant la sommation de l'ensemble du DataFrame. Par exemple, si office_idc'est un caractère à la place, vous obtenez une erreur:

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

TypeError: type (s) d'opérande non pris en charge pour /: 'str' et 'str'

iggy
la source
J'ai modifié pour noter que cela ne fonctionne que lorsque toutes les colonnes sauf la groupbycolonne sont numériques. Mais c'est par ailleurs assez élégant. Existe-t-il un moyen de le faire fonctionner avec d'autres strcolonnes?
Max Ghenis
Pas pour autant que je sache: stackoverflow.com/questions/34099684/…
iggy
2

Je pense que cela ferait l'affaire en 1 ligne:

df.groupby(['state', 'office_id']).sum().transform(lambda x: x/np.sum(x)*100)
louisD
la source
Je crois que cela prend toutes les colonnes de l'ensemble de données. dans ce cas, il n'y en a qu'un. Si vous en avez plusieurs et que vous souhaitez effectuer cette opération sur un seul, spécifiez-le simplement après l'expression groupby: df.groupby (['state', 'office_id']) [[YOUR COLUMN NAME ICI]]. Etcetc si vous le souhaitez pour garder les autres colonnes intactes, il suffit de réaffecter les colonnes spécifiques
louisD
@louisD: J'aime beaucoup votre approche consistant à essayer de rester bref. Malheureusement, lorsque j'essaye de réaffecter la colonne comme vous l'avez suggéré, j'obtiens deux erreurs: "ValueError: Buffer dtype mismatch, attendue 'Python object' but got 'long long'", et en plus (lors de la gestion de la première exception): " TypeError: index incompatible de la colonne insérée avec frame index "Le code que j'ai utilisé était le suivant: df ['percent'] = df.groupby (['state', 'office_id']). Sum (). Transform (lambda x: x / np.sum (x) * 100) Par conséquent, je publierai une réponse distincte pour résoudre ce problème.
MightyCurious
1

Un moyen simple que j'ai utilisé est une fusion après les 2 groupby, puis une division 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
lémurien
la source
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()

Retour:

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
la source
0

En tant que personne qui apprend également les pandas, j'ai trouvé les autres réponses un peu implicites, car les pandas cachent la plupart du travail dans les coulisses. À savoir comment l'opération fonctionne en faisant correspondre automatiquement les noms de colonne et d'index. Ce code doit être équivalent à une version étape par étape de la réponse acceptée par @ exp1orer

Avec le df, je l'appellerai par l'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_salesest state_office_salesregroupé par sommes totales dans index level 0(le plus à gauche).

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

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

Parce que les deux dataframes partagent un nom d'index et un nom de colonne, les pandas trouveront les emplacements appropriés via des index partagés comme:

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

Pour illustrer encore mieux cela, voici un total partiel avec un XXqui n'a pas d'équivalent. Les pandas correspondront à l'emplacement en fonction des noms d'index et de colonnes, là où il n'y a pas de chevauchement, les pandas l'ignoreront:

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

Cela devient très clair lorsqu'il n'y a pas d'index ou de colonnes partagés. Ici missing_index_totalsest égal à state_total_salessauf qu'il n'a pas de nom d'index.

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
la source
-1

Solution en une ligne:

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

Cela renvoie une série de ratios par bureau - peut être utilisé seul ou affecté au Dataframe d'origine.

ribitskiyb
la source