Як перетворити RDD-об'єкт в кадр даних в іскрі


139

Як я можу перетворити RDD ( org.apache.spark.rdd.RDD[org.apache.spark.sql.Row]) у кадр даних org.apache.spark.sql.DataFrame. Я перетворив фрейм даних в rdd за допомогою .rdd. Після обробки я повертаю його назад у фрейм даних. Як я можу це зробити?


спосіб досягти цього в Spark 2.x
mrsrinivas

Відповіді:


88

SqlContextмає ряд createDataFrameметодів, які створюють DataFrameзаданий ан RDD. Я думаю, що одна з них буде працювати для вашого контексту.

Наприклад:

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

Створює DataFrame з RDD, що містить рядки, за допомогою заданої схеми.


93

Цей код ідеально працює від Spark 2.x із Scala 2.11

Імпортуйте необхідні заняття

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

Створіть SparkSessionоб’єкт, і ось цеspark

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

Давайте, RDDщоб зробити цеDataFrame

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

Спосіб 1

Використання 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]|
+------+--------------------+

Спосіб 2

Використання SparkSession.createDataFrame(RDD obj)та вказівка ​​назв стовпців.

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

Спосіб 3 (Фактична відповідь на запитання)

Цей спосіб вимагає, щоб вхід rddповинен бути типу 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)
  )
)

створити схему

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

Тепер застосувати як rowsRddі schemaдоcreateDataFrame()

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
Дякуємо, що в зрозумілому вигляді показали різні способи використання createDataFrame
vatsug

третій метод корисний для даних із цегли, оскільки інші не працюють і помиляються
Нарендра Мару

67

Якщо припустити, що ваш RDD [рядок] називається rdd, ви можете використовувати:

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

26
Я думаю, це не працює для RDD [рядок]. Я щось пропускаю?
Даніель де Паула

4
Оскільки Spark 2.0 SQLContext замінюється SparkSession, але клас зберігається в кодовій базі для зворотної сумісності (scaladoc). Використовуючи це, викидає попередження про депресію.
tomaskazemekas

18

Примітка. Ця відповідь спочатку була розміщена тут

Я публікую цю відповідь, тому що я хотів би поділитися додатковими подробицями щодо доступних варіантів, яких я не знайшов в інших відповідях


Щоб створити DataFrame з RDD рядків, є два основні варіанти:

1) Як уже зазначалося, ви можете використовувати, toDF()які можна імпортувати import sqlContext.implicits._. Однак такий підхід працює лише для таких типів RDD:

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

(Джерело: Scaladoc від SQLContext.implicitsоб'єкта)

Останній підпис насправді означає, що він може працювати для RDD кортежів або RDD класів регістрів (тому що кортежі та регістри класів є підкласами scala.Product).

Таким чином, щоб використовувати цей підхід для анкети RDD[Row], вам потрібно зіставити його на RDD[T <: scala.Product]. Це можна зробити, зіставивши кожен рядок у користувацькому класі регістру чи кортежі, як у наведених нижче фрагментах коду:

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

або

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

Основний недолік цього підходу (на мою думку) полягає в тому, що ви повинні чітко встановити схему отриманого DataFrame у функції карти, колонка за стовпцем. Можливо, це можна зробити систематично, якщо ви не знаєте схеми заздалегідь, але там справи можуть трохи заплутатися. Отже, як варіант, є інший варіант:


2) Ви можете використовувати createDataFrame(rowRDD: RDD[Row], schema: StructType)як у прийнятій відповіді, яка доступна в об'єкті SQLContext . Приклад перетворення RDD старої DataFrame:

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

Зауважте, що не потрібно явно встановлювати жоден стовпець схеми. Ми повторно використовуємо стару схему DF, яка є StructTypeкласовою і може бути легко розширена. Однак такий підхід іноді неможливий, а в деяких випадках може бути менш ефективним, ніж перший.


Дякую за деталіimport sqlContext.implicits.
javadba

Надалі не публікуйте однакових відповідей на кілька запитань. Якщо питання дублюються, опублікуйте одну добру відповідь, а потім проголосуйте або позначте, щоб закрити інше питання як дублікат. Якщо питання не є дублікатом, підготуйте свої відповіді на питання. Див. Як написати гарну відповідь? .

15

Припустимо, у вас є DataFrameі ви хочете внести деякі зміни в дані полів, перетворивши їх у RDD[Row].

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

Для того, щоб перетворити назад в DataFrameз RDDнас необхідно визначити тип структури з RDD.

Якщо тип даних був Long тоді, він стане таким, як LongTypeза структурою.

Якщо Stringтоді StringTypeв структурі.

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

Тепер ви можете конвертувати RDD в DataFrame за допомогою методу createDataFrame .

val aNamedDF = sqlContext.createDataFrame(aRdd,aStruct)

7

Ось простий приклад перетворення списку в Spark RDD, а потім перетворення цього Spark RDD у Dataframe.

Будь ласка, зауважте, що для використання наступного коду я використав скапу REPL Spark-shell для виконання наступного коду. Сподіваюся, що він відповість на ваше запитання.

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

Веселий факт: це припиняє роботу, коли ваш список подвійний замість int (або Long, String, <: Product).
Рік Моріц

Не відповідає ОП: яка говорить про RDD [рядок]
javadba

6

Спосіб 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")

Спосіб 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()

Спосіб 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()

Спосіб 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()

Витягує значення з об’єкта рядка, а потім застосовує клас регістру для перетворення rdd в 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

На новіших версіях 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 - це лише обгортка для sqlContext, hiveContext
Archit

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

Якщо припустити, що іскра вал - це продукт компанії 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 

Такі ж дії, але з меншою кількістю оголошень:

    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

Я спробував пояснити рішення за допомогою проблеми підрахунку слів . 1. Прочитайте файл за допомогою sc

  1. Виробляють кількість слів
  2. Методи створення DF

    • метод rdd.toDF
    • rdd.toDF ("слово", "рахувати")
      • spark.createDataFrame (rdd, схема)

    Прочитайте файл за допомогою іскри

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

    Rdd до Dataframe

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

    Спосіб 1

    Створіть кількість слів RDD для Dataframe

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

    Метод2

    Створіть Dataframe з Rdd

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

    Метод3

    Визначте схему

    імпорт org.apache.spark.sql.types._

    val схема = новий StructType (). add (StructField ("слово", StringType, true)). add (StructField ("count", StringType, true))

    Створіть RowRDD

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

    Створіть DataFrame з RDD за допомогою схеми

    val df = spark.createDataFrame (rowRdd, схема)
    df.show


0

Щоб перетворити масив [рядок] в DataFrame або набір даних, елегантно працює наступне:

Скажімо, схема - це StructType для рядка

val rows: Array[Row]=...
implicit val encoder = RowEncoder.apply(schema)
import spark.implicits._
rows.toDS
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.