Crear nuevas columnas en pandas significa agregar información derivada a un DataFrame usando operaciones vectorizadas, apply o map.
Tres formas de crear columnas nuevas
Pandas ofrece tres métodos principales para enriquecer un DataFrame con columnas calculadas.
| Método | Cuándo usarlo |
|---|---|
| Operación vectorizada | Cálculos simples entre columnas existentes |
.map() |
Reemplazar valores según un diccionario o función simple |
.apply() |
Lógica condicional o cálculos complejos por fila o columna |
Conocer cuál usar en cada situación te ahorra tiempo y código innecesario.
Operaciones vectorizadas: la forma más rápida
Una operación vectorizada aplica un cálculo a toda la columna de una sola vez. No necesitas ciclos for.
Sintaxis:
df["nueva_columna"] = df["columna_a"] operador df["columna_b"]
Ejemplo 1 — Calcular margen de utilidad en Liverpool:
Supón que tienes un DataFrame de ventas de Liverpool con precio de venta y costo por producto.
import pandas as pd
ventas = pd.DataFrame({
"producto": ["Televisor", "Lavadora", "Refrigerador"],
"precio_venta": [12500, 9800, 18500],
"costo": [8000, 6200, 11000]
})
ventas["utilidad"] = ventas["precio_venta"] - ventas["costo"]
ventas["margen_pct"] = (ventas["utilidad"] / ventas["precio_venta"]) * 100
print(ventas[["producto", "utilidad", "margen_pct"]])
Resultado:
producto utilidad margen_pct
0 Televisor 4500 36.00
1 Lavadora 3600 36.73
2 Refrigerador 7500 40.54
Con dos líneas calculaste utilidad y margen para todos los registros al mismo tiempo.
Ejemplo 2 — Variación porcentual entre períodos en FEMSA:
Comparar ventas de un mes contra el mes anterior es un análisis frecuente en empresas como FEMSA.
femsa = pd.DataFrame({
"sucursal": ["CDMX", "Monterrey", "Guadalajara"],
"ventas_mayo": [320000, 210000, 185000],
"ventas_junio": [345000, 198000, 201000]
})
femsa["variacion_pct"] = (
(femsa["ventas_junio"] - femsa["ventas_mayo"]) / femsa["ventas_mayo"]
) * 100
print(femsa[["sucursal", "variacion_pct"]].round(2))
Resultado:
sucursal variacion_pct
0 CDMX 7.81
1 Monterrey -5.71
2 Guadalajara 8.65
Monterrey tuvo una caída del 5.71 %. Las otras dos sucursales crecieron.
.map(): transformar valores con un diccionario
.map() recorre cada valor de una Serie y lo reemplaza según una función o un diccionario.
Sintaxis:
df["nueva_columna"] = df["columna_origen"].map(diccionario_o_funcion)
Ejemplo — Clasificar vendedores de Mercado Libre por categoría:
vendedores = pd.DataFrame({
"nombre": ["Tienda A", "Tienda B", "Tienda C", "Tienda D"],
"nivel": ["platinum", "gold", "silver", "platinum"]
})
beneficio_map = {
"platinum": "Envío gratis + descuento 15%",
"gold": "Envío gratis",
"silver": "Sin beneficio adicional"
}
vendedores["beneficio"] = vendedores["nivel"].map(beneficio_map)
print(vendedores)
Resultado:
nombre nivel beneficio
0 Tienda A platinum Envío gratis + descuento 15%
1 Tienda B gold Envío gratis
2 Tienda C silver Sin beneficio adicional
3 Tienda D platinum Envío gratis + descuento 15%
.map() es ideal cuando tienes una tabla de equivalencias fija. Es más legible que muchos if anidados.
.apply(): lógica condicional por fila
.apply() ejecuta una función sobre cada fila o cada columna. Se usa cuando la lógica es demasiado compleja para una operación vectorizada simple.
Sintaxis con función lambda (una sola línea):
df["nueva_columna"] = df["columna"].apply(lambda x: expresion)
Sintaxis con función definida (lógica compleja):
def mi_funcion(fila):
# lógica aquí
return resultado
df["nueva_columna"] = df.apply(mi_funcion, axis=1)
El argumento axis=1 le indica a pandas que aplique la función por fila. Sin él, aplica por columna.
Ejemplo 1 — ISR estimado por salario en nómina de Bimbo:
La tasa del ISR en México varía según el nivel de ingreso. Esta es una versión simplificada para demostrar .apply().
nomina = pd.DataFrame({
"empleado": ["Ana Torres", "Luis Ramos", "Carmen Vega", "Jorge Díaz"],
"salario_mensual": [12000, 18500, 26000, 9500]
})
def calcular_isr(salario):
if salario <= 10000:
return salario * 0.10
elif salario <= 20000:
return salario * 0.16
else:
return salario * 0.21
nomina["isr_estimado"] = nomina["salario_mensual"].apply(calcular_isr)
nomina["salario_neto"] = nomina["salario_mensual"] - nomina["isr_estimado"]
print(nomina)
Resultado:
empleado salario_mensual isr_estimado salario_neto
0 Ana Torres 12000 1920.0 10080.0
1 Luis Ramos 18500 2960.0 15540.0
2 Carmen Vega 26000 5460.0 20540.0
3 Jorge Díaz 9500 950.0 8550.0
Cada empleado recibe su ISR estimado según su tramo salarial.
Ejemplo 2 — Clasificar productos de Bimbo por rango de precio:
Este ejemplo usa axis=1 para acceder a dos columnas al mismo tiempo.
productos = pd.DataFrame({
"nombre": ["Pan Blanco", "Pastel Marinela", "Donas", "Gansito"],
"precio": [28, 85, 35, 18],
"categoria": ["panadería", "pastelería", "panadería", "snack"]
})
def segmento(fila):
if fila["precio"] < 25:
return "económico"
elif fila["precio"] < 60:
return "estándar"
else:
return "premium"
productos["segmento"] = productos.apply(segmento, axis=1)
print(productos[["nombre", "precio", "segmento"]])
Resultado:
nombre precio segmento
0 Pan Blanco 28 estándar
1 Pastel Marinela 85 premium
2 Donas 35 estándar
3 Gansito 18 económico
Renombrar y reordenar columnas
Después de crear columnas nuevas, es común limpiar los nombres o cambiar el orden.
Renombrar columnas:
df.rename(columns={"margen_pct": "margen_%", "isr_estimado": "ISR"}, inplace=True)
Reordenar columnas:
df = df[["empleado", "salario_mensual", "ISR", "salario_neto"]]
Siempre reordena al final, cuando ya tienes todas las columnas calculadas.
Errores comunes
Error 1 — Olvidar axis=1 en .apply() con varias columnas
Si usas df.apply(funcion) sin axis=1, pandas aplica la función por columna, no por fila. Tu función no encontrará los campos que espera y lanzará un KeyError.
# Incorrecto
df["resultado"] = df.apply(mi_funcion) # aplica por columna
# Correcto
df["resultado"] = df.apply(mi_funcion, axis=1) # aplica por fila
Error 2 — Usar .apply() cuando una operación vectorizada es suficiente
.apply() es más lento porque procesa fila por fila. Para sumas, restas, multiplicaciones y divisiones simples, siempre usa operaciones vectorizadas.
# Lento e innecesario
df["utilidad"] = df.apply(lambda x: x["precio"] - x["costo"], axis=1)
# Rápido y correcto
df["utilidad"] = df["precio"] - df["costo"]
Error 3 — Valores faltantes en .map() producen NaN
Si un valor en la columna no existe como llave en el diccionario, .map() devuelve NaN sin advertir. Verifica siempre que tu diccionario cubra todos los valores posibles.
# Si "bronze" no está en el diccionario, esa fila tendrá NaN
df["beneficio"] = df["nivel"].map(beneficio_map)
df["beneficio"].fillna("Sin clasificar", inplace=True) # solución
Error 4 — Modificar el DataFrame original sin querer
Cuando asignas una columna con df["col"] = valores, modificas el DataFrame en su lugar. Si necesitas conservar el original para comparar, haz una copia antes.
df_copia = df.copy()
df_copia["nueva_col"] = df_copia["precio"] * 1.16
Referencia rápida
| Operación | Sintaxis | Mejor para |
|---|---|---|
| Suma de columnas | df["c"] = df["a"] + df["b"] |
Totales, subtotales |
| Porcentaje | df["c"] = df["a"] / df["b"] * 100 |
Márgenes, variaciones |
| Reemplazo por tabla | df["c"] = df["a"].map(dict) |
Etiquetas, categorías |
| Lógica condicional | df["c"] = df["a"].apply(func) |
ISR, tramos, reglas |
| Lógica multivariable | df.apply(func, axis=1) |
Combinar 2+ columnas |
Dominar estas tres técnicas te permite construir métricas completas —márgenes, ISR estimado, variaciones porcentuales— directamente dentro de tu DataFrame, sin depender de hojas de cálculo externas.