2017-09-02 2 views
0

Mon pandas géants dataframe est comme suit:Pandas tableau croisé dynamique avec aggfunc conditionnelle

df = pd.DataFrame({"PAR NAME":['abc','def','def','def','abc'], "value":[1,2,3,4,5],"DESTCD":['E','N','E','E','S']}) 

J'ai besoin de pivoter df pour NOM PAR et savoir quel âge% de sa valeur provient des endroits où DESTCD est « E ». Quelque chose comme ça (qui, évidemment, de travail na pas!)

df.pivot_table(index="PAR NAME",values=["value"],aggfunc={'value':lambda x: (x.sum() if x["DESTCD"]=="E")*100.0/x.sum()}) 

Je suis actuellement ce en ajoutant une colonne conditionnelle, puis en additionnant le long de la « valeur » dans pivot, puis en divisant, mais ma base de données est énorme (1Go +) et il doit y avoir un moyen plus facile.

Edit: Résultat attendu abc 16,67 (depuis abc et E est de 1 sur abc totale qui est 6) def 77,78 (depuis def et E est 7 sur def total de 9);

(Note: S'il vous plaît ne recommande pas de trancher dataframes multiples comme mentionné mes données est énorme et l'efficacité est essentiel :))

+1

Pouvez-vous ajouter la sortie attendue – Dark

Répondre

1

J'ai essayé de résoudre le problème sans faire référence spécifiquement 'E' il est généralisable à une lettre de l'alphabet. La sortie est une base de données que vous pouvez ensuite indexer sur E pour obtenir votre réponse. J'ai simplement fait l'agrégation séparément et ensuite utilisé une méthode de jointure efficace.

df = pd.DataFrame({"PAR NAME":['abc','def','def','def','abc'], "value":[1,2,3,4,5],"DESTCD":['E','N','E','E','S']}) 

# First groupby 'DESTCD' and 'PAR NAME' 
gb = df.groupby(['DESTCD', 'PAR NAME'], as_index=False).sum() 
print(gb) 
    DESTCD PAR NAME value 
0  E  abc  1 
1  E  def  7 
2  N  def  2 
3  S  abc  5 

gb_parname = gb.groupby(['PAR NAME']).sum() 
out = gb.join(gb_parname, on='PAR NAME', rsuffix='Total') 
print(out) 
    DESTCD PAR NAME value valueTotal 
0  E  abc  1   6 
1  E  def  7   9 
2  N  def  2   9 
3  S  abc  5   6 

out.loc[:, 'derived']= out.apply(lambda df: df.value/df.valueTotal, axis=1) 

print(out) 
    DESTCD PAR NAME value valueTotal derived 
0  E  abc  1   6 0.166667 
1  E  def  7   9 0.777778 
2  N  def  2   9 0.222222 
3  S  abc  5   6 0.833333 

Il est également une opération

%%timeit 
gb = df.groupby(['DESTCD', 'PAR NAME'], as_index=False).sum() 
gb_parname = gb.groupby(['PAR NAME']).sum() 
out = gb.join(gb_parname, on='PAR NAME', rsuffix='Total') 
out.loc[:, 'derived']= out.apply(lambda df: df.value/df.valueTotal, axis=1) 
100 loops, best of 3: 6.31 ms per loop 
+0

Pouvez-vous expliquer pourquoi? Quand j'ai ajouté cette condition au début et que je l'ai chronométrée contre la vôtre, elle semblait presque identique (11.2 ms contre 10 ms). Autant que je sache, les opérations que j'utilise sont généralement très efficaces. Tous les calculs sont goupbys, jointures et s'applique. Je ne vois aucune raison de m'attendre à ce que cela soit très lent - et je ne le vois pas non plus dans le timing. Bien que je sois prêt à me tromper, et intéressé si quelque chose me manque. –

+0

Habituellement, je ne préfère pas «appliquer», il tue parfois la performance. Et OP ne voulait effectuer l'opération que sur les lignes 'DESTCD == 'E''. L'ajout de nouvelles colonnes ne serait pas nécessaire. Votre solution est également belle. Et les horaires dépendent de la machine aussi. Il a fallu 1,2 s pour votre solution dans ma machine. – Dark

+0

Merci! J'ai utilisé ceci avec un peu de modification pour permettre différents aggs pour d'autres cols tels que val2: gb = df.groupby (['DESTCD', 'PAR NAME'], as_index = Faux) .agg ({'valeur': somme, 'valeur2': lambda x: len (x)}) gb_parname = gb.groupby (['PAR NOM ']) [' valeur ']. Sum() out = gb.join (nom_gb, on =' PAR NOM ', rsuffix =' Total ') out.loc [:,' derived '] = out.apply (lambda df: df.value * 100.0/df.valueTotal, axis = 1) out [out ["DESTCD"] == 'E'] –

1

lieu de tableau croisé dynamique, vous pouvez utiliser plusieurs méthodes de GroupBy basées sur PAR NAME puis appliquer l'opération tu veux. i.e.

new = df[df['DESTCD']=='E'].groupby('PAR NAME')['value'].sum()*100/df.groupby('PAR NAME')['value'].sum() 

Sortie:

 
PAR NAME 
abc 16.666667 
def 77.777778 
Name: value, dtype: float64 

Si vous voulez timings

%%timeit 

df[df['DESTCD']=='E'].groupby('PAR NAME')['value'].sum()*100/df.groupby('PAR NAME')['value'].sum() 
100 loops, best of 3: 4.03 ms per loop 

%%timeit 
df = pd.concat([df]*10000) 
df[df['DESTCD']=='E'].groupby('PAR NAME')['value'].sum()*100/df.groupby('PAR NAME')['value'].sum() 

100 loops, best of 3: 15.6 ms per loop 
+0

Merci! c'est une solution tout aussi bonne et qui sert mon but; J'ai choisi la bonne réponse seulement sur la base de ce qui était minutieusement plus rapide sur mon pc –

0

relativement efficace J'ai aussi trouvé un moyen de répondre à la question par pivot qui est tout aussi efficace que la réponse sélectionnée! Ajout ici pour la commodité des autres:

df.pivot_table(index="PAR NAME",values=["value"],aggfunc={'value':lambda x: x[df.iloc[x.index]['DESTCD']=='E'].sum()*100.0/x.sum()}) 

logique étant que aggfunc fonctionne uniquement avec des séries en question et ne peut faire référence à toute autre série jusqu'à ce que vous les obtenez par l'indexation de la principale df.