Aplicar funciones a elementos de Series o DataFrames (II)¶

En el vídeo anterior vimos cómo aplicar funciones universales de numpy o los métodos de pandas a DataFrames. Pero puede ser que queramos aplicar nuestras propias funciones.

In [1]:
# Preliminares
import pandas as pd
import numpy as np

Al igual que en el vídeo anterior, para ilustrar, generamos un DataFrame usando un generador de números aleatorios.

In [2]:
# Usamos el generador por defecto del módulo random de numpy
rng = np.random.default_rng()
# 
df = pd.DataFrame(
    rng.standard_normal(size=10).reshape(5, 2),
    columns=list('xy')
)
df
Out[2]:
x y
0 -0.131601 -0.186435
1 -1.908522 0.039505
2 0.324792 1.964660
3 0.093982 0.760966
4 1.246065 -0.346505

Empezamos por crear una función

In [10]:
def mi_funcion(x):
    return 'es positivo' if x >= 0 else 'es negativo'
# hemos usado la construcción if else

Para aplicar nuestra función a cada elemento del DataFrame usamos applymap

In [12]:
df.applymap(mi_funcion)
Out[12]:
x y
0 es negativo es negativo
1 es negativo es positivo
2 es positivo es positivo
3 es positivo es positivo
4 es positivo es negativo

Si quisieramos aplicar nuestra función a cada elemento de una Serie usaríamos apply:

In [13]:
df['x'].apply(mi_funcion)
Out[13]:
0    es negativo
1    es negativo
2    es positivo
3    es positivo
4    es positivo
Name: x, dtype: object

Las funciones anónimas en Python¶

Es muy útil tener la posibilidad de definir sobre la marcha una función sin darle un nombre y usarla como argumento de un método. Se hace con lambda.

In [5]:
# Para obtener el doble de cada elemento
df.applymap(lambda x: x * 2)
Out[5]:
x   -0.375284
y    2.232192
dtype: float64

El método apply para un DataFrame¶

El método apply si se aplica a un DataFrame debe tener como argumento a una función que se aplica a toda una columna o toda una fila, no a un elemento individual.

In [14]:
# Ejemplo con una función que se aplica a un vector: calculamos el número de valores positivos por columna
df.apply(lambda x: (x >= 0).sum())
Out[14]:
x    3
y    3
dtype: int64

El método apply admite también el argumento axis=1, para que se aplique la función fila por fila.

In [15]:
df.apply(lambda x: (x >= 0).sum(), axis=1)
Out[15]:
0    0
1    1
2    2
3    2
4    1
dtype: int64

Es muy recomendable evitar apply si es posible, y usar las funciones de numpy o los métodos de pandas que son muy optimizados y llevan a cabo la iteración en C. apply lleva a cabo la iteración en Python.

En el caso anterior, podríamos por ejemplo haber obtenido el mismo resultado usando directamente el método sum en pandas.

In [16]:
(df >= 0).sum()
Out[16]:
x    3
y    3
dtype: int64
In [17]:
# Fila por fila:
(df >= 0).sum(axis=1)
Out[17]:
0    0
1    1
2    2
3    2
4    1
dtype: int64