Transform en pandas

julio 05, 2020

Transform es método del objeto dataframe de pandas que permite aplicar funciones sobre filas o columnas, tal como un apply, o sobre secciones de dataframe (grupos) y agregar los resultados en el dataframe original, por lo que representa una opción muy poderosa además de los métodos aggregate, filter y apply.
Creado: Julio 5 de 2020
Testeado en:
  • Python 3.6.4
  • pandas 0.24.2
Descargar código: https://github.com/vilmauricio/pyciencia/tree/master/pandas
In [1]:
import pandas as pd
import numpy as np

datos

Se crea un dataframe de ejemplo con la lista de compras de los clientes, el valor de la compra y el número de artículos llevados en esta
In [2]:
datos = {
    'nombre': ['Carlos', 'María', 'Carlos', 'María', 'María', 'Miguel', 'Miguel', 'Miguel'],
    'compra': [10000, 23000, 32000, 43000, 12000, 45000, 43000, 12000],
    'num_articulos': [2, 3, 1, 4, 3, 2, 4, 2]
}

df = pd.DataFrame(datos)
df
Out[2]:

nombre compra num_articulos
0 Carlos 10000 2
1 María 23000 3
2 Carlos 32000 1
3 María 43000 4
4 María 12000 3
5 Miguel 45000 2
6 Miguel 43000 4
7 Miguel 12000 2

transform vs apply

El transform puede aplicar funciones sobre las columnas como un apply
In [3]:
df['num_articulos'].transform(func=['sqrt', 'exp'])
Out[3]:

sqrt exp
0 1.414214 7.389056
1 1.732051 20.085537
2 1.000000 2.718282
3 2.000000 54.598150
4 1.732051 20.085537
5 1.414214 7.389056
6 2.000000 54.598150
7 1.414214 7.389056
De igual manera se puede obtener este resultado usando apply
In [4]:
df['num_articulos'].apply(['sqrt', 'exp'])
Out[4]:

sqrt exp
0 1.414214 7.389056
1 1.732051 20.085537
2 1.000000 2.718282
3 2.000000 54.598150
4 1.732051 20.085537
5 1.414214 7.389056
6 2.000000 54.598150
7 1.414214 7.389056
Para este uso no representa una diferencia relevante en tiempo de ejecución respecto al apply
In [5]:
%%timeit
df['num_articulos'].transform(func=['sqrt', 'exp'])
1.36 ms ± 40.8 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
In [6]:
%%timeit
df['num_articulos'].apply(['sqrt', 'exp'])
1.4 ms ± 125 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
Pero al realizar una prueba con un dataframe de 10 millones de registros se observa que el apply se ejecuta mas rápido
In [7]:
df_long = pd.DataFrame(np.random.randint(0, 100, size=(10000000, 4)), columns=list('ABCD'))
In [8]:
%%timeit
df_long['A'].transform(func=['sqrt', 'exp'])
496 ms ± 95.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [9]:
%%timeit
df_long['A'].apply(['sqrt', 'exp'])
646 ms ± 201 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)

Transform posterior a un groupby

El uso mas interesante del transform es precisamente aplicar funciones sobre los grupos generados por un groupby y agregar la información resultante en el dataframe original, a diferencia de los métodos aggregate, filter y apply que generan un dataframe diferente en donde cada fila proviene de cada uno de los grupos formados.
Tomando el dataframe
In [10]:
df
Out[10]:

nombre compra num_articulos
0 Carlos 10000 2
1 María 23000 3
2 Carlos 32000 1
3 María 43000 4
4 María 12000 3
5 Miguel 45000 2
6 Miguel 43000 4
7 Miguel 12000 2
Por ejemplo si tomamos este dataframe y agrupamos los datos por el nombre del cliente y estimamos el total de la compra con un aggregation se genera un nuevo dataframe con tres filas, los nombres de los clientes: Carlos, María y Miguel.
In [11]:
df.groupby('nombre').agg({'compra': sum})
Out[11]:

compra
nombre
Carlos 42000
María 78000
Miguel 100000
Por otro lado, al usar transform se le agrega la columna compra_total con los valores con el total comprado por cada persona al dataframe original. Por ejemplo Carlos realizó 2 compras por un valor total de $10000+320000 = 42000$, por lo que cada fila en donde en la columna nombre esté Carlos tendrá el valor de $42000$ en la columna total_compra.
In [12]:
%time df['total_compra'] = df.groupby('nombre')['compra'].transform(sum)
df
Wall time: 3.5 ms
Out[12]:

nombre compra num_articulos total_compra
0 Carlos 10000 2 42000
1 María 23000 3 78000
2 Carlos 32000 1 42000
3 María 43000 4 78000
4 María 12000 3 78000
5 Miguel 45000 2 100000
6 Miguel 43000 4 100000
7 Miguel 12000 2 100000
Esta operación se puede realizar sin usar el transform, por ejemplo:
In [13]:
%time total_compra = df.groupby('nombre')['compra'].sum().rename('total_compra_merge').reset_index()
%time df_1 = df.merge(total_compra)
df_1
Wall time: 3.5 ms
Wall time: 4.5 ms
Out[13]:

nombre compra num_articulos total_compra total_compra_merge
0 Carlos 10000 2 42000 42000
1 Carlos 32000 1 42000 42000
2 María 23000 3 78000 78000
3 María 43000 4 78000 78000
4 María 12000 3 78000 78000
5 Miguel 45000 2 100000 100000
6 Miguel 43000 4 100000 100000
7 Miguel 12000 2 100000 100000
Otra forma usando agg
In [14]:
%time total_compra = df.groupby('nombre').agg({'compra': sum}).rename(columns={'compra':'total_compra'})
%time df_1 = df.merge(total_compra.reset_index())
df_1
Wall time: 4 ms
Wall time: 6.51 ms
Out[14]:

nombre compra num_articulos total_compra
0 Carlos 10000 2 42000
1 Carlos 32000 1 42000
2 María 23000 3 78000
3 María 43000 4 78000
4 María 12000 3 78000
5 Miguel 45000 2 100000
6 Miguel 43000 4 100000
7 Miguel 12000 2 100000
El transform permite realizar las estimaciones de forma mas limpia.
Otro ejemplo para calcular esta vez los porcentajes de cada compra para cada persona:
In [15]:
total_compra = df.groupby('nombre')['compra'].transform(sum)
df['porcentaje'] = df['compra']/total_compra * 100
df
Out[15]:

nombre compra num_articulos total_compra porcentaje
0 Carlos 10000 2 42000 23.809524
1 María 23000 3 78000 29.487179
2 Carlos 32000 1 42000 76.190476
3 María 43000 4 78000 55.128205
4 María 12000 3 78000 15.384615
5 Miguel 45000 2 100000 45.000000
6 Miguel 43000 4 100000 43.000000
7 Miguel 12000 2 100000 12.000000
Filtrar las compras de los usuarios con compras superiores a 80000 en total
In [16]:
df[df.groupby('nombre')['compra'].transform(sum) <= 80000]
Out[16]:

nombre compra num_articulos total_compra porcentaje
0 Carlos 10000 2 42000 23.809524
1 María 23000 3 78000 29.487179
2 Carlos 32000 1 42000 76.190476
3 María 43000 4 78000 55.128205
4 María 12000 3 78000 15.384615
In [ ]:
 

You Might Also Like

0 comments

Apoyado por: