Unnest (explode) una serie de pandas

Yo tengo:

df = pd.DataFrame({'col1': ['asdf', 'xy', 'q'], 'col2': [1, 2, 3]}) col1 col2 0 asdf 1 1 xy 2 2 q 3 

Me gustaría tomar el “producto combinatorio” de cada letra de las cadenas en col1 , con cada elemento elementwise en col2 . Es decir:

  col1 col2 0 a 1 1 s 1 2 d 1 3 f 1 4 x 2 5 y 2 6 q 3 

Método actual:

 from itertools import product pieces = [] for _, s in df.iterrows(): letters = list(s.col1) prods = list(product(letters, [s.col2])) pieces.append(pd.DataFrame(prods)) pd.concat(pieces) 

¿Alguna solución más eficiente?

Usando list + str.join y np.repeat

 pd.DataFrame( { 'col1' : list(''.join(df.col1)), 'col2' : df.col2.values.repeat(df.col1.str.len(), axis=0) }) col1 col2 0 a 1 1 s 1 2 d 1 3 f 1 4 x 2 5 y 2 6 q 3 

Una solución generalizada para cualquier número de columnas es fácilmente alcanzable, sin muchos cambios en la solución:

 i = list(''.join(df.col1)) j = df.drop('col1', 1).values.repeat(df.col1.str.len(), axis=0) df = pd.DataFrame(j, columns=df.columns.difference(['col1'])) df.insert(0, 'col1', i) df col1 col2 0 a 1 1 s 1 2 d 1 3 f 1 4 x 2 5 y 2 6 q 3 

Actuación

 df = pd.concat([df] * 100000, ignore_index=True) 

 # MaxU's solution %%timeit df.col1.str.extractall(r'(.)') \ .reset_index(level=1, drop=True) \ .join(df['col2']) \ .reset_index(drop=True) 1 loop, best of 3: 1.98 s per loop 

 # piRSquared's solution %%timeit pd.DataFrame( [[x] + b for a, *b in df.values for x in a], columns=df.columns ) 1 loop, best of 3: 1.68 s per loop 

 # Wen's solution %%timeit v = df.col1.apply(list) pd.DataFrame({'col1':np.concatenate(v.values),'col2':df.col2.repeat(v.apply(len))}) 1 loop, best of 3: 835 ms per loop 

 # Alexander's solution %%timeit pd.DataFrame([(letter, i) for letters, i in zip(df['col1'], df['col2']) for letter in letters], columns=df.columns) 1 loop, best of 3: 316 ms per loop 

 %%timeit pd.DataFrame( { 'col1' : list(''.join(df.col1)), 'col2' : df.col2.values.repeat(df.col1.str.len(), axis=0) }) 10 loops, best of 3: 124 ms per loop 

Intenté progtwigr el tiempo de Vaishali, pero me tomó mucho tiempo en este conjunto de datos.

 pd.DataFrame([(letter, i) for letters, i in zip(df['col1'], df['col2']) for letter in letters], columns=df.columns) 
 In [86]: df.col1.str.extractall(r'(.)') \ .reset_index(level=1, drop=True) \ .join(df['col2']) \ .reset_index(drop=True) Out[86]: 0 col2 0 a 1 1 s 1 2 d 1 3 f 1 4 x 2 5 y 2 6 q 3 

Truco de la list 🙂

 df.col1=df.col1.apply(list) df Out[489]: col1 col2 0 [a, s, d, f] 1 1 [x, y] 2 2 [q] 3 pd.DataFrame({'col1':np.concatenate(df.col1.values),'col2':df.col2.repeat(df.col1.apply(len))}) Out[490]: col1 col2 0 a 1 0 s 1 0 d 1 0 f 1 1 x 2 1 y 2 2 q 3 

Uno mas:)

 df.set_index('col2').col1.apply(lambda x: pd.Series(list(x))).stack()\ .reset_index(1,drop = True).reset_index(name = 'col1') col2 col1 0 1 a 1 1 s 2 1 d 3 1 f 4 2 x 5 2 y 6 3 q 

Solución general con una lista de comprensión y desempaquetado inteligente:

 pd.DataFrame( [[x] + b for a, *b in df.values for x in a], columns=df.columns ) col1 col2 0 a 1 1 s 1 2 d 1 3 f 1 4 x 2 5 y 2 6 q 3