Cómo hacer pivot en PySpark: convertir filas en columnas
Convertir filas en columnas — el llamado pivot — es una de las operaciones más pedidas al preparar datos para informes. Hacer un pivot en PySpark se reduce a combinar tres métodos (groupBy, pivot y agg), y el resultado sale listo para alimentar un dashboard o una tabla de destino. También queda el camino inverso, para cuando necesites volver al formato largo.
Requisitos previos
- Un entorno con PySpark: un notebook en Databricks o Microsoft Fabric, o una instalación local con
pip install pyspark. - Una
SparkSessionactiva. En Databricks y en Fabric ya existe una, llamadaspark. - Python básico: listas, cadenas y llamadas encadenadas de métodos.
Paso 1: Crear un DataFrame de ejemplo
Empieza con un DataFrame pequeño de ventas por mes y por región. Es el formato "largo" típico: una fila por combinación de mes y región.
from pyspark.sql import SparkSession
from pyspark.sql import functions as F
spark = SparkSession.builder.appName("pivot-demo").getOrCreate()
dados = [
("2026-01", "Norte", 1200.0),
("2026-01", "Sul", 900.0),
("2026-02", "Norte", 1500.0),
("2026-02", "Sul", 1100.0),
("2026-03", "Norte", 1300.0),
("2026-03", "Centro", 700.0),
]
vendas = spark.createDataFrame(dados, ["mes", "regiao", "valor"])
vendas.show()
Paso 2: Hacer el pivot con groupBy, pivot y agg
Un pivot en PySpark necesita siempre tres piezas: lo que queda en las filas (groupBy), la columna cuyos valores se convierten en columnas (pivot) y lo que rellena cada celda (agg). La agregación no es opcional: como varias filas pueden caer en la misma celda, Spark exige que le digas cómo combinarlas.
pivot_vendas = (
vendas
.groupBy("mes")
.pivot("regiao")
.agg(F.sum("valor"))
)
pivot_vendas.show()
El resultado tiene una fila por mes y una columna por región: mes, Centro, Norte y Sul.
Paso 3: Indicar la lista de valores (más rápido)
Sin lista, Spark tiene que recorrer los datos solo para descubrir qué regiones existen — un job extra que en un DataFrame grande sale caro. Pasar la lista evita ese trabajo y además fija el orden de las columnas.
regioes = ["Norte", "Centro", "Sul"]
pivot_rapido = (
vendas
.groupBy("mes")
.pivot("regiao", regioes)
.agg(F.sum("valor"))
)
pivot_rapido.show()
Atención: si un valor no aparece en la lista, sus filas se descartan sin más. La lista también es un filtro.
Paso 4: Tratar los nulos
Donde no había datos (Centro en enero, por ejemplo), la celda queda a null. En los informes casi siempre se quiere un cero.
pivot_limpo = pivot_rapido.fillna(0, subset=regioes)
pivot_limpo.show()
Paso 5: Varias agregaciones a la vez
Puedes pasar más de una agregación a agg. Spark crea una columna por cada combinación de valor y agregación, con el nombre en formato valor_agregacion.
pivot_multi = (
vendas
.groupBy("mes")
.pivot("regiao", regioes)
.agg(
F.sum("valor").alias("total"),
F.count("valor").alias("n")
)
)
pivot_multi.printSchema()
# mes, Norte_total, Norte_n, Centro_total, Centro_n, Sul_total, Sul_n
Paso 6: El error más común — demasiados valores distintos
Si haces pivot sobre una columna con muchos valores distintos (un ID de cliente, por ejemplo), Spark devuelve un error avisando de que la columna de pivot tiene más valores distintos que el límite permitido — controlado por la configuración spark.sql.pivotMaxValues, que por defecto es 10000. Es una protección útil: cada valor distinto se convierte en una columna, y una tabla con miles de columnas rara vez es lo que se busca.
En lugar de subir el límite, reduce los valores antes del pivot: quédate con el top N y agrupa el resto en "Outros".
top = [r["regiao"] for r in
vendas.groupBy("regiao")
.agg(F.sum("valor").alias("t"))
.orderBy(F.desc("t"))
.limit(2)
.collect()]
vendas_top = vendas.withColumn(
"regiao_grp",
F.when(F.col("regiao").isin(top), F.col("regiao")).otherwise(F.lit("Outros"))
)
vendas_top.groupBy("mes").pivot("regiao_grp").agg(F.sum("valor")).show()
Paso 7: Revertir el pivot (unpivot)
El camino inverso — volver al formato largo — se hace con stack en una expresión SQL, o con el método unpivot a partir de Spark 3.4.
# Classic: works on any version
longo = pivot_limpo.selectExpr(
"mes",
"stack(3, 'Norte', Norte, 'Centro', Centro, 'Sul', Sul) as (regiao, valor)"
)
# Spark 3.4 or later
longo = pivot_limpo.unpivot(
ids=["mes"],
values=regioes,
variableColumnName="regiao",
valueColumnName="valor"
)
longo.show()
Verificar el resultado
Dos comprobaciones rápidas dicen si el pivot ha quedado bien. Primero, el esquema debe tener exactamente una columna por región:
pivot_limpo.printSchema()
Segundo, y más importante: el total no puede cambiar. La suma de todas las celdas del resultado tiene que coincidir con la suma de la columna original.
total_original = vendas.select(F.sum("valor").alias("total"))
total_pivot = pivot_limpo.select(
F.sum(F.col("Norte") + F.col("Centro") + F.col("Sul")).alias("total")
)
total_original.show()
total_pivot.show() # 6700.0
Si los totales no coinciden, lo más probable es que falte un valor en la lista del pivot — recuerda que esa lista también filtra.
Conclusión
Con groupBy, pivot y agg ya puedes pasar cualquier tabla larga al formato ancho que piden los informes, tratar los nulos y volver atrás cuando haga falta. El siguiente paso natural es guardar el resultado como tabla Delta y conectarlo a Power BI. Un consejo: si el pivot va lento, empieza siempre por pasar la lista de valores del Paso 3 — es la optimización con mejor retorno por menos esfuerzo. Y en tu tabla, ¿el pivot pertenece de verdad a Spark, o encajaría mejor en la capa de visualización?