如何解决类型不匹配问题(预期:双,实际:单位)

时间:2016-05-02 14:29:48

标签: scala apache-spark rdd dstream

这是我计算均方根误差的函数。但是,由于错误Type mismatch issue (expected: Double, actual: Unit),无法编译最后一行。我尝试了很多不同的方法来解决这个问题,但仍然没有成功。有什么想法吗?

  def calculateRMSE(output: DStream[(Double, Double)]): Double = {
        val summse = output.foreachRDD { rdd =>
          rdd.map {
              case pair: (Double, Double) =>
                val err = math.abs(pair._1 - pair._2);
                err*err
          }.reduce(_ + _)
        }
        // math.sqrt(summse)  HOW TO APPLY SQRT HERE?
  }

2 个答案:

答案 0 :(得分:2)

正如伊莱亚指出的那样,foreach(和foreachRDD)不会返回值;它们仅用于副作用。如果您想要返回某些内容,则需要map。基于你的第二个解决方案:

val rmse = output.map(rdd => new RegressionMetrics(rdd).rootMeanSquaredError)

如果你为它做一点功能看起来更好:

val getRmse = (rdd: RDD) => new RegressionMetrics(rdd).rootMeanSquaredError

val rmse = output.map(getRmse)

忽略空的RDD,

val rmse = output.filter(_.nonEmpty).map(getRmse)

这是与for-comprehension完全相同的序列。它只是map,flatMap和filter的语法糖,但我认为在我第一次学习Scala时更容易理解:

val rmse = for {
  rdd <- output
  if (rdd.nonEmpty)
} yield new RegressionMetrics(rdd).rootMeanSquaredError

这是一个总结错误的函数,就像你的第一次尝试一样:

def calculateRmse(output: DStream[(Double, Double)]): Double = {

val getRmse = (rdd: RDD) => new RegressionMetrics(rdd).rootMeanSquaredError

output.filter(_.nonEmpty).map(getRmse).reduce(_+_)
}

编译器对nonEmpty的抱怨实际上是DStream的filter方法的问题。而不是在DStream中对RDD进行操作,filter正在对DStream的类型参数给出的双精度(Double, Double)对进行操作。

我不太了解Spark说它是缺陷,但这很奇怪。 Filter和集合上的大多数其他操作通常是defined in terms of foreach,但DStream在不遵循相同约定的情况下实现这些函数;其弃用的方法foreach和当前的foreachRDD都在流的RDD上运行,但是its other higher-order methods don't

所以我的方法不起作用。 DStream可能有一个很奇怪的原因(性能相关?)这可能是使用foreach做错的方法:

def calculateRmse(ds: DStream[(Double, Double)]): Double = {

  var totalError: Double = 0

  def getRmse(rdd:RDD[(Double, Double)]): Double = new RegressionMetrics(rdd).rootMeanSquaredError

  ds.foreachRDD((rdd:RDD[(Double, Double)]) => if (!rdd.isEmpty) totalError += getRmse(rdd))

  totalError
}

但它有效!

答案 1 :(得分:0)

我成功完成了以下任务:

import org.apache.spark.mllib.evaluation.RegressionMetrics

output.foreachRDD { rdd =>
  if (!rdd.isEmpty)
    {
      val metrics = new RegressionMetrics(rdd)
      val rmse = metrics.rootMeanSquaredError
      println("RMSE: " + rmse)
    }
}