Vai al contenuto principale

Padroneggia withColumn() di PySpark per trasformazioni delle colonne dei DataFrame

Impara a usare efficacemente withColumn() in PySpark per aggiungere, aggiornare e trasformare le colonne dei DataFrame con sicurezza. Copre sintassi, prestazioni e best practice.
· 9 min leggi

Scopri con l'IA

I DataFrame di PySpark sono centrali quando costruisci pipeline scalabili in Spark. Una cosa importante da ricordare è che i DataFrame sono immutabili. Questo significa che, una volta creati, non puoi modificarli direttamente: ogni cambiamento restituisce sempre un nuovo DataFrame. È qui che entra in gioco withColumn() di PySpark. Ti aiuta ad aggiungere, aggiornare o modificare colonne ma, dato che i DataFrame non cambiano in place, devi sempre riassegnare il risultato a una nuova variabile.

In questo tutorial ti mostrerò come usare withColumn() per modellare e rifinire i tuoi dataset, che tu stia creando feature, ripulendo i tipi o aggiungendo logica. 

Che cos'è withColumn() in PySpark?

In poche parole, withColumn() restituisce un nuovo DataFrame con una colonna aggiunta o sostituita. Poiché i DataFrame non mutano, devi assegnare quel valore di ritorno, df = df.withColumn(...).

Sotto il cofano, ogni chiamata a withColumn() aggiunge una proiezione nel piano logico. Va bene se ne fai una o due, ma concatenarne molte può gonfiare il piano, rendendo Spark sensibilmente più lento.

Nuovo in PySpark? Puoi padroneggiare le basi per gestire il big data con facilità, imparando a processare, interrogare e ottimizzare dataset massivi per analisi potenti nel nostro corso Introduction to PySpark. 

Utilizzi principali di withColumn() in PySpark

Vediamo i modi principali in cui userai withColumn():

Aggiungere una colonna costante

Mettiamo che tu voglia aggiungere un timestamp o un flag. Usa lit() o typedLit() da pyspark.sql.functions. Per esempio:

from pyspark.sql.functions import lit
df = df.withColumn("ingest_date", lit("2025-07-29"))

Creare una colonna a partire da dati esistenti

Magari vuoi un valore derivato, unire stringhe o calcolare un totale. Puoi fare così:

from pyspark.sql.functions import col, expr
df = df.withColumn("full_name", col("first_name") + expr(" ' ' + last_name"))

Anche le trasformazioni aritmetiche o basate su espressioni rientrano qui.

Sovrascrivere una colonna esistente

Se hai già una colonna e vuoi cambiarla, withColumn() la sostituisce semplicemente:

df = df.withColumn("age", col("age").cast("integer"))

Nessun bisogno di dropparla e riaggiungerla.

Eseguire cast dei tipi

Cambiare il tipo di una colonna è immediato:

df = df.withColumn("price", col("price").cast("decimal(10,2)"))

Trovo che sia particolarmente utile quando leggi JSON o CSV "molli" in cui i tipi arrivano come stringhe.

Se cerchi altri esempi su cos'è PySpark e come usarlo, ti consiglio il nostro tutorial Getting Started with PySpark. 

Logica condizionale ed espressioni when()

A volte serve fare più che semplice aritmetica. Magari stai costruendo una colonna di stato basata su un punteggio. O stai etichettando record in base a un mix di regole. Qui entra in gioco when() da pyspark.sql.functions. Pensalo come un IF. Puoi abbinarlo a otherwise() per coprire più casi.

Ecco come appare:

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")
)

Si legge quasi come l'inglese: “Se score è almeno 90, allora A. Altrimenti se è 80, allora B. E così via… e se nessuno di questi coincide, assegna F.” È espressivo e Spark trasforma quella logica in un'espressione efficiente dietro le quinte. Niente loop annidati, niente chiamate a apply(), solo DAG puliti e piani di esecuzione chiari.

Torna utile quando vuoi evitare di passare a SQL o di appesantire il codice con UDF.

Puoi imparare a manipolare i dati e creare set di feature di machine learning in Spark usando SQL in Python dal nostro tutorial Introduction to Spark SQL in Python. 

Trasformare colonne con funzioni built-in e UDF

Spesso dovrai formattare o ristrutturare colonne, convertire stringhe in maiuscolo, dividerle in parti, concatenare valori e così via. PySpark ha una ricca libreria di funzioni integrate che lavorano direttamente dentro withColumn().

Ecco un esempio:

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))

Le built-in sono ottime: veloci, native e ottimizzate. Ma a volte hai una regola estemporanea che non rientra in nessuna casistica. È qui che entrano in gioco le funzioni definite dall'utente (UDF).

Usare una UDF

Poniamo che tu voglia calcolare la lunghezza di una stringa ed etichettarla:

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")))

Semplice. Ma fai attenzione: le UDF hanno un overhead. Estraggono i dati dal motore ottimizzato, li eseguono in Python e poi li reimpacchettano. Va bene quando serve, ma per compiti ad alto volume, preferisci le built-in o le espressioni SQL se possibile.

Impara a creare, ottimizzare e usare le UDF PySpark, incluse le Pandas UDF, per gestire trasformazioni personalizzate in modo efficiente e migliorare le prestazioni di Spark nel nostro tutorial How to Use PySpark UDFs and Pandas UDFs Effectively. 

Considerazioni sulle prestazioni e pratiche avanzate

Prima o poi, ogni utente PySpark ci sbatte contro: continui ad accumulare chiamate a withColumn() e la tua pipeline rallenta fino quasi a fermarsi. Il motivo? Ogni chiamata aggiunge un nuovo livello al piano logico, che Spark deve analizzare, ottimizzare ed eseguire.

Se stai aggiungendo solo una o due colonne, va bene. Ma se ne concateni cinque, sei o più, dovresti iniziare a pensarla diversamente.

Usa select() quando aggiungi molte colonne

Invece di chiamare withColumn() ripetutamente, costruisci un nuovo elenco di colonne usando select():

df = df.select(
    "*",
    (col("salary") * 0.1).alias("bonus"),
    (col("age") + 5).alias("age_plus_five")
)

Questo approccio costruisce il piano logico in un colpo solo.

Scopri di più su select() e altri metodi nel nostro tutorial PySpark Cheat Sheet: Spark in Python.  

E withColumns()?

Introdotto in Spark 3.3, withColumns() ti permette di aggiungere più colonne in un solo passaggio. Niente più chiamate ripetute. È un metodo in stile dizionario:

df = df.withColumns({
    "bonus": col("salary") * 0.1,
    "age_plus_five": col("age") + 5
})

Non tutti sono già passati a Spark 3.3+ ma, se lo sei, usalo. È più pulito, più veloce e evita il problema della “morte per concatenazione”.

Esempio completo di withColumn() in PySpark

Poniamo che tu stia lavorando su dati di attività utenti per una piattaforma in abbonamento. Il tuo DataFrame grezzo assomiglia a questo:

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)

Hai nomi, stati, età e quanto hanno speso. Abbastanza basilare. Ma nel mondo reale non basta mai. Quindi ecco cosa faremo dopo:

  • Aggiungere una colonna costante per la data di ingestione.
  • Creare una nuova colonna che indichi se l'utente è maggiorenne.
  • Formattare purchase_amount a due decimali.
  • Categorizzare gli utenti per livello di spesa.
  • Applicare una funzione personalizzata per etichettare gli utenti.
  • Usare withColumns() per aggiungere valori extra in modo più pulito.

Ti stai preparando per il prossimo colloquio? L'articolo Top 36 PySpark Interview Questions and Answers for 2025 offre una guida completa alle domande e risposte su PySpark, dai concetti fondamentali alle tecniche avanzate e alle strategie di ottimizzazione.

Passo 1: Aggiungi una data di ingestione costante

È buona pratica tracciare quando i dati entrano nel tuo sistema.

from pyspark.sql.functions import lit
df = df.withColumn("ingest_date", lit("2025-07-29"))

Passo 2: Segnala maggiorenni vs. minorenni

Avresti potuto usare semplicemente col("age") >= 18, ma incapsularlo con when() ti dà pieno controllo se la logica diventa più complicata.

from pyspark.sql.functions import when, col
df = df.withColumn(
    "is_adult",
    when(col("age") >= 18, True).otherwise(False)
)

Passo 3: Formattta purchase_amount

Il casting dei tipi è uno dei lavori di pulizia più frequenti che farai, soprattutto quando leggi da CSV o JSON.

df = df.withColumn("purchase_amount", col("purchase_amount").cast("decimal(10,2)"))

Passo 4: Categorizza il livello di spesa

Diciamo che vuoi tre gruppi: “none”, “low” e “high”.

df = df.withColumn(
    "spend_category",
    when(col("purchase_amount") == 0, "none")
    .when(col("purchase_amount") < 100, "low")
    .otherwise("high")
)

Questo ti aiuta a segmentare gli utenti senza eseguire una query separata.

Passo 5: Etichetta gli utenti usando una UDF

Ora una regola inventata. Diciamo che etichetti una persona in base alla lunghezza del nome.

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")))

Passo 6: Aggiungi più colonne extra in un colpo solo

Magari ne vuoi ancora un paio, età in mesi e un messaggio di benvenuto.

df = df.withColumns({
    "age_in_months": col("age") * 12,
    "welcome_msg": col("name") + lit(", welcome aboard!")
})

Molto più pulito che chiamare withColumn() due volte.

Ecco come appare il DataFrame finale quando lo mostri:

df.show(truncate=False)

name

state

age

purchase_amount

ingest_date

is_adult

spend_category

label

age_in_months

welcome_msg

Alice

NY

24

129.99

2025-07-29

True

high

complex

288

Alice, welcome aboard!

Bob

CA

31

199.95

2025-07-29

True

high

simple

372

Bob, welcome aboard!

Charlie

TX

45

0.00

2025-07-29

True

none

complex

540

Charlie, welcome aboard!

Diana

WA

17

19.99

2025-07-29

False

low

complex

204

Diana, welcome aboard!

Questo tipo di pila di trasformazioni è comune nel feature engineering, nel reporting o nella pulizia di feed di terze parti.

Impara le basi per lavorare con i big data con PySpark dal nostro corso Big Data Fundamentals with PySpark. 

Best practice e insidie di withColumn()

withColumn() di PySpark può sembrare semplice, ma può mettere in difficoltà anche ingegneri esperti se non fai attenzione. Ecco il genere di cose che può rovinare silenziosamente la tua pipeline, insieme ad alcune abitudini che ti salvano da brutte sorprese.

Riassegna sempre il risultato

Questa è basilare, ma ancora sorprende: withColumn() non modifica il DataFrame originale. Te ne restituisce uno nuovo. Se dimentichi di riassegnarlo, la tua modifica sparisce.

df.withColumn("new_col", lit(1))  # This won't do anything
df = df.withColumn("new_col", lit(1))  # This works

Attenzione alle sovrascritture accidentali

Mettiamo che il tuo DataFrame abbia una colonna chiamata status. E lanci questo:

df = df.withColumn("Status", lit("Active"))

Sembra innocuo, giusto? Ma Spark tratta i nomi delle colonne come case-insensitive per impostazione predefinita. Questo significa che hai appena sovrascritto la tua colonna status originale. Senza accorgertene.

Una soluzione è controllare sempre df.columns prima e dopo. Oppure, se la tua pipeline lo supporta, attiva la sensibilità al maiuscolo/minuscolo usando:

spark.conf.set("spark.sql.caseSensitive", "true")

Non usare letterali Python nelle espressioni

Questa è facile da dimenticare. Quando aggiungi costanti, evita valori Python grezzi. Incapsulali sempre con lit().

df = df.withColumn("region", "US")  # Bad
df = df.withColumn("region", lit("US"))  # Good

Perché? Perché withColumn() si aspetta una espressione Column, non un valore grezzo. Se sgarri, Spark potrebbe lanciare un errore poco informativo, o peggio, rompere silenziosamente la logica a valle.

Gestisci le eccezioni al di fuori di withColumn()

A volte si fa i creativi e si incapsulano interi blocchi withColumn() dentro try/except. È meglio isolare le parti rischiose (come UDF o letture dati) e intercettare le eccezioni lì. Mantieni lo strato di trasformazione pulito e prevedibile.

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)

Lascia che Spark fallisca presto, non nasconderlo dietro try annidati.

Scopri di più sulle eccezioni in Python con il nostro tutorial Exception & Error Handling in Python. 

Preferisci le built-in alle UDF

Certo, le UDF ti danno potenza. Ma hanno contropartite: prestazioni inferiori, debug più difficile e meno ottimizzazione. Se esiste una funzione integrata che fa il lavoro, usa quella.

Questo:

df = df.withColumn("upper_name", upper(col("name")))

È molto più veloce di questo:

df = df.withColumn("upper_name", udf(lambda x: x.upper(), StringType())(col("name")))

Quando non usare withColumn()

Per quanto flessibile sia withColumn(), ci sono momenti in cui non è lo strumento giusto. 

Stai rimodellando molte colonne in una volta

Se ti ritrovi a chiamare withColumn() 10 volte di fila, è il momento di cambiare approccio. Usa select() e scrivi le trasformazioni come parte di una nuova proiezione.

df = df.select(
    col("name"),
    col("age"),
    (col("salary") * 0.15).alias("bonus"),
    (col("score") + 10).alias("adjusted_score")
)

È più chiaro, performa meglio e fa lavorare l'ottimizzatore di Spark per te, non contro di te.

Vuoi scrivere logica in stile SQL

Se il tuo team punta molto su SQL e hai già registrato il DataFrame come vista temporanea, spesso è più pulito eseguire direttamente una query SQL.

df.createOrReplaceTempView("users")
df2 = spark.sql("""
    SELECT name, age,
           CASE WHEN age >= 18 THEN true ELSE false END AS is_adult
    FROM users
""")

Questo può essere più semplice per analisti esperti di SQL o team che lavorano sia con Spark che con database tradizionali.

Costruisci le tue skill SQL con corsi, track e progetti interattivi curati da esperti reali nella nostra sezione SQL Courses. 

Stai già usando Spark 3.3+

Se usi Spark 3.3 o versioni successive e devi aggiungere più colonne, withColumns() è l'amico giusto. Non è solo comodo: può essere più veloce sotto il cofano creando un unico aggiornamento del piano logico.

Impara a implementare gestione dati distribuita e machine learning in Spark usando il pacchetto PySpark con il nostro corso Foundations of PySpark. 

Conclusione

La funzione withColumn() di PySpark è uno degli strumenti più versatili nel tuo arsenale di trasformazione dati, dandoti la possibilità di aggiungere, modificare e progettare feature direttamente in un workflow centrato sui DataFrame. Dal casting dei tipi e l'iniezione di costanti fino all'inserimento di logica complessa con condizionali e UDF, withColumn() ti aiuta a trasformare dati disordinati in pipeline pronte per la produzione.

Ma a questo potere corrisponde responsabilità. Un uso eccessivo di withColumn() in lunghe catene può degradare silenziosamente le prestazioni gonfiando il piano logico, rendendo i job Spark più difficili da ottimizzare e fare debug. Ecco perché sapere quando usare select(), withColumns() o anche SQL può fare la differenza tra un job che arranca e uno che scala.

Con Spark in continua evoluzione, specialmente con funzioni come withColumns() in Spark 3.3+, capire gli interni e i compromessi prestazionali di ciascun metodo è la chiave per scrivere codice più pulito, veloce e manutenibile.

Padroneggia le tecniche dietro le trasformazioni su larga scala delle colonne, evita le trappole dell'inflazione del piano e scopri come i professionisti snelliscono le pipeline di feature con il nostro corso Feature Engineering with PySpark.

PySpark withColumn() - Domande frequenti

Why is my Spark job slow when I use withColumn() a lot?

Quando concateni più chiamate a withColumn(), Spark aggiunge ciascuna come passaggio separato nel piano logico di esecuzione. Col tempo, questo può trasformarsi in un piano gonfio, più difficile da ottimizzare e più lento da eseguire. Invece di accatastare dieci withColumn(), prova a costruire le nuove colonne dentro un singolo select() o usa withColumns() per aggiungere più colonne in un colpo solo.

Can I use withColumn() to remove a column?

No, withColumn() aggiunge o sostituisce solo colonne, non le elimina. Se vuoi droppare una colonna, usa drop(). Puoi anche usare select() per mantenere solo le colonne che ti servono.

Why do I get an error when I try to use a string or number in withColumn()?

Di solito succede quando passi un valore Python grezzo invece di incapsularlo con lit().withColumn() si aspetta un'espressione Column di Spark. Ecco il modo giusto:

from pyspark.sql.functions import lit
df = df.withColumn("new_col", lit(42))  
Derrick Mwiti

Autore

Derrick Mwiti

Argomenti

Correlato