【问题标题】:Case class equality in Apache SparkApache Spark 中的案例类相等
【发布时间】:2016-05-20 00:36:38
【问题描述】:

为什么 Spark 中的模式匹配与 Scala 中的模式匹配不同?请参阅下面的示例...函数f() 尝试对类进行模式匹配,该类在 Scala REPL 中有效,但在 Spark 中失败并导致所有“???”。 f2() 是一种使用 .isInstanceOf() 在 Spark 中获得所需结果的解决方法,但我知道这在 Scala 中是不好的形式。

非常感谢您在 Spark 的这种情况下以正确方式匹配模式的任何帮助。

abstract class a extends Serializable {val a: Int}
case class b(a: Int) extends a 
case class bNull(a: Int=0) extends a 

val x: List[a] = List(b(0), b(1), bNull())
val xRdd = sc.parallelize(x)

尝试在 Scala REPL 中有效但在 Spark 中失败的模式匹配

def f(x: a) = x match {
    case b(n) => "b"
    case bNull(n) => "bnull"
    case _ => "???"
}

在 Spark 中起作用的解决方法,但形式不好(我认为)

def f2(x: a) = {
    if (x.isInstanceOf[b]) {
        "b"
    } else if (x.isInstanceOf[bNull]) {
        "bnull"
    } else {
        "???"
    }
}

查看结果

xRdd.map(f).collect                   //does not work in Spark
                                      // result: Array("???", "???", "???")
xRdd.map(f2).collect                  // works in Spark
                                      // resut: Array("b", "b", "bnull")
x.map(f(_))                           // works in Scala REPL    
                                      // result: List("b", "b", "bnull")

使用的版本... Spark 结果在 spark-shell 中运行(AWS EMR-4.3 上的 Spark 1.6) SBT 0.13.9 (Scala 2.10.5) 中的 Scala REPL

【问题讨论】:

    标签: scala apache-spark pattern-matching rdd case-class


    【解决方案1】:

    这是 Spark REPL 的一个已知问题。您可以在SPARK-2620 中找到更多详细信息。它影响 Spark REPL 中的多个操作,包括PairwiseRDDs 上的大部分转换。例如:

    case class Foo(x: Int)
    
    val foos = Seq(Foo(1), Foo(1), Foo(2), Foo(2))
    foos.distinct.size
    // Int = 2
    
    val foosRdd = sc.parallelize(foos, 4)
    foosRdd.distinct.count
    // Long = 4  
    
    foosRdd.map((_, 1)).reduceByKey(_ + _).collect
    // Array[(Foo, Int)] = Array((Foo(1),1), (Foo(1),1), (Foo(2),1), (Foo(2),1))
    
    foosRdd.first == foos.head
    // Boolean = false
    
    Foo.unapply(foosRdd.first) == Foo.unapply(foos.head)
    // Boolean = true
    

    更糟糕的是结果取决于数据分布:

    sc.parallelize(foos, 1).distinct.count
    // Long = 2
    
    sc.parallelize(foos, 1).map((_, 1)).reduceByKey(_ + _).collect
    // Array[(Foo, Int)] = Array((Foo(2),2), (Foo(1),2))
    

    您可以做的最简单的事情是在 REPL 之外定义和打包所需的案例类。使用spark-submit 直接提交的任何代码都应该可以正常工作。

    在 Scala 2.11+ 中,您可以使用paste -raw 在 REPL 中直接创建一个包。

    scala> :paste -raw
    // Entering paste mode (ctrl-D to finish)
    
    package bar
    
    case class Bar(x: Int)
    
    
    // Exiting paste mode, now interpreting.
    
    scala> import bar.Bar
    import bar.Bar
    
    scala> sc.parallelize(Seq(Bar(1), Bar(1), Bar(2), Bar(2))).distinct.collect
    res1: Array[bar.Bar] = Array(Bar(1), Bar(2))
    

    【讨论】:

    • 感谢 zero323!我看到提到模式匹配在 spark shell 中不起作用,但没有细节......你是说如果我在 jar 中定义我的案例类,我将能够在 REPL 中对它们进行模式匹配?再次感谢!
    • 没错。在外部定义,构建 jar,包含在 CLASSPATH 中并导入。
    猜你喜欢
    • 2015-09-20
    • 1970-01-01
    • 1970-01-01
    • 2015-05-20
    • 1970-01-01
    • 2020-11-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多