J'ai un cadre de données pandas
avec plusieurs lignes qui sont presque des doublons les uns des autres, à l'exception d'une valeur. Mon objectif est de fusionner ou de "fusionner" ces lignes en une seule ligne, sans additionner les valeurs numériques.
Voici un exemple de ce avec quoi je travaille:
Name Sid Use_Case Revenue
A xx01 Voice $10.00
A xx01 SMS $10.00
B xx02 Voice $5.00
C xx03 Voice $15.00
C xx03 SMS $15.00
C xx03 Video $15.00
Et voici ce que j'aimerais:
Name Sid Use_Case Revenue
A xx01 Voice, SMS $10.00
B xx02 Voice $5.00
C xx03 Voice, SMS, Video $15.00
La raison pour laquelle je ne veux pas additionner la colonne "Revenue" est parce que ma table est le résultat d'un pivot sur plusieurs périodes où "Revenue" finit simplement par être répertorié plusieurs fois au lieu d'avoir une valeur différente par "Use_Case" .
Quelle serait la meilleure façon de résoudre ce problème? J'ai examiné la fonction groupby()
mais je ne la comprends toujours pas très bien.
Je pense que vous pouvez utiliser groupby
avec aggregate
first
et la fonction personnalisée ', '.join
:
df = df.groupby('Name').agg({'Sid':'first',
'Use_Case': ', '.join,
'Revenue':'first' }).reset_index()
#change column order
print df[['Name','Sid','Use_Case','Revenue']]
Name Sid Use_Case Revenue
0 A xx01 Voice, SMS $10.00
1 B xx02 Voice $5.00
2 C xx03 Voice, SMS, Video $15.00
Belle idée de commentaire, merci Goyo :
df = df.groupby(['Name','Sid','Revenue'])['Use_Case'].apply(', '.join).reset_index()
#change column order
print df[['Name','Sid','Use_Case','Revenue']]
Name Sid Use_Case Revenue
0 A xx01 Voice, SMS $10.00
1 B xx02 Voice $5.00
2 C xx03 Voice, SMS, Video $15.00
J'utilisais du code que je ne pensais pas être optimal et j'ai finalement trouvé réponse de Jezrael . Mais après l'avoir utilisé et exécuté un test timeit
, je suis en fait retourné à ce que je faisais, qui était:
cmnts = {}
for i, row in df.iterrows():
while True:
try:
if row['Use_Case']:
cmnts[row['Name']].append(row['Use_Case'])
else:
cmnts[row['Name']].append('n/a')
break
except KeyError:
cmnts[row['Name']] = []
df.drop_duplicates('Name', inplace=True)
df['Use_Case'] = ['; '.join(v) for v in cmnts.values()]
Selon mon test 100 run timeit
, la méthode itérer et remplacer est un ordre de grandeur plus rapide que la méthode groupby
.
import pandas as pd
from my_stuff import time_something
df = pd.DataFrame({'a': [i / (i % 4 + 1) for i in range(1, 10001)],
'b': [i for i in range(1, 10001)]})
runs = 100
interim_dict = 'txt = {}\n' \
'for i, row in df.iterrows():\n' \
' try:\n' \
" txt[row['a']].append(row['b'])\n\n" \
' except KeyError:\n' \
" txt[row['a']] = []\n" \
"df.drop_duplicates('a', inplace=True)\n" \
"df['b'] = ['; '.join(v) for v in txt.values()]"
grouping = "new_df = df.groupby('a')['b'].apply(str).apply('; '.join).reset_index()"
print(time_something(interim_dict, runs, beg_string='Interim Dict', glbls=globals()))
print(time_something(grouping, runs, beg_string='Group By', glbls=globals()))
rendements:
Interim Dict
Total: 59.1164s
Avg: 591163748.5887ns
Group By
Total: 430.6203s
Avg: 4306203366.1827ns
où time_something
est une fonction qui chronomètre un extrait avec timeit
et renvoie le résultat dans le format ci-dessus.
Vous pouvez groupby
et apply
la fonction list
:
>>> df['Use_Case'].groupby([df.Name, df.Sid, df.Revenue]).apply(list).reset_index()
Name Sid Revenue 0
0 A xx01 $10.00 [Voice, SMS]
1 B xx02 $5.00 [Voice]
2 C xx03 $15.00 [Voice, SMS, Video]
(Si vous êtes préoccupé par les doublons, utilisez set
au lieu de list
.)