Pandas apply / map / applymap

apply, map y applymap son las tres funciones principales de Pandas para la transformación de datos; pueden realizar operaciones flexibles elemento por elemento o en lote sobre DataFrame o Series.


Series.map

mapEs un método de Series que se utiliza para transformar cada elemento de una Series.

Uso básico

Ejemplo

import pandas as pd

# Crear una Series
s = pd.Series([1, 2, 3, 4, 5])

print("Datos originales:")
print(s)
print()

# Usar una función
print("Cada elemento * 2:")
print(s.map(lambda x: x * 2))
print()

# Usar mapeo con diccionario
mapping = {1: "A", 2: "B", 3: "C", 4: "D", 5: "E"}
print("Mapeo con diccionario:")
print(s.map(mapping))
print()

# Usar mapeo con Series
mapping_series = pd.Series(["A", "B", "C", "D", "E"], index=[1, 2, 3, 4, 5])
print("Mapeo con Series:")
print(s.map(mapping_series))

Manejo de valores faltantes

Ejemplo

import pandas as pd
import numpy as np

s = pd.Series([1, 2, np.nan, 4, 5])

print("Contiene NaN:")
print(s)
print()

# map omite NaN de forma predeterminada
print("Procesamiento con map (omite NaN):")
print(s.map(lambda x: x * 2 if pd.notna(x) else -1))

DataFrame.applymap

applymapEs un método de DataFrame que aplica la función elemento por elemento (nota: Pandas 2.0+ recomienda usarDataFrame.mapen su lugar).

Ejemplo

import pandas as pd
import numpy as np

df = pd.DataFrame({
    "A": [1, 2, 3],
    "B": [4, 5, 6],
    "C": [7, 8, 9]
})

print("Datos originales:")
print(df)
print()

# Multiplicar cada elemento por 2
print("Cada elemento * 2:")
print(df.applymap(lambda x: x * 2))
print()

# Conservar 2 decimales
print("Conservar 2 decimales:")
print(df.applymap(lambda x: round(x, 2)))

applymap realiza operaciones elemento por elemento, lo que puede ser lento para grandes volúmenes de datos. Si solo necesitas operar sobre columnas numéricas, considera usar operaciones vectorizadas o apply con el parámetro axis.


DataFrame.apply

applyEs el método más flexible; se pueden aplicar funciones a lo largo de un eje.

Aplicación por columna

Ejemplo

import pandas as pd
import numpy as np

df = pd.DataFrame({
    "A": [1, 2, 3, 4, 5],
    "B": [10, 20, 30, 40, 50],
    "C": [100, 200, 300, 400, 500]
})

print("Datos originales:")
print(df)
print()

# Por defecto axis=0, se aplica por columna
print("Suma por columna:")
print(df.apply(sum))
print()

print("Máximo por columna:")
print(df.apply(max))

Aplicación por fila

Ejemplo

import pandas as pd
import numpy as np

df = pd.DataFrame({
    "A": [1, 2, 3],
    "B": [10, 20, 30],
    "C": [100, 200, 300]
})

print("Datos originales:")
print(df)
print()

# axis=1, se aplica por fila
print("Suma por fila:")
print(df.apply(sum, axis=1))
print()

# Máximo - mínimo por fila
print("Rango por fila:")
print(df.apply(lambda x: x.max() - x.min(), axis=1))

Agregación con aggfunc

Ejemplo

import pandas as pd
import numpy as np

df = pd.DataFrame({
    "A": [1, 2, 3],
    "B": [10, 20, 30]
})

# Aplicar varias funciones a la vez
print("Suma y media a la vez:")
print(df.apply([sum, np.mean]))
print()

# Devolver varios valores
result = df.apply(lambda x: pd.Series({
    "sum": x.sum(),
    "mean": x.mean(),
    "max": x.max()
}, index=["sum", "mean", "max"]))

print("Devolver varios valores:")
print(result)

Series.apply

Series también puede usar apply: su funcionalidad es similar a map pero más flexible.

Ejemplo

import pandas as pd
import numpy as np

s = pd.Series([1, 4, 9, 16, 25])

print("Datos originales:")
print(s)
print()

# Raíz cuadrada
print("Raíz cuadrada:")
print(s.apply(np.sqrt))
print()

# Retorno condicional
print("Evaluación condicional:")
print(s.apply(lambda x: "Grande" if x > 10 else "Pequeño"))

Comparación de rendimiento

Ejemplo

import pandas as pd
import numpy as np
import time

# Crear datos grandes
n = 100000
s = pd.Series(np.random.randn(n))

# Probar map vs apply
func = lambda x: x * 2 + 1

start = time.time()
result1 = s.map(func)
map_time = time.time() - start

start = time.time()
result2 = s.apply(func)
apply_time = time.time() - start

# Vectorización (la más rápida)
start = time.time()
result3 = s * 2 + 1
vec_time = time.time() - start

print(f"Tiempo de map: {map_time:.4f}s")
print(f"Tiempo de apply: {apply_time:.4f}s")
print(f"Tiempo de vectorización: {vec_time:.4f}s")

print("\nConclusión: prioriza las operaciones vectorizadas; son las de mejor rendimiento")

Práctica: transformación de datos

Ejemplo

import pandas as pd
import numpy as np

# Crear un DataFrame de ejemplo
df = pd.DataFrame({
    "Nombre": ["Zhang San", "Li Si", "Wang Wu", "Zhao Liu"],
    "Edad": [25, 30, 28, 35],
    "Salario": [12000, 15000, 11000, 18000],
    "Departamento": ["Tecnología", "Ventas", "Tecnología", "Operaciones"]
})

print("Datos originales:")
print(df)
print()

# Usar apply para cálculos a nivel de fila
def calculate(row):
    """Calcular el ingreso anual y el salario después de impuestos"""
    annual = row["Salario"] * 12
    tax = annual * 0.1 if annual > 120000 else annual * 0.05
    after_tax = annual - tax
    return pd.Series({
        "Salario anual": annual,
        "Impuesto": tax,
        "Después de impuestos": after_tax
    })

result = df.apply(calculate, axis=1)
df_result = pd.concat([df, result], axis=1)

print("Resultados del cálculo:")
print(df_result)

Cómo elegir entre los tres

Método Objeto aplicable Escenario Rendimiento
map Series Transformación a nivel de elemento, mapeo con diccionario Rápido
applymap DataFrame Transformación a nivel de elemento (columnas no numéricas) Lento
apply Series/DataFrame Agregación por filas y columnas, funciones personalizadas Medio

Cuando se puedan usar operaciones vectorizadas (directamente con operadores), no uses apply/map; cuando se pueda usar map, no uses apply.

Otras extensiones