python - Dividir la célula en varias filas en el marco de datos de pandas
dataframe (4)
Tengo un marco de datos que contiene datos de pedidos, cada pedido tiene varios paquetes almacenados como columnas de cadena separadas por comas [
package
y
package_code
package
]
Quiero dividir los datos de los paquetes y crear una fila para cada paquete, incluidos los detalles de su pedido
Aquí hay un ejemplo de marco de datos de entrada:
import pandas as pd
df = pd.DataFrame({"order_id":[1,3,7],"order_date":["20/5/2018","22/5/2018","23/5/2018"], "package":["p1,p2,p3","p4","p5,p6"],"package_code":["#111,#222,#333","#444","#555,#666"]})
Y esto es lo que estoy tratando de lograr como resultado:
¿Cómo puedo hacer eso con los pandas?
Aquí hay una forma de usar
numpy.repeat
e
itertools.chain
.
Conceptualmente, esto es exactamente lo que quiere hacer: repetir algunos valores, encadenar otros.
Recomendado para pequeñas cantidades de columnas, de lo contrario
stack
métodos basados en
stack
pueden ser mejores.
import numpy as np
from itertools import chain
# return list from series of comma-separated strings
def chainer(s):
return list(chain.from_iterable(s.str.split('','')))
# calculate lengths of splits
lens = df[''package''].str.split('','').map(len)
# create new dataframe, repeating or chaining as appropriate
res = pd.DataFrame({''order_id'': np.repeat(df[''order_id''], lens),
''order_date'': np.repeat(df[''order_date''], lens),
''package'': chainer(df[''package'']),
''package_code'': chainer(df[''package_code''])})
print(res)
order_id order_date package package_code
0 1 20/5/2018 p1 #111
0 1 20/5/2018 p2 #222
0 1 20/5/2018 p3 #333
1 3 22/5/2018 p4 #444
2 7 23/5/2018 p5 #555
2 7 23/5/2018 p6 #666
Cerca del método del frío :-)
df.set_index([''order_date'',''order_id'']).apply(lambda x : x.str.split('','')).stack().apply(pd.Series).stack().unstack(level=2).reset_index(level=[0,1])
Out[538]:
order_date order_id package package_code
0 20/5/2018 1 p1 #111
1 20/5/2018 1 p2 #222
2 20/5/2018 1 p3 #333
0 22/5/2018 3 p4 #444
0 23/5/2018 7 p5 #555
1 23/5/2018 7 p6 #666
Eche un vistazo a la versión 0.25 de los pandas de hoy: https://pandas.pydata.org/pandas-docs/stable/whatsnew/v0.25.0.html#series-explode-to-split-list-like-values-to-rows
df = pd.DataFrame([{''var1'': ''a,b,c'', ''var2'': 1}, {''var1'': ''d,e,f'', ''var2'': 2}])
df.assign(var1=df.var1.str.split('','')).explode(''var1'').reset_index(drop=True)
Esto debería funcionar para cualquier número de columnas como esta.
La esencia es un poco de magia de apilar pila con
str.split
.
(df.set_index([''order_date'', ''order_id''])
.stack()
.str.split('','', expand=True)
.stack()
.unstack(-2)
.reset_index(-1, drop=True)
.reset_index()
)
order_date order_id package package_code
0 20/5/2018 1 p1 #111
1 20/5/2018 1 p2 #222
2 20/5/2018 1 p3 #333
3 22/5/2018 3 p4 #444
4 23/5/2018 7 p5 #555
5 23/5/2018 7 p6 #666
Hay otra alternativa de rendimiento que involucra la
chain
, pero necesitaría encadenar y repetir explícitamente cada columna (un pequeño problema con muchas columnas).
Elija lo que mejor se adapte a la descripción de su problema, ya que no hay una respuesta única.
Detalles
Primero, establezca las columnas que no se tocarán como el índice.
df.set_index([''order_date'', ''order_id''])
package package_code
order_date order_id
20/5/2018 1 p1,p2,p3 #111,#222,#333
22/5/2018 3 p4 #444
23/5/2018 7 p5,p6 #555,#666
A continuación,
stack
las filas.
_.stack()
order_date order_id
20/5/2018 1 package p1,p2,p3
package_code #111,#222,#333
22/5/2018 3 package p4
package_code #444
23/5/2018 7 package p5,p6
package_code #555,#666
dtype: object
Tenemos una serie ahora.
Así que llame a
str.split
en coma.
_.str.split('','', expand=True)
0 1 2
order_date order_id
20/5/2018 1 package p1 p2 p3
package_code #111 #222 #333
22/5/2018 3 package p4 None None
package_code #444 None None
23/5/2018 7 package p5 p6 None
package_code #555 #666 None
Necesitamos deshacernos de los valores NULL, así que vuelve a llamar a la
stack
.
_.stack()
order_date order_id
20/5/2018 1 package 0 p1
1 p2
2 p3
package_code 0 #111
1 #222
2 #333
22/5/2018 3 package 0 p4
package_code 0 #444
23/5/2018 7 package 0 p5
1 p6
package_code 0 #555
1 #666
dtype: object
Casi estámos allí.
Ahora queremos que el segundo último nivel del índice se convierta en nuestras columnas, así que desapíllelo usando
unstack(-2)
(
unstack
en el segundo último nivel)
_.unstack(-2)
package package_code
order_date order_id
20/5/2018 1 0 p1 #111
1 p2 #222
2 p3 #333
22/5/2018 3 0 p4 #444
23/5/2018 7 0 p5 #555
1 p6 #666
Deshazte del último nivel superfluo usando
reset_index
:
_.reset_index(-1, drop=True)
package package_code
order_date order_id
20/5/2018 1 p1 #111
1 p2 #222
1 p3 #333
22/5/2018 3 p4 #444
23/5/2018 7 p5 #555
7 p6 #666
Y finalmente,
_.reset_index()
order_date order_id package package_code
0 20/5/2018 1 p1 #111
1 20/5/2018 1 p2 #222
2 20/5/2018 1 p3 #333
3 22/5/2018 3 p4 #444
4 23/5/2018 7 p5 #555
5 23/5/2018 7 p6 #666