Je compile un tableau des 3 principales cultures par comté. Certains comtés ont les mêmes variétés de cultures dans le même ordre. D'autres comtés ont les mêmes variétés de cultures dans un ordre différent.
apples melons grain 8000 pears carrots raddish 9200
Je peux faire un groupby sur Crop1, Crop2 et Crop3 et obtenir la somme de total_pop:
df1_grouped apples melons grain 1500 grain melons apples 2000 melons grain apples 4500 pears carrots raddish 6700 raddish pears carrots 2500
5 Réponses :
Voici une façon de le faire.
Commençons par récupérer les valeurs uniques dans les colonnes, puis réaffectons ces valeurs au DataFrame. Nous effectuerons cette opération sur une copie des données d'origine car vous devrez peut-être conserver les données d'origine.
df.groupby(to_sum).Total_pop.sum() Crop1 Crop2 Crop3 apples grain melons 8000 carrots pears raddish 9200 Name: Total_pop, dtype: int64
Nous pouvons maintenant effectuer notre groupby
pour obtenir les résultats souhaités.
df = df1.copy() to_sum = ['Crop1', 'Crop2', 'Crop3'] df[to_sum] = pd.DataFrame(df.loc[:, to_sum] \ .apply(set, axis=1) \ .apply(sorted) \ .values \ .tolist(), columns=to_sum) print(df) County Crop1 Crop2 Crop3 Total_pop 0 Harney grain apples melons 2000 1 Baker grain apples melons 1500 2 Wheeler grain apples melons 3000 3 Hood River grain apples melons 1500 4 Wasco pears carrots raddish 2000 5 Morrow pears carrots raddish 2500 6 Union pears carrots raddish 2700 7 Lake pears carrots raddish 2000
cela ne donne pas tout à fait la bonne réponse puisque vous ne l'avez pas triée. J'ai mis à jour une version correcte en fonction de votre réponse!
comment le trions-nous exactement alors?
Méthode 1:
Combinez les colonnes crop
>>> df.groupby(grouping_cols).Total_pop.sum() Crop1 Crop2 Crop3 apples grain melons 8000 carrots pears raddish 9200 Name: Total_pop, dtype: int64
pour en faire un tuple trié p >
df = df1.copy() grouping_cols = ['Crop1', 'Crop2', 'Crop3'] df[grouping_cols] = pd.DataFrame(df.loc[:, grouping_cols] \ .apply(set, axis=1) \ .apply(sorted) .values \ .tolist(), columns=grouping_cols) >>> df.head() County Crop1 Crop2 Crop3 Total_pop 0 Harney apples grain melons 2000 1 Baker apples grain melons 1500 2 Wheeler apples grain melons 3000 3 Hood River apples grain melons 1500 4 Wasco carrots pears raddish 2000
puis passez à votre groupe normal par opération
>>> df1_grouped = df1.groupby(['sorted'])['Total_pop'].sum().reset_index() >>> df1_grouped sorted Total_pop 0 (apples, grain, melons) 8000 1 (carrots, pears, raddish) 9200
Méthode 2: Une version abrégée basée sur la réponse de aws-apprenti
>>> df1['sorted'] = df1.apply(lambda x : tuple(sorted(x['combined_temp'])),axis=1) >>> df1.head() County Crop1 Crop2 ... Total_pop combined_temp sorted 0 Harney grain melons ... 2000 [grain, melons, apples] (apples, grain, melons) 1 Baker melons grain ... 1500 [melons, grain, apples] (apples, grain, melons) 2 Wheeler melons grain ... 3000 [melons, grain, apples] (apples, grain, melons) 3 Hood River apples melons ... 1500 [apples, melons, grain] (apples, grain, melons) 4 Wasco pears carrots ... 2000 [pears, carrots, raddish] (carrots, pears, raddish)
désormais groupe par groupe par
>>> df1['combined_temp'] = df1.apply(lambda x : list([x['Crop1'], ... x['Crop2'], ... x['Crop3']]),axis=1) >>> df1.head() County Crop1 Crop2 Crop3 Total_pop combined_temp 0 Harney grain melons apples 2000 [grain, melons, apples] 1 Baker melons grain apples 1500 [melons, grain, apples] 2 Wheeler melons grain apples 3000 [melons, grain, apples] 3 Hood River apples melons grain 1500 [apples, melons, grain] 4 Wasco pears carrots raddish 2000 [pears, carrots, raddish]
mais je personnellement préférez cette réponse en utilisant numpy
parce que votre réponse originale ne fonctionnait pas sans le tri. (Je l'ai exécuté et vérifié) J'ai changé le .apply (liste)
dans votre réponse originale à .apply (trié)
pour le faire fonctionner correctement. J'ai également donné du crédit à votre réponse.
sans courir trié sur la colonne, votre réponse ne donnait pas le résultat correct après group by
que l'OP voulait sur mon système. peut-être que vous devriez revérifier? Faites donc le changement pour trier les 3 colonnes, en fonction de votre réponse bien sûr.
hé, je n'implique pas que le PO veut que la réponse soit triée. Je voulais dire que la sortie du groupe final par
n'était pas correcte quand j'ai suivi exactement ce que vous aviez donné comme réponse. J'ai donc utilisé apply (trié)
pour le corriger lorsque j'ai mis à jour ma réponse
continuons cette discussion dans le chat .
Merci pour toutes les réponses. Ils ont été d'une grande aide et éducative pour moi. La méthode 1 ci-dessus a généré les résultats que je recherchais et je l'ai donc sélectionnée.
Étant donné que vos données semblent garantir 3 cultures uniques par pays ("Je compile un tableau des 3 principales cultures par comté."), il suffit de trier les valeurs et de les réattribuer.
df1 = pd.concat([df1]*10000, ignore_index=True) cols = ['Crop1', 'Crop2', 'Crop3'] %timeit df1[cols] = np.sort(df1[cols].to_numpy(), axis=1) #36.1 ms ± 399 µs per loop (mean ± std. dev. of 7 runs, 10 loops each) to_sum = ['Crop1', 'Crop2', 'Crop3'] %timeit df1[to_sum] = pd.DataFrame(df1.loc[:, to_sum].apply(set, axis=1).apply(list).values.tolist(), columns=to_sum) #1.41 s ± 51.2 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
Alors pour résumer:
df1.groupby(cols).sum() # Total_pop #Crop1 Crop2 Crop3 #apples grain melons 8000 #carrots pears raddish 9200
L'avantage est que vous évitez Series.apply
ou .apply (axis = 1)
. Pour les DataFrames
plus grands, la différence de performances est perceptible:
import numpy as np cols = ['Crop1', 'Crop2', 'Crop3'] df1[cols] = np.sort(df1[cols].to_numpy(), axis=1) County Crop1 Crop2 Crop3 Total_pop 0 Harney apples grain melons 2000 1 Baker apples grain melons 1500 2 Wheeler apples grain melons 3000 3 Hood River apples grain melons 1500 4 Wasco carrots pears raddish 2000 5 Morrow carrots pears raddish 2500 6 Union carrots pears raddish 2700 7 Lake carrots pears raddish 2000
County Crop1 Crop2 Crop3 Total_pop Harney grain melons apples 2000 Baker melons grain apples 1500 Wheeler melons grain apples 3000 Hood River apples melons grain 1500 Wasco pears carrots raddish 2000 Morrow raddish pears carrots 2500 Union pears carrots raddish 2700 Lake pears carrots raddish 2000 x y z Total_pop apples grain melons 8000 carrots pears raddish 9200
np.bincount
pd.Series(dict(zip(map(tuple, u), s))) \ .rename_axis(['Crop1', 'Crop2', 'Crop3']).reset_index(name='Total_pop') Crop1 Crop2 Crop3 Total_pop 0 melons grain apples 8000.0 1 carrots raddish pears 9200.0
Ou, si vous voulez des colonnes séparées
pd.Series(dict(zip(map(tuple, u), s))) melons grain apples 8000.0 carrots raddish pears 9200.0 dtype: float64
Et complètement jolie
i, u = pd.factorize([*map(frozenset, zip(df1.Crop1, df1.Crop2, df1.Crop3))]) s = np.bincount(i, df1.Total_pop) pd.Series(s, u) (melons, grain, apples) 8000.0 (carrots, raddish, pears) 9200.0 dtype: float64