Come aggiungere una colonna costante in Spark DataFrame?


137

Voglio aggiungere una colonna in a DataFramecon un valore arbitrario (che è lo stesso per ogni riga). Viene visualizzato un errore quando utilizzo withColumncome segue:

dt.withColumn('new_column', 10).head(5)
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-50-a6d0257ca2be> in <module>()
      1 dt = (messages
      2     .select(messages.fromuserid, messages.messagetype, floor(messages.datetime/(1000*60*5)).alias("dt")))
----> 3 dt.withColumn('new_column', 10).head(5)

/Users/evanzamir/spark-1.4.1/python/pyspark/sql/dataframe.pyc in withColumn(self, colName, col)
   1166         [Row(age=2, name=u'Alice', age2=4), Row(age=5, name=u'Bob', age2=7)]
   1167         """
-> 1168         return self.select('*', col.alias(colName))
   1169 
   1170     @ignore_unicode_prefix

AttributeError: 'int' object has no attribute 'alias'

Sembra che io possa indurre la funzione a funzionare come voglio aggiungendo e sottraendo una delle altre colonne (in modo che si aggiungano a zero) e quindi aggiungendo il numero che voglio (10 in questo caso):

dt.withColumn('new_column', dt.messagetype - dt.messagetype + 10).head(5)
[Row(fromuserid=425, messagetype=1, dt=4809600.0, new_column=10),
 Row(fromuserid=47019141, messagetype=1, dt=4809600.0, new_column=10),
 Row(fromuserid=49746356, messagetype=1, dt=4809600.0, new_column=10),
 Row(fromuserid=93506471, messagetype=1, dt=4809600.0, new_column=10),
 Row(fromuserid=80488242, messagetype=1, dt=4809600.0, new_column=10)]

Questo è estremamente caotico, giusto? Presumo che ci sia un modo più legittimo per farlo?

Risposte:


221

Spark 2.2+

Spark 2.2 introduce il typedLitsupporto Seq, Mape Tuples( SPARK-19254 ) e dovrebbero essere supportate le seguenti chiamate (Scala):

import org.apache.spark.sql.functions.typedLit

df.withColumn("some_array", typedLit(Seq(1, 2, 3)))
df.withColumn("some_struct", typedLit(("foo", 1, 0.3)))
df.withColumn("some_map", typedLit(Map("key1" -> 1, "key2" -> 2)))

Spark 1.3+ ( lit), 1.4+ ( array, struct), 2.0+ ( map):

Il secondo argomento per DataFrame.withColumndovrebbe essere un Columnquindi devi usare un valore letterale:

from pyspark.sql.functions import lit

df.withColumn('new_column', lit(10))

Se hai bisogno di colonne complesse puoi costruirle usando blocchi come array:

from pyspark.sql.functions import array, create_map, struct

df.withColumn("some_array", array(lit(1), lit(2), lit(3)))
df.withColumn("some_struct", struct(lit("foo"), lit(1), lit(.3)))
df.withColumn("some_map", create_map(lit("key1"), lit(1), lit("key2"), lit(2)))

Esattamente gli stessi metodi possono essere usati in Scala.

import org.apache.spark.sql.functions.{array, lit, map, struct}

df.withColumn("new_column", lit(10))
df.withColumn("map", map(lit("key1"), lit(1), lit("key2"), lit(2)))

Per fornire nomi da structsutilizzare aliassu ciascun campo:

df.withColumn(
    "some_struct",
    struct(lit("foo").alias("x"), lit(1).alias("y"), lit(0.3).alias("z"))
 )

o castsull'intero oggetto

df.withColumn(
    "some_struct", 
    struct(lit("foo"), lit(1), lit(0.3)).cast("struct<x: string, y: integer, z: double>")
 )

È anche possibile, anche se più lento, utilizzare un UDF.

Nota :

Gli stessi costrutti possono essere utilizzati per passare argomenti costanti a UDF o funzioni SQL.


1
Per altri che usano questo per implementare ... il metodo withColumn restituisce un nuovo DataFrame aggiungendo una colonna o sostituendo la colonna esistente che ha lo stesso nome, quindi dovrai riassegnare i risultati a df o assegnarli a una nuova variabile. Ad esempio, `df = df.withColumn ('new_column', lit (10)) '
Anche Mien,

con ogni iterazione, possiamo cambiare i valori all'interno della colonna? l'ho già provato for i in range(len(item)) : df.withColumn('new_column', lit({}).format(i)) ma questo non funziona
Tracy

30

In spark 2.2 ci sono due modi per aggiungere valore costante in una colonna in DataFrame:

1) Utilizzo lit

2) Utilizzo typedLit.

La differenza tra i due è che typedLitpuò anche gestire tipi di scala parametrizzati, ad esempio List, Seq e Map

DataFrame di esempio:

val df = spark.createDataFrame(Seq((0,"a"),(1,"b"),(2,"c"))).toDF("id", "col1")

+---+----+
| id|col1|
+---+----+
|  0|   a|
|  1|   b|
+---+----+

1) Utilizzo lit: aggiunta di un valore stringa costante nella nuova colonna denominata newcol:

import org.apache.spark.sql.functions.lit
val newdf = df.withColumn("newcol",lit("myval"))

Risultato:

+---+----+------+
| id|col1|newcol|
+---+----+------+
|  0|   a| myval|
|  1|   b| myval|
+---+----+------+

2) Utilizzando typedLit:

import org.apache.spark.sql.functions.typedLit
df.withColumn("newcol", typedLit(("sample", 10, .044)))

Risultato:

+---+----+-----------------+
| id|col1|           newcol|
+---+----+-----------------+
|  0|   a|[sample,10,0.044]|
|  1|   b|[sample,10,0.044]|
|  2|   c|[sample,10,0.044]|
+---+----+-----------------+

Potresti condividere la versione completa insieme alla dichiarazione di importazione
Ayush Vatsyayan il

spark versione 2.2.1. l'istruzione import viene da pyspark.sql.functions import typedLit. Ho anche provato quello condiviso da te sopra.
braj,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.