Cómo explotar una lista dentro de una celda de marco de datos en filas separadas

Resuelto SpicyClubSauce asked hace 9 años • 10 respuestas

Estoy buscando convertir una celda de pandas que contiene una lista en filas para cada uno de esos valores.

Entonces, toma esto:

ingrese la descripción de la imagen aquí

Si quisiera descomprimir y apilar los valores de la nearest_neighborscolumna para que cada valor sea una fila dentro de cada opponentíndice, ¿cuál sería la mejor manera de hacerlo? ¿Existen métodos pandas destinados a operaciones como esta?

SpicyClubSauce avatar Sep 09 '15 05:09 SpicyClubSauce
Aceptado

La explosión de una columna tipo lista se ha simplificado significativamente en pandas 0.25 con la adición del explode()método:

df = (pd.DataFrame({'name': ['A.J. Price'] * 3, 
                    'opponent': ['76ers', 'blazers', 'bobcats'], 
                    'nearest_neighbors': [['Zach LaVine', 'Jeremy Lin', 'Nate Robinson', 'Isaia']] * 3})
      .set_index(['name', 'opponent']))

df.explode('nearest_neighbors')

Afuera:

                    nearest_neighbors
name       opponent                  
A.J. Price 76ers          Zach LaVine
           76ers           Jeremy Lin
           76ers        Nate Robinson
           76ers                Isaia
           blazers        Zach LaVine
           blazers         Jeremy Lin
           blazers      Nate Robinson
           blazers              Isaia
           bobcats        Zach LaVine
           bobcats         Jeremy Lin
           bobcats      Nate Robinson
           bobcats              Isaia
joelostblom avatar Jul 19 '2019 05:07 joelostblom

En el código siguiente, primero restablezco el índice para facilitar la iteración de la fila.

Creo una lista de listas donde cada elemento de la lista externa es una fila del DataFrame de destino y cada elemento de la lista interna es una de las columnas. Esta lista anidada finalmente se concatenará para crear el DataFrame deseado.

Utilizo una lambdafunción junto con la iteración de la lista para crear una fila para cada elemento del nearest_neighborsemparejado con el relevante namey opponent.

Finalmente, creo un nuevo DataFrame a partir de esta lista (usando los nombres de las columnas originales y estableciendo el índice nuevamente en namey opponent).

df = (pd.DataFrame({'name': ['A.J. Price'] * 3, 
                    'opponent': ['76ers', 'blazers', 'bobcats'], 
                    'nearest_neighbors': [['Zach LaVine', 'Jeremy Lin', 'Nate Robinson', 'Isaia']] * 3})
      .set_index(['name', 'opponent']))

>>> df
                                                    nearest_neighbors
name       opponent                                                  
A.J. Price 76ers     [Zach LaVine, Jeremy Lin, Nate Robinson, Isaia]
           blazers   [Zach LaVine, Jeremy Lin, Nate Robinson, Isaia]
           bobcats   [Zach LaVine, Jeremy Lin, Nate Robinson, Isaia]

df.reset_index(inplace=True)
rows = []
_ = df.apply(lambda row: [rows.append([row['name'], row['opponent'], nn]) 
                         for nn in row.nearest_neighbors], axis=1)
df_new = pd.DataFrame(rows, columns=df.columns).set_index(['name', 'opponent'])

>>> df_new
                    nearest_neighbors
name       opponent                  
A.J. Price 76ers          Zach LaVine
           76ers           Jeremy Lin
           76ers        Nate Robinson
           76ers                Isaia
           blazers        Zach LaVine
           blazers         Jeremy Lin
           blazers      Nate Robinson
           blazers              Isaia
           bobcats        Zach LaVine
           bobcats         Jeremy Lin
           bobcats      Nate Robinson
           bobcats              Isaia

EDITAR JUNIO 2017

Un método alternativo es el siguiente:

>>> (pd.melt(df.nearest_neighbors.apply(pd.Series).reset_index(), 
             id_vars=['name', 'opponent'],
             value_name='nearest_neighbors')
     .set_index(['name', 'opponent'])
     .drop('variable', axis=1)
     .dropna()
     .sort_index()
     )
Alexander avatar Sep 09 '2015 03:09 Alexander

Utilice apply(pd.Series)y stack, luego reset_indexyto_frame

In [1803]: (df.nearest_neighbors.apply(pd.Series)
              .stack()
              .reset_index(level=2, drop=True)
              .to_frame('nearest_neighbors'))
Out[1803]:
                    nearest_neighbors
name       opponent
A.J. Price 76ers          Zach LaVine
           76ers           Jeremy Lin
           76ers        Nate Robinson
           76ers                Isaia
           blazers        Zach LaVine
           blazers         Jeremy Lin
           blazers      Nate Robinson
           blazers              Isaia
           bobcats        Zach LaVine
           bobcats         Jeremy Lin
           bobcats      Nate Robinson
           bobcats              Isaia

Detalles

In [1804]: df
Out[1804]:
                                                   nearest_neighbors
name       opponent
A.J. Price 76ers     [Zach LaVine, Jeremy Lin, Nate Robinson, Isaia]
           blazers   [Zach LaVine, Jeremy Lin, Nate Robinson, Isaia]
           bobcats   [Zach LaVine, Jeremy Lin, Nate Robinson, Isaia]
Zero avatar Sep 11 '2017 17:09 Zero

Creo que esta es una muy buena pregunta, en Hive usarías EXPLODE, creo que se puede argumentar que Pandas debería incluir esta funcionalidad de forma predeterminada. Probablemente explotaría la columna de la lista con una comprensión del generador anidado como esta:

pd.DataFrame({
    "name": i[0],
    "opponent": i[1],
    "nearest_neighbor": neighbour
    }
    for i, row in df.iterrows() for neighbour in row.nearest_neighbors
    ).set_index(["name", "opponent"])
maxymoo avatar Sep 09 '2015 00:09 maxymoo