transform
sobre GroupedDataFrame
s¶El método transform
permite calcular, para cada grupo, una o varias columnas con el mismo index
que el grupo, por lo tanto con el mismo número de filas y las mismas etiquetas. Por ejemplo puedo, dentro de cada grupo, normalizar los valores respecto a la media y la desviación típica del grupo.
Preliminares
import pandas as pd
import numpy as np
Consideramos el DataFrame:
df = pd.DataFrame(
{
"X": ['a', 'a', 'a', 'a', 'b', 'b', 'c', 'c'],
"Y": np.arange(8),
"Z": np.arange(8,16)
}
)
df
X | Y | Z | |
---|---|---|---|
0 | a | 0 | 8 |
1 | a | 1 | 9 |
2 | a | 2 | 10 |
3 | a | 3 | 11 |
4 | b | 4 | 12 |
5 | b | 5 | 13 |
6 | c | 6 | 14 |
7 | c | 7 | 15 |
Agrupamos según los valores de X
. En el vídeo anterior vimos cómo aplicar el método agg
, pasándole la función para calcular el indicador.
df.groupby('X').agg(np.mean)
Y | Z | |
---|---|---|
X | ||
a | 1.5 | 9.5 |
b | 4.5 | 12.5 |
c | 6.5 | 14.5 |
Hagamos lo mismo pero usando transform
df.groupby('X').transform(np.mean)
Y | Z | |
---|---|---|
0 | 1.5 | 9.5 |
1 | 1.5 | 9.5 |
2 | 1.5 | 9.5 |
3 | 1.5 | 9.5 |
4 | 4.5 | 12.5 |
5 | 4.5 | 12.5 |
6 | 6.5 | 14.5 |
7 | 6.5 | 14.5 |
Se puede añadir las columnas proporcionadas por transform
a nuestro DataFrame inicial
# Creamos una copia del DataFrame original
df_extended = df.copy()
Añadimos las columnas que contienen las medias de Y y Z desglosadas por grupo
df_extended[['media_grupo_Y', 'media_grupo_Z']] = df.groupby('X').transform(np.mean)
df_extended
X | Y | Z | media_grupo_Y | media_grupo_Z | |
---|---|---|---|---|---|
0 | a | 0 | 8 | 1.5 | 9.5 |
1 | a | 1 | 9 | 1.5 | 9.5 |
2 | a | 2 | 10 | 1.5 | 9.5 |
3 | a | 3 | 11 | 1.5 | 9.5 |
4 | b | 4 | 12 | 4.5 | 12.5 |
5 | b | 5 | 13 | 4.5 | 12.5 |
6 | c | 6 | 14 | 6.5 | 14.5 |
7 | c | 7 | 15 | 6.5 | 14.5 |
Vamos ahora a llevar a cabo la normalización de los valores de Y y Z, teniendo en cuenta los grupos formados por los valores de X.
# Creamos una copia del DataFrame original
df_normalizado = df.copy()
df_normalizado.groupby('X').transform(lambda x: (x - x.mean()) / x.std())
Y | Z | |
---|---|---|
0 | -1.161895 | -1.161895 |
1 | -0.387298 | -0.387298 |
2 | 0.387298 | 0.387298 |
3 | 1.161895 | 1.161895 |
4 | -0.707107 | -0.707107 |
5 | 0.707107 | 0.707107 |
6 | -0.707107 | -0.707107 |
7 | 0.707107 | 0.707107 |
Podemos sustituir las columnas Y y Z por sus equivalentes normalizados, por grupo.
df_normalizado[['Y', 'Z']] = df_normalizado.groupby('X').transform(lambda x: (x - x.mean()) / x.std())
df_normalizado
X | Y | Z | |
---|---|---|---|
0 | a | -1.161895 | -1.161895 |
1 | a | -0.387298 | -0.387298 |
2 | a | 0.387298 | 0.387298 |
3 | a | 1.161895 | 1.161895 |
4 | b | -0.707107 | -0.707107 |
5 | b | 0.707107 | 0.707107 |
6 | c | -0.707107 | -0.707107 |
7 | c | 0.707107 | 0.707107 |
Modificamos df
para introducir valores faltantes en sus columnas:
df.loc[[0, 2, 5], 'Y'] = np.NaN
df.loc[6, 'Z'] = np.NaN
df
X | Y | Z | |
---|---|---|---|
0 | a | NaN | 8.0 |
1 | a | 1.0 | 9.0 |
2 | a | NaN | 10.0 |
3 | a | 3.0 | 11.0 |
4 | b | 4.0 | 12.0 |
5 | b | NaN | 13.0 |
6 | c | 6.0 | NaN |
7 | c | 7.0 | 15.0 |
Vamos a pasar a transform
una función que tomando una columna, recorra sus elementos, si el elemento no falta, lo deja intacto. Si el elemento falta, lo sustituye por la media de la columna del grupo al que pertenece.
Para ello, vamos a usar el método where
, ver referencia
where
admite como parámetro un vector booleano y un vectorother
. Si el elemento del vector booleano esTrue
, deja intacto el elemento de la columna a la que se aplica. Si esFalse
, usa el elemento correspondiente del vectorother
.
Ejemplo: aplicamos el método where
a la columna Y, para sustituir los valores faltantes por el valor 1000.
# Usamos ~ para negar un vector booleano, True se vuelve False y viceversa
df['Y'].where( ~df['Y'].isna(), 1000)
0 1000.0 1 1.0 2 1000.0 3 3.0 4 4.0 5 1000.0 6 6.0 7 7.0 Name: Y, dtype: float64
df.groupby('X').transform(lambda x: x.where(~x.isna(), x.mean()))
Y | Z | |
---|---|---|
0 | 2.0 | 8.0 |
1 | 1.0 | 9.0 |
2 | 2.0 | 10.0 |
3 | 3.0 | 11.0 |
4 | 4.0 | 12.0 |
5 | 4.0 | 13.0 |
6 | 6.0 | 15.0 |
7 | 7.0 | 15.0 |