Cách chuyển đổi đối tượng rdd thành dataframe trong spark


139

Làm cách nào tôi có thể chuyển đổi RDD ( org.apache.spark.rdd.RDD[org.apache.spark.sql.Row]) thành Dataframe org.apache.spark.sql.DataFrame. Tôi đã chuyển đổi một dataframe sang rdd bằng cách sử dụng .rdd. Sau khi xử lý nó tôi muốn nó trở lại trong dataframe. Tôi có thể làm cái này như thế nào ?


cách để đạt được điều này trong Spark 2.x
mrsrinivas

Câu trả lời:


88

SqlContextcó một số createDataFramephương thức tạo DataFramera một RDD. Tôi tưởng tượng một trong số này sẽ làm việc cho bối cảnh của bạn.

Ví dụ:

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

Tạo một DataFrame từ RDD chứa Hàng bằng cách sử dụng lược đồ đã cho.


93

Mã này hoạt động hoàn hảo từ Spark 2.x với Scala 2.11

Nhập các lớp cần thiết

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

Tạo SparkSessionđối tượng và ở đâyspark

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

Chúng ta hãy RDDlàm cho nó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))
  )
)

Phương pháp 1

Sử dụng 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]|
+------+--------------------+

Cách 2

Sử dụng SparkSession.createDataFrame(RDD obj)và chỉ định tên cột.

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

Phương pháp 3 (Trả lời thực tế cho câu hỏi)

Cách này đòi hỏi đầu vào rddphải là loại 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)
  )
)

tạo lược đồ

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

Bây giờ áp dụng cả hai rowsRddschemađể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
Cảm ơn bạn đã chỉ ra các cách khác nhau để sử dụng createDataFrame theo cách dễ hiểu
vatsug

phương pháp thứ ba rất hữu ích trên các khối dữ liệu vì các phương thức khác không hoạt động và gây ra lỗi
Narendra Maru

67

Giả sử RDD [hàng] của bạn được gọi là rdd, bạn có thể sử dụng:

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

26
Tôi nghĩ rằng nó không hoạt động cho RDD [Row]. Tôi có thiếu thứ gì không?
Daniel de Paula

4
Vì Spark 2.0 SQLContext được thay thế bằng SparkSession, nhưng lớp được giữ trong cơ sở mã để tương thích ngược (scaladoc). Sử dụng nó ném cảnh báo khấu hao.
tomaskazemekas

18

Lưu ý: Câu trả lời này ban đầu được đăng ở đây

Tôi đang đăng câu trả lời này vì tôi muốn chia sẻ chi tiết bổ sung về các tùy chọn có sẵn mà tôi không tìm thấy trong các câu trả lời khác


Để tạo DataFrame từ RDD of Rows, có hai tùy chọn chính:

1) Như đã chỉ ra, bạn có thể sử dụng toDF()có thể được nhập bởi import sqlContext.implicits._. Tuy nhiên, cách tiếp cận này chỉ hoạt động đối với các loại RDD sau:

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

(nguồn: Scaladoc của SQLContext.implicitsđối tượng)

Chữ ký cuối cùng thực sự có nghĩa là nó có thể hoạt động cho RDD của các bộ dữ liệu hoặc RDD của các lớp trường hợp (vì các bộ dữ liệu và các lớp trường hợp là các lớp con của scala.Product).

Vì vậy, để sử dụng phương pháp này cho một RDD[Row], bạn phải ánh xạ nó tới một RDD[T <: scala.Product]. Điều này có thể được thực hiện bằng cách ánh xạ mỗi hàng vào một lớp trường hợp tùy chỉnh hoặc một tuple, như trong các đoạn mã sau:

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

hoặc là

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

Hạn chế chính của phương pháp này (theo ý kiến ​​của tôi) là bạn phải đặt rõ ràng lược đồ của DataFrame kết quả trong hàm ánh xạ, theo từng cột. Có thể điều này có thể được thực hiện theo chương trình nếu bạn không biết trước lược đồ, nhưng mọi thứ có thể hơi lộn xộn ở đó. Vì vậy, thay vào đó, có một lựa chọn khác:


2) Bạn có thể sử dụng createDataFrame(rowRDD: RDD[Row], schema: StructType)như trong câu trả lời được chấp nhận, có sẵn trong đối tượng SQLContext . Ví dụ để chuyển đổi RDD của DataFrame cũ:

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

Lưu ý rằng không cần thiết phải đặt rõ ràng bất kỳ cột lược đồ nào. Chúng tôi sử dụng lại lược đồ của DF cũ, thuộc StructTypelớp và có thể dễ dàng mở rộng. Tuy nhiên, cách tiếp cận này đôi khi không thể thực hiện được và trong một số trường hợp có thể kém hiệu quả hơn phương pháp đầu tiên.


Cảm ơn chi tiếtimport sqlContext.implicits.
javadba

Trong tương lai, xin vui lòng không đăng câu trả lời giống hệt nhau cho nhiều câu hỏi. Nếu các câu hỏi là trùng lặp, hãy đăng một câu trả lời hay, sau đó bỏ phiếu hoặc gắn cờ để đóng câu hỏi khác dưới dạng trùng lặp. Nếu câu hỏi không trùng lặp, điều chỉnh câu trả lời của bạn cho câu hỏi. Xem Làm thế nào để tôi viết một câu trả lời tốt? .

15

Giả sử bạn có một DataFramevà bạn muốn thực hiện một số sửa đổi trên dữ liệu trường bằng cách chuyển đổi nó thành RDD[Row].

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

Để chuyển đổi trở lại DataFrametừ RDDchúng ta cần xác định loại cấu trúc của RDD.

Nếu kiểu dữ liệu là vậy Long thì nó sẽ trở thành như LongTypetrong cấu trúc.

Nếu Stringsau đó StringTypetrong cấu trúc.

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

Bây giờ bạn có thể chuyển đổi RDD sang DataFrame bằng phương thức createDataFrame .

val aNamedDF = sqlContext.createDataFrame(aRdd,aStruct)

7

Dưới đây là một ví dụ đơn giản về việc chuyển đổi Danh sách của bạn thành Spark RDD và sau đó chuyển đổi Spark RDD đó thành Dataframe.

Xin lưu ý rằng tôi đã sử dụng REPL của Spark-shell để thực thi mã sau, Ở đây sc là một phiên bản của SparkContext, có sẵn trong Spark-shell. Hy vọng nó trả lời câu hỏi của bạn.

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

Một sự thật thú vị: điều này dừng hoạt động, khi Danh sách của bạn là Double, thay vì int (hoặc Long, String, <: Product).
Rick Moritz

Không trả lời OP: mà nói về RDD [Row]
javadba

6

Cách 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")

Cách 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()

Phương pháp 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()

Phương pháp 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()

Trích xuất giá trị từ đối tượng hàng và sau đó áp dụng lớp case để chuyển đổi rdd sang 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

Trên các phiên bản mới hơn của tia lửa (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 chỉ là một trình bao bọc cho sqlContext, hiveContext
Archit

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

Giả sử val spark là sản phẩm của 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 

Các bước tương tự, nhưng với khai báo val ít hơn:

    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

Tôi đã cố gắng giải thích các giải pháp bằng cách sử dụng vấn đề đếm từ . 1. Đọc tệp bằng sc

  1. Sản xuất số từ
  2. Phương pháp tạo DF

    • phương pháp rdd.toDF
    • rdd.toDF ("từ", "đếm")
      • spark.createDataFrame (rdd, lược đồ)

    Đọc tập tin bằng tia lửa

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

    Chuyển đến Dataframe

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

    Phương pháp 1

    Tạo số lượng từ RDD vào Dataframe

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

    Phương pháp 2

    Tạo Dataframe từ Rdd

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

    Phương pháp 3

    Xác định lược đồ

    nhập org.apache.spark.sql.types._

    lược đồ val = new StructType (). thêm (StructField ("từ", StringType, true)). thêm (StructField ("đếm", StringType, true))

    Tạo RowRDD

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

    Tạo DataFrame từ RDD với lược đồ

    val df = spark.createDataFrame (rowRdd, lược đồ)
    df.show


0

Để chuyển đổi một mảng [Hàng] thành DataFrame hoặc Bộ dữ liệu, các thao tác sau đây hoạt động một cách tao nhã:

Nói, lược đồ là StructType cho hàng, sau đó

val rows: Array[Row]=...
implicit val encoder = RowEncoder.apply(schema)
import spark.implicits._
rows.toDS
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.