Bagaimana mengkonversi objek rdd ke dataframe di spark

139

Bagaimana saya bisa mengubah RDD ( org.apache.spark.rdd.RDD[org.apache.spark.sql.Row]) ke Dataframe org.apache.spark.sql.DataFrame. Saya mengkonversi dataframe menjadi rdd menggunakan .rdd. Setelah mengolahnya, saya ingin kembali dalam bingkai data. Bagaimana saya bisa melakukan ini?

pengguna568109
sumber
cara untuk mencapai ini di Spark 2.x
mrsrinivas

Jawaban:

88

SqlContextmemiliki sejumlah createDataFramemetode yang membuat DataFramesuatu RDD. Saya membayangkan salah satunya akan bekerja untuk konteks Anda.

Sebagai contoh:

def createDataFrame(rowRDD: RDD[Row], schema: StructType): DataFrame

Membuat DataFrame dari RDD yang berisi Baris menggunakan skema yang diberikan.

Pola Dasar Paul
sumber
93

Kode ini berfungsi sempurna dari Spark 2.x dengan Scala 2.11

Impor kelas yang diperlukan

import org.apache.spark.sql.{Row, SparkSession}
import org.apache.spark.sql.types.{DoubleType, StringType, StructField, StructType}

Buat SparkSessionObyek, dan Ini diaspark

val spark: SparkSession = SparkSession.builder.master("local").getOrCreate
val sc = spark.sparkContext // Just used to create test RDDs

Mari kita RDDmembuatnyaDataFrame

val rdd = sc.parallelize(
  Seq(
    ("first", Array(2.0, 1.0, 2.1, 5.4)),
    ("test", Array(1.5, 0.5, 0.9, 3.7)),
    ("choose", Array(8.0, 2.9, 9.1, 2.5))
  )
)

Metode 1

Menggunakan SparkSession.createDataFrame(RDD obj).

val dfWithoutSchema = spark.createDataFrame(rdd)

dfWithoutSchema.show()
+------+--------------------+
|    _1|                  _2|
+------+--------------------+
| first|[2.0, 1.0, 2.1, 5.4]|
|  test|[1.5, 0.5, 0.9, 3.7]|
|choose|[8.0, 2.9, 9.1, 2.5]|
+------+--------------------+

Metode 2

Menggunakan SparkSession.createDataFrame(RDD obj)dan menentukan nama kolom.

val dfWithSchema = spark.createDataFrame(rdd).toDF("id", "vals")

dfWithSchema.show()
+------+--------------------+
|    id|                vals|
+------+--------------------+
| first|[2.0, 1.0, 2.1, 5.4]|
|  test|[1.5, 0.5, 0.9, 3.7]|
|choose|[8.0, 2.9, 9.1, 2.5]|
+------+--------------------+

Metode 3 (Jawaban aktual untuk pertanyaan)

Cara ini membutuhkan input yang rddharus bertipe RDD[Row].

val rowsRdd: RDD[Row] = sc.parallelize(
  Seq(
    Row("first", 2.0, 7.0),
    Row("second", 3.5, 2.5),
    Row("third", 7.0, 5.9)
  )
)

buat skema

val schema = new StructType()
  .add(StructField("id", StringType, true))
  .add(StructField("val1", DoubleType, true))
  .add(StructField("val2", DoubleType, true))

Sekarang terapkan keduanya rowsRdddan schemakecreateDataFrame()

val df = spark.createDataFrame(rowsRdd, schema)

df.show()
+------+----+----+
|    id|val1|val2|
+------+----+----+
| first| 2.0| 7.0|
|second| 3.5| 2.5|
| third| 7.0| 5.9|
+------+----+----+
mrsrinivas
sumber
2
Terima kasih telah menunjukkan berbagai cara menggunakan createDataFrame dengan cara yang dapat dimengerti
vatsug
metode ketiga sangat membantu pada batu bata data karena yang lain tidak bekerja dan memberikan kesalahan
Narendra Maru
67

Dengan asumsi RDD Anda [baris] disebut rdd, Anda dapat menggunakan:

val sqlContext = new SQLContext(sc) 
import sqlContext.implicits._
rdd.toDF()
dtjones
sumber
26
Saya pikir itu tidak berhasil untuk RDD [Row]. Apakah saya kehilangan sesuatu?
Daniel de Paula
4
Karena Spark 2.0 SQLContext digantikan oleh SparkSession, tetapi kelas disimpan dalam basis kode untuk kompatibilitas mundur (scaladoc). Menggunakannya melempar peringatan penghentian.
tomaskazemekas
18

Catatan: Jawaban ini awalnya diposting di sini

Saya memposting jawaban ini karena saya ingin membagikan detail tambahan tentang opsi yang tersedia yang tidak saya temukan di jawaban lain


Untuk membuat DataFrame dari RDD of Rows, ada dua opsi utama:

1) Seperti yang sudah ditunjukkan, Anda dapat menggunakan toDF()yang dapat diimpor oleh import sqlContext.implicits._. Namun, pendekatan ini hanya berfungsi untuk jenis RDD berikut:

  • RDD[Int]
  • RDD[Long]
  • RDD[String]
  • RDD[T <: scala.Product]

(sumber: Scaladoc dari SQLContext.implicitsobjek)

Tanda tangan terakhir sebenarnya berarti bahwa ia dapat bekerja untuk RDD tupel atau RDD kelas kasus (karena tupel dan kelas kasus adalah subclass dari scala.Product).

Jadi, untuk menggunakan pendekatan ini untuk RDD[Row], Anda harus memetakannya ke RDD[T <: scala.Product]. Ini dapat dilakukan dengan memetakan setiap baris ke kelas kasus khusus atau ke tupel, seperti dalam cuplikan kode berikut:

val df = rdd.map({ 
  case Row(val1: String, ..., valN: Long) => (val1, ..., valN)
}).toDF("col1_name", ..., "colN_name")

atau

case class MyClass(val1: String, ..., valN: Long = 0L)
val df = rdd.map({ 
  case Row(val1: String, ..., valN: Long) => MyClass(val1, ..., valN)
}).toDF("col1_name", ..., "colN_name")

Kelemahan utama dari pendekatan ini (menurut saya) adalah bahwa Anda harus secara eksplisit mengatur skema DataFrame yang dihasilkan dalam fungsi peta, kolom demi kolom. Mungkin ini bisa dilakukan secara terprogram jika Anda tidak tahu skema sebelumnya, tetapi keadaan bisa sedikit berantakan di sana. Jadi, sebagai alternatif, ada opsi lain:


2) Anda dapat menggunakan createDataFrame(rowRDD: RDD[Row], schema: StructType)seperti pada jawaban yang diterima, yang tersedia di objek SQLContext . Contoh untuk mengonversi RDD dari DataFrame lama:

val rdd = oldDF.rdd
val newDF = oldDF.sqlContext.createDataFrame(rdd, oldDF.schema)

Perhatikan bahwa tidak perlu mengatur kolom skema secara eksplisit. Kami menggunakan kembali skema DF lama, yang StructTypeberkelas dan dapat dengan mudah diperpanjang. Namun, pendekatan ini kadang-kadang tidak mungkin, dan dalam beberapa kasus bisa jadi kurang efisien daripada yang pertama.

Daniel de Paula
sumber
Terima kasih atas detailnyaimport sqlContext.implicits.
javadba
Di masa depan, tolong jangan posting jawaban yang identik untuk beberapa pertanyaan. Jika pertanyaannya adalah duplikat, poskan satu jawaban yang baik, lalu pilih atau panji untuk menutup pertanyaan lainnya sebagai duplikat. Jika pertanyaan itu bukan duplikat, sesuaikan jawaban Anda untuk pertanyaan itu. Lihat Bagaimana cara menulis jawaban yang baik? .
15

Misalkan Anda memiliki DataFramedan ingin melakukan beberapa modifikasi pada bidang data dengan mengubahnya menjadi RDD[Row].

val aRdd = aDF.map(x=>Row(x.getAs[Long]("id"),x.getAs[List[String]]("role").head))

Untuk mengkonversi kembali ke DataFramedari RDDkita perlu mendefinisikan tipe struktur dari RDD.

Jika tipe data itu Long maka akan menjadi seperti LongTypedalam struktur.

Jika Stringkemudian StringTypedalam struktur.

val aStruct = new StructType(Array(StructField("id",LongType,nullable = true),StructField("role",StringType,nullable = true)))

Sekarang Anda dapat mengubah RDD ke DataFrame menggunakan metode createDataFrame .

val aNamedDF = sqlContext.createDataFrame(aRdd,aStruct)
Ajay Gupta
sumber
7

Berikut adalah contoh sederhana dari mengubah Daftar Anda menjadi Spark RDD dan kemudian mengubah RDD Spark itu menjadi Dataframe.

Harap dicatat bahwa saya telah menggunakan scala REPL Spark-shell untuk mengeksekusi kode berikut, Here sc adalah instance dari SparkContext yang secara implisit tersedia di Spark-shell. Semoga ini menjawab pertanyaan Anda.

scala> val numList = List(1,2,3,4,5)
numList: List[Int] = List(1, 2, 3, 4, 5)

scala> val numRDD = sc.parallelize(numList)
numRDD: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[80] at parallelize at <console>:28

scala> val numDF = numRDD.toDF
numDF: org.apache.spark.sql.DataFrame = [_1: int]

scala> numDF.show
+---+
| _1|
+---+
|  1|
|  2|
|  3|
|  4|
|  5|
+---+
Rashmit Rathod
sumber
Fakta yang menyenangkan: ini berhenti berfungsi, ketika Daftar Anda adalah Double, bukan int (atau Long, String, <: Product).
Rick Moritz
Tidak menjawab OP: yang berbicara tentang RDD [Row]
javadba
6

Metode 1: (Scala)

val sqlContext = new org.apache.spark.sql.SQLContext(sc)
import sqlContext.implicits._
val df_2 = sc.parallelize(Seq((1L, 3.0, "a"), (2L, -1.0, "b"), (3L, 0.0, "c"))).toDF("x", "y", "z")

Metode 2: (Scala)

case class temp(val1: String,val3 : Double) 

val rdd = sc.parallelize(Seq(
  Row("foo",  0.5), Row("bar",  0.0)
))
val rows = rdd.map({case Row(val1:String,val3:Double) => temp(val1,val3)}).toDF()
rows.show()

Metode 1: (Python)

from pyspark.sql import Row
l = [('Alice',2)]
Person = Row('name','age')
rdd = sc.parallelize(l)
person = rdd.map(lambda r:Person(*r))
df2 = sqlContext.createDataFrame(person)
df2.show()

Metode 2: (Python)

from pyspark.sql.types import * 
l = [('Alice',2)]
rdd = sc.parallelize(l)
schema =  StructType([StructField ("name" , StringType(), True) , 
StructField("age" , IntegerType(), True)]) 
df3 = sqlContext.createDataFrame(rdd, schema) 
df3.show()

Mengekstraksi nilai dari objek baris dan kemudian menerapkan kelas kasus untuk mengkonversi rdd ke DF

val temp1 = attrib1.map{case Row ( key: Int ) => s"$key" }
val temp2 = attrib2.map{case Row ( key: Int) => s"$key" }

case class RLT (id: String, attrib_1 : String, attrib_2 : String)
import hiveContext.implicits._

val df = result.map{ s => RLT(s(0),s(1),s(2)) }.toDF
Aravind Krishnakumar
sumber
4

Pada versi percikan yang lebih baru (2.0+)

import org.apache.spark.sql.SparkSession
import org.apache.spark.sql.functions._
import org.apache.spark.sql._
import org.apache.spark.sql.types._

val spark = SparkSession
  .builder()
  .getOrCreate()
import spark.implicits._

val dfSchema = Seq("col1", "col2", "col3")
rdd.toDF(dfSchema: _*)
ozzieisaacs
sumber
1
sparkSession hanyalah pembungkus untuk sqlContext, hiveContext
Archit
1
One needs to create a schema, and attach it to the Rdd.

Dengan asumsi val spark adalah produk dari SparkSession.builder ...

    import org.apache.spark._
    import org.apache.spark.sql._       
    import org.apache.spark.sql.types._

    /* Lets gin up some sample data:
     * As RDD's and dataframes can have columns of differing types, lets make our
     * sample data a three wide, two tall, rectangle of mixed types.
     * A column of Strings, a column of Longs, and a column of Doubules 
     */
    val arrayOfArrayOfAnys = Array.ofDim[Any](2,3)
    arrayOfArrayOfAnys(0)(0)="aString"
    arrayOfArrayOfAnys(0)(1)=0L
    arrayOfArrayOfAnys(0)(2)=3.14159
    arrayOfArrayOfAnys(1)(0)="bString"
    arrayOfArrayOfAnys(1)(1)=9876543210L
    arrayOfArrayOfAnys(1)(2)=2.71828

    /* The way to convert an anything which looks rectangular, 
     * (Array[Array[String]] or Array[Array[Any]] or Array[Row], ... ) into an RDD is to 
     * throw it into sparkContext.parallelize.
     * http://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.SparkContext shows
     * the parallelize definition as 
     *     def parallelize[T](seq: Seq[T], numSlices: Int = defaultParallelism)
     * so in our case our ArrayOfArrayOfAnys is treated as a sequence of ArraysOfAnys.
     * Will leave the numSlices as the defaultParallelism, as I have no particular cause to change it. 
     */
    val rddOfArrayOfArrayOfAnys=spark.sparkContext.parallelize(arrayOfArrayOfAnys)

    /* We'll be using the sqlContext.createDataFrame to add a schema our RDD.
     * The RDD which goes into createDataFrame is an RDD[Row] which is not what we happen to have.
     * To convert anything one tall and several wide into a Row, one can use Row.fromSeq(thatThing.toSeq)
     * As we have an RDD[somethingWeDontWant], we can map each of the RDD rows into the desired Row type. 
     */     
    val rddOfRows=rddOfArrayOfArrayOfAnys.map(f=>
        Row.fromSeq(f.toSeq)
    )

    /* Now to construct our schema. This needs to be a StructType of 1 StructField per column in our dataframe.
     * https://spark.apache.org/docs/latest/api/scala/index.html#org.apache.spark.sql.types.StructField shows the definition as
     *   case class StructField(name: String, dataType: DataType, nullable: Boolean = true, metadata: Metadata = Metadata.empty)
     * Will leave the two default values in place for each of the columns:
     *        nullability as true, 
     *        metadata as an empty Map[String,Any]
     *   
     */

    val schema = StructType(
        StructField("colOfStrings", StringType) ::
        StructField("colOfLongs"  , LongType  ) ::
        StructField("colOfDoubles", DoubleType) ::
        Nil
    )

    val df=spark.sqlContext.createDataFrame(rddOfRows,schema)
    /*
     *      +------------+----------+------------+
     *      |colOfStrings|colOfLongs|colOfDoubles|
     *      +------------+----------+------------+
     *      |     aString|         0|     3.14159|
     *      |     bString|9876543210|     2.71828|
     *      +------------+----------+------------+
    */ 
    df.show 

Langkah yang sama, tetapi dengan deklarasi val lebih sedikit:

    val arrayOfArrayOfAnys=Array(
        Array("aString",0L         ,3.14159),
        Array("bString",9876543210L,2.71828)
    )

    val rddOfRows=spark.sparkContext.parallelize(arrayOfArrayOfAnys).map(f=>Row.fromSeq(f.toSeq))

    /* If one knows the datatypes, for instance from JDBC queries as to RDBC column metadata:
     * Consider constructing the schema from an Array[StructField].  This would allow looping over 
     * the columns, with a match statement applying the appropriate sql datatypes as the second
     *  StructField arguments.   
     */
    val sf=new Array[StructField](3)
    sf(0)=StructField("colOfStrings",StringType)
    sf(1)=StructField("colOfLongs"  ,LongType  )
    sf(2)=StructField("colOfDoubles",DoubleType)        
    val df=spark.sqlContext.createDataFrame(rddOfRows,StructType(sf.toList))
    df.show
penguji
sumber
1

Saya mencoba menjelaskan solusinya menggunakan masalah jumlah kata . 1. Baca file menggunakan sc

  1. Menghasilkan jumlah kata
  2. Metode untuk membuat DF

    • metode rdd.toDF
    • rdd.toDF ("word", "count")
      • spark.createDataFrame (rdd, schema)

    Baca file menggunakan spark

    val rdd=sc.textFile("D://cca175/data/")  

    Rdd ke Dataframe

    val df = sc.textFile ("D: // cca175 / data /") .toDF ("t1") df.show

    Metode 1

    Buat jumlah kata RDD ke Dataframe

    val df=rdd.flatMap(x=>x.split(" ")).map(x=>(x,1)).reduceByKey((x,y)=>(x+y)).toDF("word","count")

    Metode2

    Buat Dataframe dari Rdd

    val df=spark.createDataFrame(wordRdd) 
    # with header   
    val df=spark.createDataFrame(wordRdd).toDF("word","count")  df.show

    Metode3

    Tentukan Skema

    import org.apache.spark.sql.types._

    skema val = StructType baru (). tambahkan (StructField ("word", StringType, true)). add (StructField ("count", StringType, true))

    Buat RowRDD

    import org.apache.spark.sql.Row
    val rowRdd=wordRdd.map(x=>(Row(x._1,x._2)))     

    Buat DataFrame dari RDD dengan skema

    val df = spark.createDataFrame (rowRdd, schema)
    df.show

Priyanshu Singh
sumber
0

Untuk mengonversi Array [Baris] ke DataFrame atau Dataset, yang berikut ini berfungsi dengan elegan:

Katakanlah, skema adalah StructType untuk baris, lalu

val rows: Array[Row]=...
implicit val encoder = RowEncoder.apply(schema)
import spark.implicits._
rows.toDS
Tom
sumber