有没有一种方法可以创建一个UDF来接受两个字符串的数组,并将这些字符串作为两个参数传递给函数?

时间:2019-01-14 14:58:50

标签: sql scala apache-spark databricks

我是Scala的新手,所以请原谅我可怜的笔迹。 我有一个函数func1,它接受两个字符串并返回一个字符串。 我也有一个具有2列a1和b1的数据框df1。我正在尝试使用来自df1的列(a1和b1)和作为功能func1的输出的新列c1创建一个新的数据框df2。我知道我需要使用UDF。我不知道如何创建可以接受2列的UDF,并将这两列作为参数传递给func1并返回输出字符串(列c1)。

这是我尝试过的一些事情-

def func1(str1:String, str2:String) : String = {   
        //code
        return str3;
}

val df1= spark.sql("select * from emp")
  .select("a1", "b1").cache()


val df2 = spark.sql("select * from df1")
  .withColumn("c1", func1("a1","b1"))
  .select("a1", "b1").cache()

但是我没有得到结果。请指教。提前致谢。

2 个答案:

答案 0 :(得分:2)

您基本上有语法问题。

请记住,当您执行def func1(str1:String, str2:String) : String = ...时func1是指Scala函数对象,而不是Spark表达式。

另一方面,.withColumn期望将Spark表达式作为第二个参数。

因此,发生的情况是您对.withColumn("c1", func1("a1","b1"))的调用向Spark发送了一个Scala function对象,而Spark API期望使用“ Spark Expression”(例如,列或对列的操作,例如用户定义函数(UDF)。

幸运的是,通常来说,通过调用Spark的udf方法来包装Scala函数,将其转换为Spark UDF很容易。

因此,一个可行的示例可以这样发出:

// A sample dataframe 
val dataframe = Seq(("a", "b"), ("c", "d")).toDF("columnA", "columnB")
// An example scala function that actually does something (string concat)
def concat(first: String, second: String) = first+second
// A conversion from scala function to spark UDF :
val concatUDF = udf((first: String, second: String) => concat(first, second))
// An sample execution of the UDF
// note the $ sign, which is short for indicating a column name
dataframe.withColumn("concat", concatUDF($"columnA", $"columnB")).show
+-------+-------+------+
|columnA|columnB|concat|
+-------+-------+------+
|      a|      b|    ab|
|      c|      d|    cd|
+-------+-------+------+

从那里开始,应该很容易适应您的精确函数及其参数。

答案 1 :(得分:1)

这是您要怎么做

scala> val df = Seq(("John","26"),("Bob","31")).toDF("a1","b1")
df: org.apache.spark.sql.DataFrame = [a1: string, b1: string]

scala> df.createOrReplaceTempView("emp")

scala> :paste
// Entering paste mode (ctrl-D to finish)

def func1(str1:String, str2:String) : String = {
        val str3 = s" ${str1} is ${str2} years old"
        return str3;
}

// Exiting paste mode, now interpreting.

func1: (str1: String, str2: String)String

scala> val my_udf_func1 = udf( func1(_:String,_:String):String )
my_udf_func1: org.apache.spark.sql.expressions.UserDefinedFunction = UserDefinedFunction(<function2>,StringType,Some(List(StringType, StringType)))

scala> spark.sql("select * from emp").withColumn("c1", my_udf_func1($"a1",$"b1")).show(false)
2019-01-14 21:08:30 WARN  ObjectStore:568 - Failed to get database global_temp, returning NoSuchObjectException
+----+---+---------------------+
|a1  |b1 |c1                   |
+----+---+---------------------+
|John|26 | John is 26 years old|
|Bob |31 | Bob is 31 years old |
+----+---+---------------------+


scala>

您需要更正两个地方。

定义常规函数后,您需要将其在udf()中注册为

val my_udf_func1 = udf( func1(_:String,_:String):String )

在调用udf时,您应该使用$"a1"语法,而不仅仅是"a1"