如何使用用户定义的类和toDF将RDD转换为数据帧

时间:2019-05-27 15:49:16

标签: scala apache-spark

当我尝试通过sbt package创建以下软件包时:

import org.apache.spark.sql.SparkSession

class Log(val cip: String, val scstatus: Int) {
    var src: String = cip
    var status: Int = scstatus
}

object IISHttpLogs {
  def main(args: Array[String]) {
    val logFiles = "D:/temp/tests/wwwlogs" 
    val spark = SparkSession.builder.appName("LogParser").getOrCreate()
    val sc = spark.sparkContext;
    sc.setLogLevel("ERROR")

    val logs = sc.textFile(logFiles)        

    import spark.implicits._
    val rowDF = logs.filter(l => !l.startsWith("#"))
        .map(l => l.split(" "))
        .map(c => new Log(c(8), c(11).trim.toInt))
        .toDF();
    println(s"line count: ${rowDF.count()}")        
    rowDF.createOrReplaceTempView("rows")
    val maxHit = spark.sql("SELECT top 1 src, count(*) FROM rows group by src order by count(*) desc")
    maxHit.show()

    spark.stop()
  }
}

我收到以下错误:

  

value toDF不是org.apache.spark.rdd.RDD [Log]的成员

我尝试了几种方法:

  • toDFlog
  • 创建一个sql上下文并从此sqlContext导入imlicits._

我只是无法编译我的代码。

欢迎您使用ovverride这个错误。


我很好地读过Generate a Spark StructType / Schema from a case class并写道:

val schema =
    StructType(
        StructField("src", StringType, false) ::
        StructField("status", IntegerType, true) :: Nil)

val rowRDD = logs.filter(l => !l.startsWith("#"))
    .map(l => l.split(" "))
    .map(c => Row(c(8), c(11).trim.toInt));

val rowDF = spark.sqlContext.createDataFrame(rowRDD, schema); 

但是这样做,我不使用Log类。我想知道是否存在通过使用定义的DataFrame类来获取Log的方法,或者官方/最佳方法是使用{{1} }类?

例如我不能写:

Row

我就是不知道为什么吗?

1 个答案:

答案 0 :(得分:2)

您必须使用案例类。至少对我有用:

case class Log(cip: String,  scstatus: Int)
//...
.map(c =>  Log(c(8), c(11).trim.toInt) // ommit 'new'
.toDF()

我不太确定这是否是一般规则。但是,在Dataset API的公告中,明确提到了案例类的用法:

  

Spark 1.6支持自动生成多种类型的编码器,包括原始类型(例如String,Integer,Long),Scala案例类和Java Bean。   (https://databricks.com/blog/2016/01/04/introducing-apache-spark-datasets.html

如果您不能使用案例类,则this answer似乎是合适的。