如何将rdd对象转换为spark中的dataframe

时间:2015-04-01 05:38:33

标签: scala apache-spark apache-spark-sql rdd

如何将RDD(org.apache.spark.rdd.RDD[org.apache.spark.sql.Row])转换为数据帧org.apache.spark.sql.DataFrame。我使用.rdd将数据帧转换为rdd。处理完之后我想把它放回到数据帧中。我怎么能这样做?

12 个答案:

答案 0 :(得分:80)

SqlContext有多种createDataFrame方法,可以DataFrame创建RDD。我想其中一个适用于你的背景。

例如:

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

使用给定的RDD从包含Rows的RDD创建DataFrame   架构。

答案 1 :(得分:65)

此代码与 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))

现在将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 :(得分:64)

假设你的RDD [row]被称为rdd,你可以使用:

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

答案 3 :(得分: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)))

现在,您可以使用 createDataFrame 方法将RDD转换为DataFrame。

val aNamedDF = sqlContext.createDataFrame(aRdd,aStruct)

答案 4 :(得分:15)

注意:此答案最初发布为here

我发布此答案是因为我想分享其他答案中我没有找到的其他详细信息

要从RDD of Rows创建DataFrame,有两个主要选项:

1)正如已经指出的那样,您可以使用可以toDF()导入的import sqlContext.implicits._。但是,此方法仅适用于以下类型的RDD:

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

(来源:SQLContext.implicits对象的Scaladoc

最后一个签名实际上意味着它可以用于元组的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")

这种方法的主要缺点(在我看来)是你必须逐列显式地设置map函数中结果DataFrame的模式。也许这可以以编程方式完成,如果你事先不知道架构,但事情可能会有点混乱。所以,或者,还有另一种选择:

2)您可以在SQLContext对象中提供的已接受答案中使用createDataFrame(rowRDD: RDD[Row], schema: StructType)。转换旧DataFrame的RDD的示例:

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

请注意,无需显式设置任何架构列。我们重用旧的DF架构,它是StructType类,可以很容易地扩展。但是,这种方法有时是不可能的,在某些情况下效率可能低于第一种方法。

答案 5 :(得分:6)

以下是将List转换为Spark RDD,然后将Spark RDD转换为Dataframe的简单示例。

请注意,我使用了Spark-shell的scala REPL来执行以下代码。这里sc是SparkContext的一个实例,它在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|
+---+

答案 6 :(得分:5)

方法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()

从行对象中提取值,然后应用case类将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

答案 7 :(得分:3)

更新版本的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: _*)

答案 8 :(得分:1)

join -t, -a1 input.csv output.csv | perl -MText::Parsewords -lne '
    my @flds = Text::ParseWords::parse_line(",", 1, $_);
    if ($#flds >= 5) { $flds[4] = $flds[5]; $#flds = 4 };
    print join(",", @flds);
  '

假设val spark是SparkSession.builder的产物......

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

相同的步骤,但val声明较少:

    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 

答案 9 :(得分:1)

我尝试使用字数统计问题来解释解决方案。 1.使用sc

读取文件
  1. 产生字数
  2. 创建DF的方法

    • rdd.toDF方法
    • rdd.toDF(“ word”,“ count”)
      • spark.createDataFrame(rdd,schema)

    使用spark读取文件

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

    Rdd到数据框

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

    方法1

    创建到数据框的字数RDD

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

    方法2

    从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 schema = new StructType()。 add(StructField(“ word”,StringType,true))。 add(StructField(“ count”,StringType,true))

    创建RowRDD

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

    使用模式从RDD创建DataFrame

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

答案 10 :(得分:0)

要将数组[Row]转换为DataFrame或Dataset,以下工作优雅:

比如说,schema是行的StructType,然后是

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

答案 11 :(得分:0)

我也遇到了同样的问题,终于解决了。这是退出示例和简单。

  1. 您必须添加此代码 import sc.implicits._sc 表示 SQLContext。添加此代码,您将获得 rdd.toDF() 方法。
  2. 将您的 rdd[RawData] 转换为 rdd[YourCaseClass]。例如,你有一个像 rdd[(String, Integer, Long)] 这样的 rdd 类型,你可以创建一个 Case Class YourCaseClass(name: String, age: Integer, timestamp: Long) 并将原始 rdd 转换为 YourCaseClass 类型的 rdd,然后你得到 rdd[YourCaseClass]< /li>
  3. rdd[YourCaseClass] 保存到 hive 表。 yourRdd.toDF().write.format("parquet").mode(SaveMode.Overwrite).insertInto(yourHiveTableName) 使用 case 类来表示 rdd 类型,我们可以避免命名每个列字段或 StructType 相关架构。