J'ai deux dataframes avec des observations sur les lignes et des fonctionnalités (ou l'appartenance à un groupe) sur des colonnes, par exemple:
> aggr_df a b c g1 2 2 5 g2 2 3 2
Je souhaite regrouper et agréger (par somme) les valeurs dans le premier dataframe ( data_df
) conditionnel aux valeurs binaires (masques) dans le deuxième dataframe ( mask_df
). Le résultat devrait être le suivant (groupes x fonctionnalités):
> data_df a b c A 1 2 1 B 0 1 3 C 0 0 1 D 2 1 1 E 1 1 1 > mask_df g1 g2 A 0 1 B 1 0 C 1 0 D 1 0 E 0 1
Y a-t-il un moyen dans les pandas de regrouper le premier dataframe ( data_df
) en utilisant masques contenus dans un second dataframe ( mask_df
) en une seule commande?
5 Réponses :
La meilleure façon de procéder est de combiner les dataframes. Vous pouvez combiner sur l'index en utilisant d'abord une instruction de jointure. df_merge = data_df.merge (aggr_df, left_on = True, right_on = True)
. Ensuite, vous pouvez simplement utiliser df_merge
pour vos opérations de regroupement.
Voici une manière d'utiliser une compréhension de liste:
pd.DataFrame([(data_df.T * mask_df[i]).sum(axis=1) for i in mask_df.columns], index = mask.columns) a b c g1 2 2 5 g2 2 3 2
Vous pouvez le faire à moindre coût avec dot
et groupby
:
mask_df.dot(mask_df.columns) A g2 B g1 C g1 D g1 E g2 dtype: object
Où ,
data_df.groupby(mask_df.dot(mask_df.columns)).sum() a b c g1 2 2 5 g2 2 3 2
Je pensais que le point était destiné à la multiplication matricielle, j'ai du mal à comprendre comment cela produirait le résultat souhaité. (Cela pourrait être la façon dont un manque de compréhension sur la façon dont cela fonctionne sous le capot.)
La fonctionnalité de @ Polkaguy6000 pandas dot
a été étendue pour fonctionner avec des chaînes pour faire des trucs sympas comme ça.
Cela semble aussi fonctionner lorsque mask_df
est une dataframe de booléens
Malheureusement, cette solution (bien que rapide) ne fonctionne qu'avec des masques qui ne se chevauchent pas, voir W-B's ou ma réponse pour une solution plus générale (dans le cas de l'agrégation somme / moyenne).
@ gc5 Nice, merci d'avoir souligné la distinction. J'ai voté pour votre réponse.
Notez que cela fonctionnera même dans le cas où les observations de la première trame de données ( data_df
) appartiennent à plusieurs masques dans la deuxième trame de données ( mask_df
).
> pd.concat({x:data_df.mul(mask_df[x],0).sum() for x in mask_df}).unstack() a b c g1 2 2 5 g2 2 3 2
C'est plus général
Cependant, cela est très lent. Voir ma réponse pour une solution plus rapide dans le cas de l'agrégation somme / moyenne.
J'ai décidé d'écrire une autre réponse depuis:
Dans mon cas, j'ai remarqué que je pouvais obtenir le même résultat simplement en utilisant un produit scalaire de mask_df
avec data_df
:
> mask_df.T.dot(data_df).div(mask_df.sum(), axis=0)
Dans le cas particulier de l'obtention de la moyenne au lieu de la somme, il est possible de mettre à l'échelle le mask_df
par le nombre d'unités pour chaque groupe:
> mask_df.T.dot(data_df)