【问题标题】:Pass column and a Map to a Scala UDF将列和映射传递给 Scala UDF
【发布时间】:2020-06-16 22:10:47
【问题描述】:

我来自 Pyspark。我知道如何在 Pyspark 中做到这一点,但在 Scala 中却没有做到这一点。

这是一个数据框,

val df = Seq(
  ("u1", Array[Int](2,3,4)),
  ("u2", Array[Int](7,8,9))
).toDF("id", "mylist")


//    +---+---------+
//    | id|   mylist|
//    +---+---------+
//    | u1|[2, 3, 4]|
//    | u2|[7, 8, 9]|
//    +---+---------+

这是一个 Map 对象,

val myMap = (1 to 4).toList.map(x => (x,0)).toMap

//myMap: scala.collection.immutable.Map[Int,Int] = Map(1 -> 0, 2 -> 0, 3 -> 0, 4 -> 0)

所以这个映射的键值是从 1 到 4。

对于 df 的每一行,我想检查“mylist”中的任何元素是否包含在 myMap 中作为键值。如果 myMap 包含一个元素,则返回该元素(如果包含多个元素,则返回任意一个),否则返回 -1。

所以结果应该是这样的

    +---+---------+-------+
    | id|   mylist| label|
    +---+---------+-------+
    | u1|[2, 3, 4]|    2  |
    | u2|[7, 8, 9]|    -1 |
    +---+---------+-------+

我尝试了以下方法:

  1. 以下函数适用于数组对象,但不适用于列:
def list2label(ls: Array[Int],
                m:  Map[Int, Int]):(Int) = {
                    var flag = 0
                    for (element <- ls) {
                        if (m.contains(element)) flag = element
                    }
                    flag
                }

val testls = Array[Int](2,3,4)
list2label(testls, myMap)

//testls: Array[Int] = Array(2, 3, 4)
//res33: Int = 4
  1. 尝试使用 UDF,但出现错误:
def list2label_udf(m: Map[Int, Int]) = udf( (ls: Array[Int]) =>(

                    var flag = 0
                    for (element <- ls) {
                        if (m.contains(element)) flag = element
                    }
                    flag
    )
)

//<console>:3: error: illegal start of simple expression
//                    var flag = 0
//                    ^

我认为我的 udf 格式错误..

  1. 在 Pyspark 中,我可以随心所欲地做到这一点:
%pyspark

myDict={1:0, 2:0, 3:0, 4:0}

def list2label(ls, myDict):
    for i in ls:
        if i in dict3:
            return i
    return 0

def list2label_UDF(myDict):
     return udf(lambda c: list2label(c,myDict))

df = df.withColumn("label",list2label_UDF(myDict)(col("mylist")))

任何帮助将不胜感激!

【问题讨论】:

  • @user10938362 谢谢,我相信这提出了一个更普遍的问题,但是与这种情况相比,我的额外问题是需要在 UDF 中编写多行方法。所以我提出这个问题。显然,从接受的答案来看,我错误地使用了括号。

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


【解决方案1】:

解决方法如下:

  scala> df.show
+---+---------+
| id|   mylist|
+---+---------+
| u1|[2, 3, 4]|
| u2|[7, 8, 9]|
+---+---------+


scala> def customUdf(m: Map[Int,Int]) = udf((s: Seq[Int]) => {
          val intersection = s.toList.intersect(m.keys.toList)
          if(intersection.isEmpty) -1 else intersection(0)})

customUdf: (m: Map[Int,Int])org.apache.spark.sql.expressions.UserDefinedFunction

scala> df.select($"id", $"myList", customUdf(myMap)($"myList").as("new_col")).show
+---+---------+-------+
| id|   myList|new_col|
+---+---------+-------+
| u1|[2, 3, 4]|      2|
| u2|[7, 8, 9]|     -1|
+---+---------+-------+

另一种方法可能是发送 map 的键列表而不是 map 本身,因为 ypu 只检查键。为此,解决方案如下:

scala> def customUdf1(m: List[Int]) = udf((s: Seq[Int]) => {
          val intersection = s.toList.intersect(m)
          if(intersection.isEmpty) -1 else intersection(0)})

customUdf1: (m: List[Int])org.apache.spark.sql.expressions.UserDefinedFunction

scala> df.select($"id",$"myList", customUdf1(myMap.keys.toList)($"myList").as("new_col")).show
+---+---------+-------+
| id|   myList|new_col|
+---+---------+-------+
| u1|[2, 3, 4]|      2|
| u2|[7, 8, 9]|     -1|
+---+---------+-------+

如果有帮助请告诉我!!

【讨论】:

  • 谢谢阿南德!您的解决方案效果很好。我发现我在第二种方法中做错了:1.使用括号()而不是大括号{} 2.定义Array[Int]而不是Seq[Int]
猜你喜欢
  • 2017-12-11
  • 2021-12-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-08
  • 2021-11-14
  • 1970-01-01
相关资源
最近更新 更多