【问题标题】:Pattern match on range in Scala with Spark udf使用 Spark udf 在 Scala 范围内进行模式匹配
【发布时间】:2018-06-14 10:10:21
【问题描述】:

我有一个 Spark DataFrame,其中包含我使用李克特量表与数字分数匹配的字符串。不同的问题 ID 映射到不同的分数。我正在尝试在 Apache Spark udf 中的 Scala 范围内进行模式匹配,使用这个问题作为指导:

How can I pattern match on a range in Scala?

但是当我使用范围而不是简单的 OR 语句时,我会遇到编译错误, 即

31 | 32 | 33 | 34 工作正常

31 to 35 无法编译。请问有什么我在语法上出错的地方吗?

另外,在最后一种情况下 _,我想映射到 String 而不是 Int, case _ => "None" 但这给出了一个错误: java.lang.UnsupportedOperationException: Schema for type Any is not supported

大概这是 Spark 的通用问题,因为完全有可能在原生 Scala 中返回 Any

这是我的代码:

def calculateScore = udf((questionId: Int, answerText: String) => (questionId, answerText) match {

      case ((31 | 32 | 33 | 34 | 35), "Rarely /<br>Never") => 4 //this is fine
      case ((31 | 32 | 33 | 34 | 35), "Occasionally") => 3
      case ((31 | 32 | 33 | 34 | 35), "Often") => 2
      case ((31 | 32 | 33 | 34 | 35), "Almost always /<br>Always") => 1
      case ((x if 41 until 55 contains x), "None of the time") => 1 //this line won't compile
      case _ => 0 //would like to map to "None"
    })

然后在 Spark DataFrame 上使用 udf,如下所示:

val df3 = df.withColumn("NumericScore", calculateScore(df("QuestionId"), df("AnswerText")))

【问题讨论】:

  • UDF 的想法是它返回一个可以在 SQL 语句中使用的结果。因此,它需要是 Int、String 或其他支持的类型。 Any 在 SQL 上下文中没有任何意义。在这里,您正在做一些基本相似的事情,只是使用数据帧而不是直接使用 SQL。不过,如果您真的想要在通配符情况下有不同的行为(我不明白您为什么会这样做),也许您可​​以返回 -1 或类似的东西。或者,让其他情况返回字符串。
  • @Phasmid 我正在清理我正在对其执行分析的数据文件。该文件目前被格式化为一个长长的问题列表,我正在将这些问题作为答案。一些响应需要是字符串,其他是整数、双精度等。当我旋转数据时,我将为每个响应设置架构。所以我需要列的输出是灵活的 - 因此使用 Any。不过,使用 -1 是个好主意。
  • 但我意识到使用 Any 是不可能的,所以我将使用字符串而不是整数

标签: scala apache-spark pattern-matching user-defined-functions


【解决方案1】:

保护表达式应该放在模式之后:

def calculateScore = udf((questionId: Int, answerText: String) => (questionId, answerText) match {
  case ((31 | 32 | 33 | 34 | 35), "Rarely /<br>Never") => 4 
  case ((31 | 32 | 33 | 34 | 35), "Occasionally") => 3
  case ((31 | 32 | 33 | 34 | 35), "Often") => 2
  case ((31 | 32 | 33 | 34 | 35), "Almost always /<br>Always") => 1
  case (x, "None of the time") if 41 until 55 contains x => 1
  case _ => 0 //would like to map to "None"
})

【讨论】:

    【解决方案2】:

    如果您想将最后一个case(即case _)映射到“无”String,那么所有的情况都应该返回String

    以下udf 函数应该适合你

    def calculateScore  = udf((questionId: Int, answerText: String) => (questionId, answerText) match {
      case ((31 | 32 | 33 | 34 | 35), "Rarely /<br>Never") => "4" //this is fine
      case ((31 | 32 | 33 | 34 | 35), "Occasionally") => "3"
      case ((31 | 32 | 33 | 34 | 35), "Often") => "2"
      case ((31 | 32 | 33 | 34 | 35), "Almost always /<br>Always") => "1"
      case (x, "None of the time") if (x >= 41 && x < 55) => "1" //this line won't compile
      case _ => "None"
    })
    

    如果您想将最后一个casecase _ 映射到None,那么您需要将其他返回类型更改为Option 的子级,因为NoneOption 的子级

    以下代码也应该适合您

    def calculateScore  = udf((questionId: Int, answerText: String) => (questionId, answerText) match {
      case ((31 | 32 | 33 | 34 | 35), "Rarely /<br>Never") => Some(4) //this is fine
      case ((31 | 32 | 33 | 34 | 35), "Occasionally") => Some(3)
      case ((31 | 32 | 33 | 34 | 35), "Often") => Some(2)
      case ((31 | 32 | 33 | 34 | 35), "Almost always /<br>Always") => Some(1)
      case (x, "None of the time") if (x >= 41 && x < 55) => Some(1) //this line won't compile
      case _ => None
    })
    

    最后一点是,您收到的错误消息java.lang.UnsupportedOperationException: Schema for type Any is not supported 明确指出不支持返回类型为Anyudf 函数。 match cases 中的所有return types 应该是一致的。

    【讨论】:

    • 这两个答案都非常有帮助,感谢你们俩
    猜你喜欢
    • 2011-03-10
    • 1970-01-01
    • 1970-01-01
    • 2016-03-06
    • 1970-01-01
    • 1970-01-01
    • 2021-12-20
    • 2019-05-26
    • 1970-01-01
    相关资源
    最近更新 更多