Spark使用类将rdd转换为数据帧

时间:2017-03-31 10:23:57

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

我使用Spark 1.6和Scala。 我正在寻找here,但我没有找到明确的答案 我有一个大文件,在过滤了包含一些版权的第一行后,我想获取标题(104个字段)并将其转换为StructType模式。 我正在考虑使用类扩展Product特征来定义Dataframe的模式,然后根据该模式将其转换为Dataframe

最好的方法是什么。

这是我文件中的一个示例:

   text  (06.07.03.216)  COPYRIGHT © skdjh 2000-2016
    text  160614_54554.vf Database    53643_csc   Interface   574 zn  65
    Start   Date    14/06/2016  00:00:00:000
    End Date    14/06/2016  00:14:59:999
    State   "s23"

        cin. Nb      Start     End        Event       Con. Duration  IMSI
        32055680    16/09/2010 16:59:59:245 16/09/2016 17:00:00:000 xxxxxxxxxxxxx
        32055680    16/09/2010 16:59:59:245 16/09/2016 17:00:00:000 xxxxxxxxxxxxx
        32055680    16/09/2010 16:59:59:245 16/09/2016 17:00:00:000 xxxxxxxxxxxxx
        32055680    16/09/2010 16:59:59:245 16/09/2016 17:00:00:000 xxxxxxxxxxxxx
        32055680    16/09/2010 16:59:59:245 16/09/2016 17:00:00:000 xxxxxxxxxxxxx

T想要像这个架构一样将它转换为SparkSQL

    ----------------------------------------------------------------------------------------
  |    cin_Nb |  Start            |   End          |      Event   |   Con_Duration  | IMSI  |
  | ----------------------------------------------------------------------------------------|
  |   32055680 |   16/09/2010     |   16:59:59:245 |  16/09/2016  |   17:00:00:000  | xxxxx |
  |   32055680 |   16/09/2010     |   16:59:59:245 |  16/09/2016  |   17:00:00:000  | xxxxx |
  |   32055680 |   16/09/2010     |   16:59:59:245 |  16/09/2016  |   17:00:00:000  | xxxxx |
  |   20556800 |   16/09/2010     |   16:59:59:245 |  16/09/2016  |   17:00:00:000  | xxxxx |
  |   32055680 |   16/09/2010     |   16:59:59:245 |  16/09/2016  |   17:00:00:000  | xxxxx | 
    ----------------------------------------------------------------------------------------

2 个答案:

答案 0 :(得分:0)

不幸的是,您无法使用案例类或StructType架构!原因是scala不支持超过22个部分的元组,这两种方法都在幕后使用元组。由于您有超过22列,该方法不起作用。

然而,你仍然可以做到这一点,它只是不太好:)你需要做的是将它转换为单列数据帧,并调用列有意义的像“原始”

val df = rdd.toDF("raw")

接下来,您需要定义一个函数来为任何给定列提取所需的列:

val extractData(idx: Long) = udf[String, String, Int](raw => ???)

现在,您需要使用此功能附加所需的每个列。

val columns = yourColumnNamesList.zipWithIndex

val df2 = columns.foldLeft(df){case (acc,(cname,cid)) => acc.withColumn(cname, extractData(cid)($"raw")}

尽管在执行foldLeft时看起来有点可怕,但是如果你看一下执行计划器创建的计划,那么spark就足够聪明,可以将所有这些内容压缩成一个映射步骤,并且吞吐量比你预期的要好。

最后,您可以删除原始数据,因为不再需要它。

df2.drop("raw")

可替换地!

如果您的数据在文件系统上采用分隔格式,那么您应该查看DataBricks csv解析器,该解析器也适用于1.6: - )

答案 1 :(得分:0)

你可以使用zipwithindex而不是过滤第一行 你可以使用一个类检查标题

class convert( var  cin_Nb:String, var start:String, var end:String, 
        var event:String, var duration:String, var zed:String,......)extends Product  with Serializable {    
      def canEqual(that: Any) = that.isInstanceOf[convert]    
      def productArity = 104   
      def productElement(idx: Int) = idx match {
                    case 0 => cin_Nb;case 1 =>  start;case 2 =>  end;
                    case 3 =>  event;case 4 =>  duration;case 5 =>  zed;
                    ..........
                    }
      }

并使用此结构将rdd转换为dataframe