Cómo explotar una lista dentro de una celda de marco de datos en filas separadas
Estoy buscando convertir una celda de pandas que contiene una lista en filas para cada uno de esos valores.
Entonces, toma esto:
Si quisiera descomprimir y apilar los valores de la nearest_neighbors
columna 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?
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
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 lambda
función junto con la iteración de la lista para crear una fila para cada elemento del nearest_neighbors
emparejado con el relevante name
y opponent
.
Finalmente, creo un nuevo DataFrame a partir de esta lista (usando los nombres de las columnas originales y estableciendo el índice nuevamente en name
y 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()
)
Utilice apply(pd.Series)
y stack
, luego reset_index
yto_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]
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"])