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:
Testeado en:
- Python 3.6.4
- pandas 0.24.2
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
Tomando el dataframe
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
Otro ejemplo para calcular esta vez los porcentajes de cada compra para cada persona:
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 [ ]: