Tengo un pandas dataframe
en el que una columna de cadenas de texto contiene valores separados por comas. Quiero dividir cada campo CSV y crear una nueva fila por entrada (suponga que CSV está limpio y solo necesita dividirse en ','). Por ejemplo, a
debería convertirse en b
:
In [7]: a
Out[7]:
var1 var2
0 a,b,c 1
1 d,e,f 2
In [8]: b
Out[8]:
var1 var2
0 a 1
1 b 1
2 c 1
3 d 2
4 e 2
5 f 2
Hasta ahora, he probado varias funciones simples, pero el .apply
método parece aceptar solo una fila como valor de retorno cuando se usa en un eje, y no puedo ponerme .transform
a trabajar. ¡Cualquier sugerencia sería muy apreciada!
Datos de ejemplo:
from pandas import DataFrame
import numpy as np
a = DataFrame([{'var1': 'a,b,c', 'var2': 1},
{'var1': 'd,e,f', 'var2': 2}])
b = DataFrame([{'var1': 'a', 'var2': 1},
{'var1': 'b', 'var2': 1},
{'var1': 'c', 'var2': 1},
{'var1': 'd', 'var2': 2},
{'var1': 'e', 'var2': 2},
{'var1': 'f', 'var2': 2}])
Sé que esto no funcionará porque perdemos metadatos de DataFrame al pasar por numpy, pero debería darle una idea de lo que intenté hacer:
def fun(row):
letters = row['var1']
letters = letters.split(',')
out = np.array([row] * len(letters))
out['var1'] = letters
a['idx'] = range(a.shape[0])
z = a.groupby('idx')
z.transform(fun)
Respuestas:
Qué tal algo como esto:
Entonces solo tienes que cambiar el nombre de las columnas
fuente
ACTUALIZACIÓN2: función vectorizada más genérica, que funcionará para columnas múltiples
normal
y múltipleslist
Manifestación:
Múltiples
list
columnas: todas laslist
columnas deben tener el mismo número de elementos en cada fila:preservar los valores del índice original:
Preparar:
Columna CSV:
Con este pequeño truco podemos convertir una columna similar a CSV en una
list
columna:ACTUALIZACIÓN: enfoque vectorizado genérico (funcionará también para múltiples columnas):
DF original:
Solución:
primero convierta las cadenas CSV a listas:
Ahora podemos hacer esto:
ANTIGUA respuesta:
Inspirado por la solución @AFinkelstein , quería que fuera un poco más generalizado, lo que podría aplicarse al DF con más de dos columnas y tan rápido, bueno, casi tan rápido como la solución de AFinkelstein):
fuente
.explode()
método en la API (también vea esta respuesta ).Después de una dolorosa experimentación para encontrar algo más rápido que la respuesta aceptada, conseguí que esto funcionara. Funcionó alrededor de 100 veces más rápido en el conjunto de datos que lo probé.
Si alguien conoce una manera de hacer esto más elegante, modifique mi código. No pude encontrar una manera que funcione sin establecer las otras columnas que desea mantener como índice y luego restablecer el índice y cambiar el nombre de las columnas, pero me imagino que hay algo más que funciona.
fuente
TypeError: object of type 'float' has no len()
al primer paso (DataFrame(df.var1.str.split(',').tolist())
)NaN
en esa columna, por lo que el reemplazo esb = DataFrame(a.var1.str.split(',').values.tolist(), index=a.var2).stack()
Aquí hay una función que escribí para esta tarea común. Es más eficiente que los métodos
Series
/stack
. Se conservan el orden de las columnas y los nombres.Con esta función, la pregunta original es tan simple como:
fuente
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 .Como tiene una lista de cadenas separadas por comas, divida la cadena en una coma para obtener una lista de elementos, luego llame
explode
a esa columna.Tenga en cuenta que
explode
solo funciona en una sola columna (por ahora).Las NaN y las listas vacías obtienen el tratamiento que se merecen sin que tenga que saltar por los aros para hacerlo bien.
Esta es una gran ventaja sobre las soluciones basadas en
ravel
+repeat
(que ignoran por completo las listas vacías y se ahogan con los NaN).fuente
Pregunta similar a: pandas: ¿Cómo divido el texto de una columna en varias filas?
Podrías hacerlo:
fuente
s.name = 'var1'
TL; DR
Demostración
Creemos un nuevo marco de datos
d
que tenga listasComentarios generales
Voy a usar
np.arange
conrepeat
producir trama de datos de posiciones de índice que puedo usar coniloc
.Preguntas más frecuentes
¿Por qué no lo uso
loc
?Debido a que el índice puede no ser único y usar
loc
devolverá cada fila que coincida con un índice consultado.¿Por qué no usas el
values
atributo y lo cortas?Al llamar
values
, si la totalidad del marco de datos está en un "bloque" cohesivo, Pandas devolverá una vista de la matriz que es el "bloque". De lo contrario, los pandas tendrán que improvisar una nueva matriz. Al enredar, esa matriz debe ser de un tipo uniforme. A menudo eso significa devolver una matriz con dtype que esobject
. Al usar eniloc
lugar de cortar elvalues
atributo, me alivio de tener que lidiar con eso.¿Por qué lo usas
assign
?Cuando uso
assign
el mismo nombre de columna que estoy explotando, sobrescribo la columna existente y mantengo su posición en el marco de datos.¿Por qué se repiten los valores del índice?
En virtud del uso
iloc
en posiciones repetidas, el índice resultante muestra el mismo patrón repetido. Una repetición para cada elemento de la lista o cadena.Esto se puede restablecer con
reset_index(drop=True)
Para cuerdas
No quiero tener que dividir las cuerdas prematuramente. Entonces, en cambio, cuento las ocurrencias del
sep
argumento suponiendo que si me dividiera, la longitud de la lista resultante sería uno más que el número de separadores.Entonces lo uso
sep
parajoin
las cuerdas entoncessplit
.Para listas
Similar a las cadenas, excepto que no necesito contar las ocurrencias
sep
porque ya está dividido.Yo uso Numpy's
concatenate
para atascar las listas juntas.fuente
Existe la posibilidad de dividir y explotar el marco de datos sin cambiar la estructura del marco de datos
Entrada:
Fuera:
Editar-1
Volver a indexar según la columna de referencia y alinear la información del valor de la columna con la pila
Fuera:
fuente
Se me ocurrió una solución para los marcos de datos con números arbitrarios de columnas (mientras que solo separaba las entradas de una columna a la vez).
fuente
Aquí hay un mensaje bastante sencillo que utiliza el
split
método de pandasstr
accessor y luego usa NumPy para aplanar cada fila en una sola matriz.Los valores correspondientes se recuperan repitiendo la columna no dividida con la cantidad correcta de veces
np.repeat
.fuente
He estado luchando con la experiencia de falta de memoria usando varias formas de explotar mis listas, así que preparé algunos puntos de referencia para ayudarme a decidir qué respuestas votar. Probé cinco escenarios con proporciones variables de la longitud de la lista al número de listas. Compartiendo los resultados a continuación:
Tiempo: (menos es mejor, haga clic para ver la versión grande)
Uso máximo de memoria: (menos es mejor)
Conclusiones :
Los detalles completos (funciones y código de evaluación comparativa) se encuentran en esta esencia de GitHub . Tenga en cuenta que el problema de referencia se simplificó y no incluyó la división de cadenas en la lista, que la mayoría de las soluciones se realizaron de manera similar.
fuente
Basado en la excelente solución de @ DMulligan , aquí hay una función genérica vectorizada (sin bucles) que divide una columna de un marco de datos en varias filas y la fusiona nuevamente con el marco de datos original. También utiliza una gran
change_column_order
función genérica de esta respuesta .Ejemplo:
Tenga en cuenta que conserva el índice original y el orden de las columnas. También funciona con marcos de datos que tienen índice no secuencial.
fuente
La función de división de cadena puede tomar una opción de argumento booleano 'expandir'.
Aquí hay una solución usando este argumento:
fuente
Solo utilicé la excelente respuesta de jiln de arriba, pero necesitaba expandirme para dividir múltiples columnas. Pensé que iba a compartir.
fuente
la respuesta de MaxU actualizada con soporte MultiIndex
fuente
One-liner usando
split(___, expand=True)
y los argumentoslevel
yname
parareset_index()
:Si necesita
b
verse exactamente como en la pregunta, también puede hacer:fuente
Se me ocurrió la siguiente solución a este problema:
fuente
Otra solución que usa el paquete de copia de Python
fuente
Aquí hay muchas respuestas, pero me sorprende que nadie haya mencionado la función de explosión de pandas incorporada. Consulte el siguiente enlace: https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.explode.html#pandas.DataFrame.explode
Por alguna razón no pude acceder a esa función, así que utilicé el siguiente código:
Arriba hay una muestra de mis datos. Como puede ver, la columna de personas tenía una serie de personas, y estaba tratando de explotarla. El código que he dado funciona para datos de tipo de lista. Así que trate de obtener sus datos de texto separados por comas en formato de lista. Además, dado que mi código utiliza funciones integradas, es mucho más rápido que las funciones personalizadas / de aplicación.
Nota: Es posible que deba instalar pandas_explode con pip.
fuente
Tuve un problema similar, mi solución fue convertir el marco de datos a una lista de diccionarios primero, luego hacer la transición. Aquí está la función:
Ejemplo:
También puede cambiar un poco la función para admitir la separación de filas de tipo de lista.
fuente