Curso
Los DataFrame de PySpark son fundamentales a la hora de crear canalizaciones escalables en Spark. Una cosa importante que hay que recordar es que los DataFrame son inmutables. Esto significa que, una vez que tienes uno, no puedes cambiarlo directamente; siempre obtienes un nuevo DataFrame cada vez que realizas un cambio. Aquí es donde entra en juego PySpark withColumn()
. Te ayuda a añadir, actualizar o modificar columnas, pero como los DataFrame no cambian en su lugar, siempre hay que reasignar el resultado a una nueva variable.
En este tutorial, te explicaré cómo utilizar withColumn()
para dar forma y ajustar tus conjuntos de datos, ya sea para crear características, limpiar tipos o añadir lógica.
¿Qué es withColumn() en PySpark?
En resumen, withColumn()
devuelve un nuevo DataFrame con una columna añadida o sustituida. Dado que los DataFrame no mutan, debes asignar ese valor de retorno, df = df.withColumn(...)
.
En segundo plano, cada llamada a withColumn()
añade una proyección en el plan lógico. No hay problema si solo haces uno o dos, pero encadenar muchos puede sobrecargar el plan, lo que hace que Spark funcione mucho más lento.
¿Eres nuevo en PySpark? Podrás dominar los fundamentos para manejar big data con facilidad, aprendiendo a procesar, consultar y optimizar conjuntos de datos masivos para realizar potentes análisis en nuestro curso Introducción a PySpark.
Core Uses of PySpark withColumn()
Veamos las principales formas en que utilizarás withColumn()
:
Añadir una columna constante
Supongamos que deseas añadir una marca de tiempo o un indicador. Utiliza lit()
o typedLit()
de pyspark.sql.functions
. Por ejemplo:
from pyspark.sql.functions import lit
df = df.withColumn("ingest_date", lit("2025-07-29"))
Crear una columna a partir de datos existentes
Quizás quieras obtener un valor derivado, combinar cadenas o calcular un total. Puedes hacer lo siguiente:
from pyspark.sql.functions import col, expr
df = df.withColumn("full_name", col("first_name") + expr(" ' ' + last_name"))
Las transformaciones aritméticas o basadas en expresiones también encajan aquí.
Sobrescribir una columna existente
Si ya tienes una columna y deseas cambiarla, withColumn()
simplemente la reemplaza:
df = df.withColumn("age", col("age").cast("integer"))
No es necesario eliminarlo y volver a añadirlo.
Tipos de datos de casting
Cambiar el tipo de una columna es muy sencillo:
df = df.withColumn("price", col("price").cast("decimal(10,2)"))
Me parece que esto resulta especialmente útil cuando se lee JSON o CSV sueltos, donde los tipos aparecen como cadenas.
Si buscas más ejemplos sobre qué es PySpark y cómo puedes utilizarlo, te recomiendo nuestro tutorial Introducción a PySpark.
Lógica condicional y expresiones when()
Hay ocasiones en las que necesitas hacer algo más que simples operaciones aritméticas. Quizás estés creando una columna de estado basada en una puntuación. O marcar entradas basándose en una combinación de reglas. Aquí es donde entra en juego when()
de pyspark.sql.functions
. Piensa en ello como una declaración « IF
». Puedes combinarlo con otherwise()
para cubrir múltiples rutas.
Así es como se ve:
from pyspark.sql.functions import when
df = df.withColumn(
"grade",
when(col("score") >= 90, "A")
.when(col("score") >= 80, "B")
.when(col("score") >= 70, "C")
.otherwise("F")
)
Se lee casi como inglés sencillo: Si la puntuación es de al menos 90, entonces A. Si es de 80, entonces B. Sigue adelante... y si ninguna de esas opciones coincide, ponle un suspenso. Es expresivo, y Spark convierte esa lógica en una expresión eficiente entre bastidores. Sin bucles anidados, sin llamadas « apply()
», solo DAG limpios y planes de ejecución claros.
Esto resulta útil cuando quieres evitar cambiar a SQL o saturar tu código con funciones definidas por el usuario (UDF).
Puedes aprender a manipular datos y crear conjuntos de características de machine learning en Spark utilizando SQL en Python con nuestro tutorial Introducción a Spark SQL en Python .
Transformación de columnas con funciones integradas y definidas por el usuario
A menudo necesitarás formatear o reestructurar columnas, convertir cadenas a mayúsculas, dividirlas en partes, concatenar valores, etc. PySpark cuenta con una amplia biblioteca de funciones integradas que funcionan directamente dentro de withColumn()
.
Aquí tienes un ejemplo:
from pyspark.sql.functions import upper, concat_ws, split
df = df.withColumn("full_caps", upper(col("name")))
df = df.withColumn("city_state", concat_ws(", ", col("city"), col("state")))
df = df.withColumn("first_word", split(col("description"), " ").getItem(0))
Ahora, los muebles empotrados son geniales. Rápido, nativo y optimizado. Pero a veces, hay reglas excepcionales que no encajan en ninguna categoría. Ahí es donde entran en juego las funciones definidas por el usuario (UDF).
Uso de una UDF
Supongamos que quieres calcular la longitud de una cadena y etiquetarla:
from pyspark.sql.functions import udf
from pyspark.sql.types import StringType
def label_length(x):
return "short" if len(x) < 5 else "long"
label_udf = udf(label_length, StringType())
df = df.withColumn("name_length_label", label_udf(col("name")))
Es bastante sencillo. Pero ten cuidado, los UDF conllevan una sobrecarga. Extraen datos del motor optimizado, los ejecutan en Python y luego los vuelven a empaquetar. Eso está bien cuando es necesario, pero para tareas de gran volumen, es preferible utilizar expresiones integradas o SQL, si es posible.
Aprende a crear, optimizar y utilizar las UDF de PySpark, incluidas las UDF de Pandas, para gestionar de forma eficaz las transformaciones de datos personalizadas y mejorar el rendimiento de Spark con nuestro tutorial Cómo utilizar eficazmente las UDF de PySpark y Pandas.
Consideraciones sobre el rendimiento y prácticas avanzadas
En algún momento, todos los usuarios de PySpark se encuentran con esto: sigues acumulando llamadas a « withColumn()
» y tu canalización se ralentiza hasta casi detenerse. ¿El motivo? Cada llamada añade una nueva capa al plan lógico, que Spark tiene que analizar, optimizar y ejecutar.
Si solo vas a añadir una o dos columnas, no hay problema. Pero si estás encadenando cinco, seis o más, deberías empezar a pensar de otra manera.
Uso select()
al añadir muchas columnas
En lugar de llamar repetidamente a withColumn()
, crea una nueva lista de columnas utilizando select()
:
df = df.select(
"*",
(col("salary") * 0.1).alias("bonus"),
(col("age") + 5).alias("age_plus_five")
)
Este enfoque crea el plan lógico de una sola vez.
Más información sobre select()
y otros métodos en nuestra hoja de referencia rápida de PySpark « » (Creación de mapas y mapas de mapas): Tutorial de Spark en Python.
¿Qué hay de withColumns()
?
Introducido en Spark 3.3, withColumns()
te permite añadir varias columnas de una sola vez. No más llamadas repetidas. Es un método tipo diccionario:
df = df.withColumns({
"bonus": col("salary") * 0.1,
"age_plus_five": col("age") + 5
})
No todo el mundo se ha puesto al día con Spark 3.3+ todavía, pero si tú lo has hecho, utiliza esto. Es más limpio, más rápido y evita el problema de la «muerte por encadenamiento».
Ejemplo completo de PySpark con Column()
Supongamos que estás trabajando con datos de actividad de usuarios para una plataforma basada en suscripciones. Tu DataFrame sin procesar tiene un aspecto similar al siguiente:
from pyspark.sql import SparkSession
spark = SparkSession.builder.appName("withColumn-demo").getOrCreate()
data = [
("Alice", "NY", 24, 129.99),
("Bob", "CA", 31, 199.95),
("Charlie", "TX", 45, 0.0),
("Diana", "WA", 17, 19.99)
]
columns = ["name", "state", "age", "purchase_amount"]
df = spark.createDataFrame(data, columns)
Tienes nombres, estados, edades y cuánto han gastado. Bastante básico. Pero en el mundo real, eso nunca es suficiente. Esto es lo que haremos a continuación:
- Añade una columna constante para la fecha de ingestión.
- Crea una nueva columna que indique si el usuario es adulto.
- Formatea
purchase_amount
con dos decimales. - Clasifica a los usuarios según su nivel de gasto.
- Aplicar una función personalizada para etiquetar a los usuarios.
- Utiliza
withColumns()
para envolver valores adicionales de una forma más limpia.
¿Te estás preparando para tu próxima entrevista? El artículo «Las 36 preguntas y respuestas más frecuentes en entrevistas sobre PySpark para 2025» ofrece una guía completa sobre preguntas y respuestas en entrevistas sobre PySpark, que abarca desde conceptos básicos hasta técnicas avanzadas y estrategias de optimización.
Paso 1: Añadir una fecha de ingesta constante
Es recomendable programar cuándo se introducen los datos en tu sistema.
from pyspark.sql.functions import lit
df = df.withColumn("ingest_date", lit("2025-07-29"))
Paso 2: Denunciar a adultos frente a menores
Podrías haber utilizado solo col("age") >= 18
, pero envolverlo con when()
te da un control total si la lógica se complica.
from pyspark.sql.functions import when, col
df = df.withColumn(
"is_adult",
when(col("age") >= 18, True).otherwise(False)
)
Paso 3: Formato purchase_amount
Los tipos de conversión son una de las tareas de limpieza más frecuentes que realizarás, especialmente al leer archivos CSV o JSON.
df = df.withColumn("purchase_amount", col("purchase_amount").cast("decimal(10,2)"))
Paso 4: Clasificar el nivel de gasto
Supongamos que quieres tres grupos: «ninguno», «bajo» y «alto».
df = df.withColumn(
"spend_category",
when(col("purchase_amount") == 0, "none")
.when(col("purchase_amount") < 100, "low")
.otherwise("high")
)
Esto te ayuda a segmentar a los usuarios sin tener que ejecutar una consulta por separado.
Paso 5: Etiquetar a los usuarios que utilizan una UDF
Ahora, una regla inventada. Supongamos que etiquetas a una persona en función de la longitud de su nombre.
from pyspark.sql.functions import udf
from pyspark.sql.types import StringType
def user_label(name):
return "simple" if len(name) <= 4 else "complex"
label_udf = udf(user_label, StringType())
df = df.withColumn("label", label_udf(col("name")))
Paso 6: Añadir varias columnas adicionales de una sola vez
Quizás quieras añadir algunos más, la edad en meses y un mensaje de bienvenida.
df = df.withColumns({
"age_in_months": col("age") * 12,
"welcome_msg": col("name") + lit(", welcome aboard!")
})
Mucho más limpio que llamar dos veces a withColumn()
.
Así es como se ve el DataFrame final cuando lo muestras:
df.show(truncate=False)
nombre |
estado |
edad |
purchase_amount |
ingest_date |
is_adult |
spend_category |
etiqueta |
age_in_months |
welcome_msg |
Alicia |
NY |
24 |
129,99 |
2025-07-29 |
Verdadero |
alto |
complejo |
288 |
¡Alice, bienvenida a bordo! |
Bob |
CA |
31 |
199,95 |
2025-07-29 |
Verdadero |
alto |
simple |
372 |
¡Bob, bienvenido a bordo! |
Charlie |
TX |
45 |
0,00 |
2025-07-29 |
Verdadero |
ninguno |
complejo |
540 |
Charlie, ¡bienvenido a bordo! |
Diana |
WA |
17 |
19,99 |
2025-07-29 |
Falso |
bajo |
complejo |
204 |
¡Diana, bienvenida a bordo! |
Este tipo de pila de transformación es habitual en la ingeniería de características, la elaboración de informes o la limpieza de fuentes de terceros.
Aprende los fundamentos del trabajo con big data con PySpark en nuestro curso Fundamentos de big data con PySpark .
Prácticas recomendadas y errores comunes de withColumn()
withColumn()
, de PySpark, puede parecer sencillo, pero puede confundir incluso a ingenieros experimentados si no se tiene cuidado. Aquí tienes algunos ejemplos de cosas que pueden arruinar silenciosamente tu canalización, junto con algunos hábitos que pueden evitarte sorpresas desagradables.
Siempre reasigna el resultado.
Esto es básico, pero aún así pilla a la gente desprevenida: « withColumn()
» no modifica tu DataFrame original. Te da uno nuevo. Si olvidas reasignarlo, tu cambio se perderá.
df.withColumn("new_col", lit(1)) # This won't do anything
df = df.withColumn("new_col", lit(1)) # This works
Ten cuidado con las sobrescrituras accidentales.
Supongamos que tu DataFrame tiene una columna llamada «status». Ejecuta esto:
df = df.withColumn("Status", lit("Active"))
Parece inofensivo, ¿verdad? Sin embargo, Spark trata los nombres de columna sin distinción entre mayúsculas y minúsculas de forma predeterminada. Eso significa que acabas de sobrescribir tu columna de estado original. Sin darme cuenta.
Una solución es comprobar siempre df.columns
antes y después. O, si tu canalización lo admite, activa la distinción entre mayúsculas y minúsculas utilizando:
spark.conf.set("spark.sql.caseSensitive", "true")
No utilices literales de Python en expresiones.
Esto es fácil de olvidar. Al añadir constantes, evita los valores Python sin procesar. Envuélvelos siempre con lit()
.
df = df.withColumn("region", "US") # Bad
df = df.withColumn("region", lit("US")) # Good
¿Por qué? Porque withColumn()
espera una expresión Column, no un valor sin procesar. Si cometes un error, Spark podría mostrar un error inútil o, lo que es peor, romper silenciosamente la lógica descendente.
Gestiona las excepciones fuera de withColumn()
A veces, la gente se vuelve creativa y envuelve bloques withColumn()
completos dentro de try/except. Es mejor aislar las partes riesgosas (como las UDF o las lecturas de datos) y detectar las excepciones allí. Mantén tu capa de transformación limpia y predecible.
try:
def risky_udf(x):
if not x:
raise ValueError("Empty input")
return x.lower()
except Exception as e:
print("Error in UDF definition:", e)
Deja que Spark falle pronto, no lo ocultes detrás de bloques try anidados.
Más información sobre las excepciones en Python en nuestro tutorial Manejo de excepciones y errores en Python.
Prefiere los elementos integrados a las UDF.
Claro, las UDF te dan poder. Pero tienen sus inconvenientes: rendimiento más lento, depuración más difícil y menor optimización. Si hay una función integrada que haga ese trabajo, úsala.
Esto:
df = df.withColumn("upper_name", upper(col("name")))
Es mucho más rápido que esto:
df = df.withColumn("upper_name", udf(lambda x: x.upper(), StringType())(col("name")))
Cuándo no utilizar withColumn()
A pesar de la flexibilidad de withColumn()
, hay ocasiones en las que no es la herramienta adecuada para el trabajo.
Estás remodelando muchas columnas a la vez.
Si te encuentras diciendo « withColumn()
» diez veces seguidas, es hora de cambiar de estrategia. Utiliza select()
en su lugar y escribe tus transformaciones como parte de una nueva proyección.
df = df.select(
col("name"),
col("age"),
(col("salary") * 0.15).alias("bonus"),
(col("score") + 10).alias("adjusted_score")
)
Es más claro, funciona mejor y hace que el optimizador de Spark trabaje a tu favor en lugar de en tu contra.
Quieres escribir lógica al estilo SQL.
Si tu equipo depende en gran medida de SQL y ya has registrado el DataFrame como una vista temporal, a menudo es más limpio simplemente ejecutar una consulta SQL.
df.createOrReplaceTempView("users")
df2 = spark.sql("""
SELECT name, age,
CASE WHEN age >= 18 THEN true ELSE false END AS is_adult
FROM users
""")
Esto puede resultar más fácil para los analistas con conocimientos de SQL o los equipos que trabajan tanto con Spark como con bases de datos tradicionales.
Desarrolla tus habilidades en SQL con cursos interactivos, programas y proyectos seleccionados por expertos del mundo real utilizando nuestros cursos de SQL.
Ya estás en Spark 3.3+
Si utilizas Spark 3.3 o una versión posterior y necesitas añadir varias columnas, withColumns()
es tu aliado. No solo es cómodo, sino que puede ser más rápido bajo el capó al crear una única actualización del plan lógico.
Aprende a implementar la gestión de datos distribuidos y machine learning en Spark utilizando el paquete PySpark de nuestro curso Fundamentos de PySpark.
Conclusión
La función « withColumn()
» (Añadir características) de PySpark es una de las herramientas más versátiles de tu arsenal de transformación de datos, ya que te permite añadir, modificar y diseñar características directamente en un flujo de trabajo centrado en DataFrame. Desde tipos de conversión y constantes de inyección hasta la incorporación de lógica compleja con condicionales y UDF, withColumn()
te ayuda a transformar datos desordenados en canalizaciones listas para la producción.
Pero ese poder conlleva una gran responsabilidad. El uso excesivo de withColumn()
en cadenas largas puede degradar silenciosamente el rendimiento al sobrecargar el plan lógico, lo que dificulta la optimización y la depuración de tus trabajos de Spark. Por eso, saber cuándo recurrir a select()
, withColumns()
o incluso SQL puede marcar la diferencia entre un trabajo que avanza lentamente y uno que se adapta a las necesidades.
Dada la constante evolución de Spark, especialmente con características como withColumns()
en Spark 3.3+, comprender el funcionamiento interno y las ventajas y desventajas de cada método en cuanto al rendimiento es fundamental para escribir código más limpio, rápido y fácil de mantener.
Domina las técnicas que hay detrás de las transformaciones de columnas a gran escala, evita los inconvenientes de la inflación de planes y aprende cómo los profesionales optimizan los procesos de características en nuestro curso Ingeniería de características con PySpark .
PySpark withColumn() FAQs
¿Por qué mi trabajo en Spark es lento cuando utilizas mucho withColumn()?
Cuando encadenas varias llamadas a withColumn()
, Spark añade cada una de ellas como un paso independiente en el plan de ejecución lógico. Con el tiempo, esto puede convertirse en un plan inflado que es más difícil de optimizar y más lento de ejecutar. En lugar de apilar diez llamadas withColumn()
, intenta crear tus nuevas columnas dentro de una sola select()
o utiliza withColumns()
para añadir varias columnas de una sola vez.
¿Puedes usar withColumn() para eliminar una columna?
No, withColumn()
solo añade o sustituye columnas, no las elimina. Si deseas eliminar una columna, utiliza drop()
en su lugar. También puedes utilizar select()
para conservar solo las columnas que necesites.
¿Por qué aparece un error cuando intento utilizar una cadena o un número en withColumn()?
Esto suele ocurrir cuando pasas un valor Python sin procesar en lugar de envolverlo con lit()
.withColumn()
espera una expresión Spark Column. Esta es la forma correcta:
from pyspark.sql.functions import lit
df = df.withColumn("new_col", lit(42))
