2016-03-28 15 views
6

Ho un dataframe pandas con più righe che si avvicinano a duplicati l'uno con l'altro, ad eccezione di un valore. Il mio obiettivo è unire o "coalizzare" queste righe in una singola riga, senza sommare i valori numerici.panda - Unisci quasi duplicate righe in base al valore di colonna

Ecco un esempio di quello che sto lavorando con:

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 

Ed ecco quello che vorrei:

Name Sid Use_Case   Revenue 
A  xx01 Voice, SMS   $10.00 
B  xx02 Voice    $5.00 
C  xx03 Voice, SMS, Video $15.00 

Il motivo per cui non voglio riassumere il "Ricavi "La colonna è perché la mia tabella è il risultato di un pivot su diversi periodi in cui" Revenue "finisce semplicemente per essere elencato più volte invece di avere un valore diverso per" Use_Case ".

Quale sarebbe il modo migliore per affrontare questo problema? Ho esaminato la funzione groupby() ma ancora non la capisco molto bene.

+0

Se la risposta di mio o di Ami è stata utile, non dimenticare [accettare] (http://meta.stackexchange.com/questions/5234/how-does-accepting-an-answer-work). Grazie. – jezrael

+0

Grazie! Entrambi funzionano bene. Potrei dover unire un paio di colonne in quanto il mio dataset effettivo è molto più ampio del 4 che ho fornito, ma questo dovrebbe fare il trucco –

risposta

5

Penso che si possa utilizzare groupby con aggregatefirst e funzione personalizzata ', '.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 

Bella idea da commentare, grazie 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 
+1

Vorrei raggruppare per tutto tranne "Use_Case", per ogni evenienza. Anche la funzione aggregata può essere solo "," .join ", non c'è bisogno di usare' lambda' .. – Goyo

+0

Risolve questo break se la tua colonna ha valori che "join" non piace. Ho dovuto lanciare un '.map (str)' prima di 'apply' perché funzioni in modo pulito. –

+1

Sì, o usare '.astype (str)', è una funzione per il cast in stringa. – jezrael

1

È possibile groupby e apply la funzione 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] 

(Nel caso in cui si sono preoccupati per i duplicati, utilizzare set invece di list.)

1

stavo usando un codice che non pensavo che era ottimale e finalmente trovato jezrael's answer. Ma dopo l'uso e l'esecuzione di un test timeit, Io sono andato di nuovo a quello che stavo facendo, che era:

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()] 

Secondo la mia prova di 100 run timeit, l'iterata e sostituire il metodo è un ordine di grandezza più veloce di il metodo 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())) 

rendimenti:

Interim Dict 
    Total: 59.1164s 
    Avg: 591163748.5887ns 

Group By 
    Total: 430.6203s 
    Avg: 4306203366.1827ns 

dove time_something è una funzione che volte un frammento con timeit e restituisce il risultato nel formato sopra.

Problemi correlati