Come convertire l'oggetto RDD in frame di dati in Spark


139

Come posso convertire un RDD ( org.apache.spark.rdd.RDD[org.apache.spark.sql.Row]) in un Dataframe org.apache.spark.sql.DataFrame. Ho convertito un dataframe in rdd utilizzando .rdd. Dopo averlo elaborato, lo rivoglio in dataframe. Come posso fare questo ?


modo per raggiungere questo obiettivo in Spark 2.x
mrsrinivas,

Risposte:


88

SqlContextha un numero di createDataFramemetodi che creano un DataFramedato un RDD. Immagino che uno di questi funzionerà per il tuo contesto.

Per esempio:

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

Crea un DataFrame da un RDD contenente righe utilizzando lo schema indicato.


93

Questo codice funziona perfettamente da Spark 2.x con Scala 2.11

Importa le classi necessarie

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

Crea SparkSessionoggetto, ed eccolo quispark

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

Facciamo una RDDper renderloDataFrame

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

Metodo 1

Usando 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]|
+------+--------------------+

Metodo 2

Utilizzo SparkSession.createDataFrame(RDD obj)e specifica dei nomi di colonna.

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

Metodo 3 (risposta effettiva alla domanda)

In questo modo è necessario che l'input rddsia di tipo 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)
  )
)

creare lo schema

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

Ora applica sia a rowsRddche schemaacreateDataFrame()

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

2
Grazie per aver mostrato i diversi modi di usare createDataFrame in modo comprensibile
vatsug,

il terzo metodo è utile sui mattoncini di dati poiché altri non funzionano e danno un errore
Narendra Maru,

67

Supponendo che il tuo RDD [riga] si chiami rdd, puoi usare:

val sqlContext = new SQLContext(sc) 
import sqlContext.implicits._
rdd.toDF()

26
Penso che non funzioni per RDD [Row]. Mi sto perdendo qualcosa?
Daniel de Paula,

4
Poiché Spark 2.0 SQLContext è sostituito da SparkSession, ma la classe viene mantenuta nella base di codice per la compatibilità con le versioni precedenti (scaladoc). Usandolo genera un avviso di deprecazione.
Tomaskazemekas,

18

Nota: questa risposta è stata originariamente pubblicata qui

Sto pubblicando questa risposta perché vorrei condividere ulteriori dettagli sulle opzioni disponibili che non ho trovato nelle altre risposte


Per creare un DataFrame da un RDD di righe, ci sono due opzioni principali:

1) Come già sottolineato, è possibile utilizzare toDF()quali possono essere importati da import sqlContext.implicits._. Tuttavia, questo approccio funziona solo per i seguenti tipi di RDD:

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

(fonte: scaladoc del SQLContext.implicitsdell'oggetto)

L'ultima firma in realtà significa che può funzionare per un RDD di tuple o un RDD di classi case (poiché le tuple e le classi case sono sottoclassi di scala.Product).

Quindi, per usare questo approccio per un RDD[Row], devi mapparlo a un RDD[T <: scala.Product]. Questo può essere fatto mappando ogni riga su una classe case personalizzata o su una tupla, come nei seguenti frammenti di codice:

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

o

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

Lo svantaggio principale di questo approccio (secondo me) è che devi impostare esplicitamente lo schema del DataFrame risultante nella funzione mappa, colonna per colonna. Forse questo può essere fatto a livello di programmazione se non si conosce lo schema in anticipo, ma le cose possono diventare un po 'confuse lì. Quindi, in alternativa, c'è un'altra opzione:


2) È possibile utilizzare createDataFrame(rowRDD: RDD[Row], schema: StructType)come nella risposta accettata, disponibile nell'oggetto SQLContext . Esempio per la conversione di un RDD di un vecchio DataFrame:

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

Si noti che non è necessario impostare esplicitamente alcuna colonna dello schema. Riutilizziamo il vecchio schema di DF, che è di StructTypeclasse e può essere facilmente esteso. Tuttavia, questo approccio a volte non è possibile e in alcuni casi può essere meno efficiente del primo.


Grazie per il dettaglioimport sqlContext.implicits.
javadba,

In futuro, non pubblicare risposte identiche a più domande. Se le domande sono duplicate, pubblica una buona risposta, quindi vota o contrassegna per chiudere l'altra domanda come duplicato. Se la domanda non è un duplicato, personalizza le tue risposte alla domanda. Vedi Come posso scrivere una buona risposta? .

15

Supponi di avere un DataFramee desideri apportare alcune modifiche ai dati dei campi convertendoli in RDD[Row].

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

Per riconvertire da DataFrameda RDDabbiamo bisogno di definire il tipo di struttura di RDD.

Se il tipo di dati era Long allora diventerà come LongTypenella struttura.

Se Stringpoi StringTypenella struttura.

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

Ora puoi convertire RDD in DataFrame usando il metodo createDataFrame .

val aNamedDF = sqlContext.createDataFrame(aRdd,aStruct)

7

Ecco un semplice esempio di conversione del tuo Elenco in Spark RDD e di conversione di quel Spark RDD in Dataframe.

Nota che ho usato lo scala REPL di Spark-shell per eseguire il seguente codice, Here sc è un'istanza di SparkContext che è implicitamente disponibile in Spark-shell. Spero che risponda alla tua domanda.

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

Un fatto divertente: questo smette di funzionare, quando la tua lista è doppia, anziché int (o lunga, stringa, <: prodotto).
Rick Moritz,

Non risponde all'OP: che parla di RDD [Row]
javadba,

6

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

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

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

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

Estratto il valore dall'oggetto riga e quindi applicato la classe case per convertire rdd in 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

4

Nelle versioni più recenti di spark (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: _*)

1
sparkSession è solo un wrapper per sqlContext, hiveContext
Archit

1
One needs to create a schema, and attach it to the Rdd.

Supponendo che val spark sia un prodotto di 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 

Stessi passi, ma con meno dichiarazioni val:

    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

1

Ho provato a spiegare la soluzione usando il problema del conteggio delle parole . 1. Leggi il file usando sc

  1. Produce il conteggio delle parole
  2. Metodi per creare DF

    • metodo rdd.toDF
    • rdd.toDF ( "parola", "Count")
      • spark.createDataFrame (RDD, lo schema)

    Leggi il file usando spark

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

    Rdd a Dataframe

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

    Metodo 1

    Crea RDD conteggio parole in Dataframe

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

    method2

    Crea Dataframe da Rdd

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

    method3

    Definisci schema

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

    val schema = new StructType (). aggiungere (StructField ( "parola", StringType, true)). aggiungere (StructField ( "conte", StringType, true))

    Crea RowRDD

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

    Crea DataFrame da RDD con schema

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


0

Per convertire un array [Row] in DataFrame o Dataset, funziona in modo elegante:

Supponiamo che lo schema sia il StructType per la riga, quindi

val rows: Array[Row]=...
implicit val encoder = RowEncoder.apply(schema)
import spark.implicits._
rows.toDS
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.