Bagaimana cara menambahkan kolom konstan dalam Spark DataFrame?

137

Saya ingin menambahkan kolom dalam DataFramedengan nilai arbitrer (sama untuk setiap baris). Saya mendapatkan kesalahan saat menggunakan withColumnsebagai berikut:

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'

Tampaknya saya bisa mengelabui fungsi agar berfungsi seperti yang saya inginkan dengan menambahkan dan mengurangi salah satu kolom lainnya (jadi itu menambah nol) dan kemudian menambahkan angka yang saya inginkan (10 dalam kasus ini):

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

Ini sangat hacky, bukan? Saya berasumsi ada cara yang lebih sah untuk melakukan ini?

Evan Zamir
sumber

Jawaban:

221

Spark 2.2+

Spark 2.2 memperkenalkan typedLitdukungan Seq,, Mapdan Tuples( SPARK-19254 ) dan panggilan berikut harus didukung (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):

Argumen kedua untuk DataFrame.withColumnharus Columnjadi Anda harus menggunakan literal:

from pyspark.sql.functions import lit

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

Jika Anda membutuhkan kolom kompleks, Anda dapat membangun ini menggunakan blok seperti 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)))

Metode yang persis sama dapat digunakan di 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)))

Untuk memberikan nama untuk structsdigunakan aliaspada setiap bidang:

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

atau castpada seluruh objek

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

Dimungkinkan juga, meskipun lebih lambat, untuk menggunakan UDF.

Catatan :

Konstruk yang sama dapat digunakan untuk meneruskan argumen konstan ke fungsi UDF atau SQL.

nol323
sumber
1
Bagi yang lain yang menggunakan ini untuk mengimplementasikan ... metode withColumn mengembalikan DataFrame baru dengan menambahkan kolom atau mengganti kolom yang ada yang memiliki nama yang sama, jadi Anda harus menetapkan kembali hasilnya ke df atau menetapkan ke variabel baru. Misalnya, `df = df.withColumn ('new_column', lit (10)) '
Even Mien
dengan setiap iterasi, dapatkah kita mengubah nilai di dalam kolom? saya sudah mencoba ini for i in range(len(item)) : df.withColumn('new_column', lit({}).format(i)) tetapi ini tidak berhasil
Tracy
30

Di percikan 2.2 ada dua cara untuk menambahkan nilai konstan dalam kolom di DataFrame:

1) Menggunakan lit

2) Menggunakan typedLit.

Perbedaan antara keduanya adalah yang typedLitjuga dapat menangani tipe scala yang diparameterisasi misalnya Daftar, Seq, dan Peta

Contoh DataFrame:

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

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

1) Menggunakan lit: Menambahkan nilai string konstan di kolom baru bernama newcol:

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

Hasil:

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

2) Menggunakan typedLit:

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

Hasil:

+---+----+-----------------+
| id|col1|           newcol|
+---+----+-----------------+
|  0|   a|[sample,10,0.044]|
|  1|   b|[sample,10,0.044]|
|  2|   c|[sample,10,0.044]|
+---+----+-----------------+
Ayush Vatsyayan
sumber
Bisakah Anda membagikan versi lengkapnya bersama dengan pernyataan impor
Ayush Vatsyayan
percikan versi 2.2.1. pernyataan impor dari pyspark.sql.function import typedLit. Coba juga yang Anda bagikan di atas.
braj