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 state
niveau du groupby
pour totaliser sales
le tout state
pour calculer la fraction.
df['sales'] / df.groupby('state')['sales'].transform('sum')
semble être la réponse la plus claire.Réponses:
La réponse de Paul H est juste que vous devrez faire un deuxième
groupby
objet, mais vous pouvez calculer le pourcentage d'une manière plus simple -groupby
lastate_office
et diviser lasales
colonne par sa somme. Copie du début de la réponse de Paul H:Retour:
la source
x
s'agit d'un tableau quelconque, donc cela100 * x
n'a pas de sens intuitivement (surtout lorsque certaines cellules contiennent des chaînes commeAZ
, ...).state_office
est 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, chacunx
est un sous-ensemble de cette colonne. Cela a-t-il du sens?level=0
dire?Vous devez créer un deuxième objet groupby qui regroupe par états, puis utiliser la
div
méthode:le
level='state'
kwarg indiv
dit aux pandas de diffuser / rejoindre les dataframes basés sur les valeurs austate
niveau de l'index.la source
div
mais aveclevel=["index1", "index2"]
mais ça me dit çaJoin on level between two MultiIndex objects is ambiguous
.Pour plus de concision, j'utiliserais SeriesGroupBy:
Pour plusieurs groupes, vous devez utiliser transform (en utilisant le df de Radical ):
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).
la source
Je pense que cela nécessite une analyse comparative. En utilisant le DataFrame original d'OP,
1er Andy Hayden
Comme commenté sa réponse, Andy profite pleinement de la vectorisation et de l'indexation des pandas.
3,42 ms ± 16,7 µs par boucle
(moyenne ± écart type de 7 analyses, 100 boucles chacune)
2e Paul H
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 chacunx
au 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,
apply
vous 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,
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,
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 !!
la source
(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
:Donc, en utilisant
transformation
, la solution est 1-liner:Et si vous imprimez:
la source
transform('max')
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
Lorsqu'il est groupé, il ressemble à:
Méthode de tableau pour trouver le pourcentage:
Cette méthode prend environ 0,15 seconde
Méthode de réponse supérieure (en utilisant la fonction lambda):
Cette méthode prend environ 21 secondes pour produire le même résultat.
Le résultat:
la source
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:
Notez que si vous utilisez toujours Python 2, vous devrez remplacer le x dans le dénominateur du terme lambda par float (x).
la source
* 100
de faire un pourcentage.groupby
objet temporaire , est super concis et se lit très logiquement de gauche à droite.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
La trame de données de sortie est comme ça
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.
la source
Vous pouvez
sum
le toutDataFrame
et diviser par lestate
total:Retour
Mais notez que cela ne fonctionne que parce que toutes les colonnes autres que
state
numériques, permettant la sommation de l'ensemble du DataFrame. Par exemple, sioffice_id
c'est un caractère à la place, vous obtenez une erreur:la source
groupby
colonne sont numériques. Mais c'est par ailleurs assez élégant. Existe-t-il un moyen de le faire fonctionner avec d'autresstr
colonnes?Je pense que cela ferait l'affaire en 1 ligne:
la source
Un moyen simple que j'ai utilisé est une fusion après les 2 groupby, puis une division simple.
la source
Retour:
la source
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'aliasstate_office_sales
:state_total_sales
eststate_office_sales
regroupé par sommes totales dansindex level 0
(le plus à gauche).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:
Pour illustrer encore mieux cela, voici un total partiel avec un
XX
qui 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:Cela devient très clair lorsqu'il n'y a pas d'index ou de colonnes partagés. Ici
missing_index_totals
est égal àstate_total_sales
sauf qu'il n'a pas de nom d'index.la source
Solution en une ligne:
Cela renvoie une série de ratios par bureau - peut être utilisé seul ou affecté au Dataframe d'origine.
la source