Tengo un marco de datos donde algunas celdas contienen listas de múltiples valores. En lugar de almacenar múltiples valores en una celda, me gustaría expandir el marco de datos para que cada elemento de la lista obtenga su propia fila (con los mismos valores en todas las demás columnas). Entonces si tengo:
import pandas as pd
import numpy as np
df = pd.DataFrame(
{'trial_num': [1, 2, 3, 1, 2, 3],
'subject': [1, 1, 1, 2, 2, 2],
'samples': [list(np.random.randn(3).round(2)) for i in range(6)]
}
)
df
Out[10]:
samples subject trial_num
0 [0.57, -0.83, 1.44] 1 1
1 [-0.01, 1.13, 0.36] 1 2
2 [1.18, -1.46, -0.94] 1 3
3 [-0.08, -4.22, -2.05] 2 1
4 [0.72, 0.79, 0.53] 2 2
5 [0.4, -0.32, -0.13] 2 3
¿Cómo convierto a forma larga, por ejemplo:
subject trial_num sample sample_num
0 1 1 0.57 0
1 1 1 -0.83 1
2 1 1 1.44 2
3 1 2 -0.01 0
4 1 2 1.13 1
5 1 2 0.36 2
6 1 3 1.18 0
# etc.
El índice no es importante, está bien establecer columnas existentes como índice y el orden final no es importante.
df.explode('samples')
para resolver esto.explode
solo puede soportar explotar una columna por ahora.Respuestas:
Resultado:
PD: aquí puede encontrar una solución un poco más genérica
ACTUALIZACIÓN: algunas explicaciones: en mi opinión, la forma más fácil de entender este código es intentar ejecutarlo paso a paso:
en la siguiente línea estamos repitiendo valores en una columna por
N
dondeN
- es la longitud de la lista correspondiente:Esto se puede generalizar para todas las columnas, que contienen valores escalares:
usando
np.concatenate()
podemos aplanar todos los valores en lalist
columna (samples
) y obtener un vector 1D:poniendo todo esto junto:
el uso
pd.DataFrame()[df.columns]
garantizará que estemos seleccionando columnas en el orden original ...fuente
lst_col
completo; para mantener estas filas y poblar sulst_col
connp.nan
, sólo puede hacerdf[lst_col] = df[lst_col].apply(lambda x: x if len(x) > 0 else [np.nan])
antes de utilizar este método. Evidentemente.mask
no devolverá listas, de ahí el.apply
.Un poco más de lo que esperaba:
Si desea un índice secuencial, puede aplicarlo
reset_index(drop=True)
al resultado.actualización :
fuente
df.apply(lambda x: pd.Series(x['samples']),axis=1)
condf.samples.apply(pd.Series)
.df.explode()
como se muestra aquí.Pandas> = 0.25
Los métodos Series y DataFrame definen un
.explode()
método que explota las listas en filas separadas. Consulte la sección de documentos sobre Desglosar una columna tipo lista .Tenga en cuenta que esto también maneja columnas mixtas de listas y escalares, así como listas vacías y NaN de manera adecuada (esto es un inconveniente de las
repeat
soluciones basadas).Sin embargo, debe tener en cuenta que
explode
solo funciona en una sola columna (por ahora).PD: si está buscando explotar una columna de cadenas , primero debe dividir en un separador y luego usar
explode
. Vea esta (muy) respuesta relacionada de mí.fuente
También puedes usar
pd.concat
ypd.melt
para esto:Por último, si lo necesita, puede ordenar la base de las primeras tres primeras columnas.
fuente
Tratando de trabajar paso a paso a través de la solución de Roman Pekar para comprenderla mejor, se me ocurrió mi propia solución, que se usa
melt
para evitar algunos de los confusos apilamientos y restablecimientos de índices. Sin embargo, no puedo decir que obviamente sea una solución más clara:Salida (obviamente, podemos soltar la columna de muestras originales ahora):
fuente
Para aquellos que buscan una versión de la respuesta de Roman Pekar que evite el nombramiento manual de columnas:
fuente
Encontré que la forma más fácil era:
samples
columna en un DataFrameMostrado aquí:
Vale la pena señalar que esto puede haber funcionado solo porque cada ensayo tiene el mismo número de muestras (3). Puede ser necesario algo más inteligente para los ensayos de diferentes tamaños de muestra.
fuente
Respuesta muy tardía pero quiero agregar esto:
Una solución rápida con Vanilla Python que también se encarga de la
sample_num
columna en el ejemplo de OP. En mi propio conjunto de datos grande con más de 10 millones de filas y un resultado con 28 millones de filas, esto solo lleva unos 38 segundos. La solución aceptada se descompone por completo con esa cantidad de datos y conduce a unmemory error
en mi sistema que tiene 128 GB de RAM.fuente
También es muy tarde, pero aquí hay una respuesta de Karvy1 que funcionó bien para mí si no tienes pandas> = 0.25 versión: https://stackoverflow.com/a/52511166/10740287
Para el ejemplo anterior, puede escribir:
Prueba de velocidad:
1,33 ms ± 74,8 µs por bucle (media ± desviación estándar de 7 corridas, 1000 bucles cada una)
4.9 ms ± 189 µs por bucle (media ± desviación estándar de 7 corridas, 100 bucles cada una)
1,38 ms ± 25 µs por bucle (media ± desviación estándar de 7 corridas, 1000 bucles cada una)
fuente
Prueba esto en pandas> = versión 0.25
fuente
.str.split(',')
porquePrices
ya es una lista.