2016-07-05 19 views
8

Se ho un DataFrame tale che:Espandere colonna dataframe panda in più righe

pd.DataFrame({"name" : "John", 
       "days" : [[1, 3, 5, 7]] 
       }) 

dà questa struttura:

  days name 
0 [1, 3, 5, 7] John 

Come si espandono al seguente?

days name 
0  1 John 
1  3 John 
2  5 John 
3  7 John 
+0

Non capisco completamente perché vuoi farlo? È perché hai un dizionario come questo e vuoi trasformarlo in un dataframe? E nella colonna 'nome' vuoi avere lo stesso valore fino in fondo? –

risposta

7

Si potrebbe utilizzare df.itertuples per scorrere ogni riga, e utilizzare una lista di comprensione per rimodellare i dati nella forma desiderata:

import pandas as pd 

df = pd.DataFrame({"name" : ["John", "Eric"], 
       "days" : [[1, 3, 5, 7], [2,4]]}) 
result = pd.DataFrame([(d, tup.name) for tup in df.itertuples() for d in tup.days]) 
print(result 

rendimenti

0  1 
0 1 John 
1 3 John 
2 5 John 
3 7 John 
4 2 Eric 
5 4 Eric 

Divakar's solution , using_repeat, è il più veloce:

In [48]: %timeit using_repeat(df) 
1000 loops, best of 3: 834 µs per loop 

In [5]: %timeit using_itertuples(df) 
100 loops, best of 3: 3.43 ms per loop 

In [7]: %timeit using_apply(df) 
1 loop, best of 3: 379 ms per loop 

In [8]: %timeit using_append(df) 
1 loop, best of 3: 3.59 s per loop 

Qui è la configurazione utilizzata per il punto di riferimento di cui sopra:

import numpy as np 
import pandas as pd 

N = 10**3 
df = pd.DataFrame({"name" : np.random.choice(list('ABCD'), size=N), 
        "days" : [np.random.randint(10, size=np.random.randint(5)) 
           for i in range(N)]}) 

def using_itertuples(df): 
    return pd.DataFrame([(d, tup.name) for tup in df.itertuples() for d in tup.days]) 

def using_repeat(df): 
    lens = [len(item) for item in df['days']] 
    return pd.DataFrame({"name" : np.repeat(df['name'].values,lens), 
          "days" : np.concatenate(df['days'].values)}) 

def using_apply(df): 
    return (df.apply(lambda x: pd.Series(x.days), axis=1) 
      .stack() 
      .reset_index(level=1, drop=1) 
      .to_frame('day') 
      .join(df['name'])) 

def using_append(df): 
    df2 = pd.DataFrame(columns = df.columns) 
    for i,r in df.iterrows(): 
     for e in r.days: 
      new_r = r.copy() 
      new_r.days = e 
      df2 = df2.append(new_r) 
    return df2 
+0

Ti disturbo, come ho appena modificato il mio per sostituire 'np.concatenate' con' np.hstack'. Sembra che sia un po 'più veloce. Ti dispiace, aggiornando i tempi con esso? :) – Divakar

+0

@Divakar: Sono stupito. Questo è un bel miglioramento! – unutbu

+0

Apprezzi gli aggiornamenti! Bene, sono sorpreso di vedere np.hstack essere più veloce di np.concatenate, poiché ho avuto l'impressione che tutti quegli hstack e vstacks siano derivati ​​da np.concatenate. Forse con i panda, sta facendo ottimizzazioni? Non sono sicuro! – Divakar

0

un'altra soluzione:

In [139]: (df.apply(lambda x: pd.Series(x.days), axis=1) 
    .....: .stack() 
    .....: .reset_index(level=1, drop=1) 
    .....: .to_frame('day') 
    .....: .join(df['name']) 
    .....:) 
Out[139]: 
    day name 
0 1 John 
0 3 John 
0 5 John 
0 7 John 
4

Ecco qualcosa con NumPy -

lens = [len(item) for item in df['days']] 
df_out = pd.DataFrame({"name" : np.repeat(df['name'].values,lens), 
       "days" : np.hstack(df['days']) 
       }) 

Come indicato in @unutbu's solutionnp.concatenate(df['days'].values) sarebbe più veloce di np.hstack(df['days']).

Utilizza una comprensione del ciclo per estrarre le lunghezze di ciascun elemento 'days', che deve essere minimo in termini di runtime.

run

Campione -

>>> df 
      days name 
0 [1, 3, 5, 7] John 
1  [2, 4] Eric 
>>> lens = [len(item) for item in df['days']] 
>>> pd.DataFrame({"name" : np.repeat(df['name'].values,lens), 
...    "days" : np.hstack(df['days']) 
...    }) 
    days name 
0  1 John 
1  3 John 
2  5 John 
3  7 John 
4  2 Eric 
5  4 Eric 
1

Probabilmente in qualche modo simile a questo: 'native' soluzione

df2 = pd.DataFrame(columns = df.columns) 
for i,r in df.iterrows(): 
    for e in r.days: 
     new_r = r.copy() 
     new_r.days = e 
     df2 = df2.append(new_r) 
df2 
1

Un panda - abbiamo Unstack la colonna in una serie, quindi unire nuovamente sulla base dell'indice:

import pandas as pd #import 
x2 = x.days.apply(lambda x: pd.Series(x)).unstack() #make an unstackeded series, x2 
x.drop('days', axis = 1).join(pd.DataFrame(x2.reset_index(level=0, drop=True))) #drop the days column, join to the x2 series