如何处理spark reduceByKey函数中的空值?

时间:2016-03-29 21:48:28

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

我有一个Spark DataFrame(df),如下所示:

+----------+--------+----------+--------+                                                                 
|        c1|      c2|        c3|      c4|
+----------+--------+----------+--------+
|      1   |    5   |      null|    7   |
+----------+--------+----------+--------+
|      1   |    5   |      4   |    8   |
+----------+--------+----------+--------+
|      1   |    3   |      null|   11   |
+----------+--------+----------+--------+
|      1   |    3   |      null| null   |
+----------+--------+----------+--------+
|      2   |    6   |      23  |   17   |
+----------+--------+----------+--------+
|      2   |    6   |      7   |    3   |
+----------+--------+----------+--------+
|      2   |    3   |      null|   11   |
+----------+--------+----------+--------+
|      2   |    3   |      null|   17   |
+----------+--------+----------+--------+

我希望使用(c1,c2)作为关键字进行汇总,并average c3c4,以便我拥有:

+----------+--------+----------+--------+                                                                 
|        c1|      c2|        c3|      c4|
+----------+--------+----------+--------+
|      1   |    5   |      4   |  7.5   |
+----------+--------+----------+--------+
|      1   |    3   |      null|   11   |
+----------+--------+----------+--------+
|      2   |    6   |      15  |    10  |
+----------+--------+----------+--------+
|      2   |    3   |      null|   14   |
+----------+--------+----------+--------+

所以,基本上我忽略了null值。

我的半生不熟的代码如下所示:

val df1 = df.
          // just working on c3 for time being
          map(x => ((x.getInt(0), x.getInt(1)), x.getDouble(3))).
          reduceByKey( 
            (x, y) => {
            var temp = 0
            var sum = 0.0
            var flag = false
            if (x == null) {
              if (y != null) {
                temp = temp + 1
                sum = y
                flag = true
              }
            } else {
              if (y == null) {
                temp = temp + 1
                sum = x 
              } else {
                temp = temp + 1
                sum = x + y
                flag = true
              } 
            } 
            if (flag == false) {
              null 
            } else {
              sum/temp 
            }
            }
          )

显然,上面的代码不起作用。任何帮助使代码工作的人都非常感激。

编辑1 @ zero232给出的答案是一个解决方案。但是,它不是我正在寻找的“解决方案”。我的兴趣是在为reduceByKey() 编写自定义函数时理解如何处理空值。 我正在重新询问以下问题

我希望使用(c1,c2)作为关键字进行汇总,并root mean square [{sum(a_i ^ 2)} ^ 0.5](或某些函数在火花中不可用){忽略空值时{1}}和c3,所以我有这个:

c4

1 个答案:

答案 0 :(得分:3)

只需groupBy并使用mean

df.groupBy("c1", "c2").mean("c3", "c4")

agg

df.groupBy("c1", "c2").agg(avg("c3"), avg("c4"))

通常DataFrames上的所有原始函数都会正确处理null值。

import org.apache.spark.sql.functions._

def rms(c: String) = sqrt(avg(pow(col(c), 2))).alias(s"rms($c)")
df.groupBy("c1", "c2").agg(rms("c3"), rms("c4"))

如果您想忽略null RDDs,请在应用缩减之前将其过滤掉:

somePairRDD.filter(_._2 != null)
  .foldByKey(someDefualtValue)(someReducingFunction)

或将值转换为Option并使用模式匹配:

somePairRDD.mapValues(Option(_)).reduceByKey {
  case (Some(x), Some(y)) => doSomething(x, y)
  case (Some(x), _) => doSomething(x)
  case (_, Some(_)) => doSomething(y)
  case _ => someDefualt
}

或使用map / flatMap / getOrElse和其他标准工具来处理未定义的值。