【问题标题】:passing UDF to a method or class将 UDF 传递给方法或类
【发布时间】:2017-07-27 06:34:09
【问题描述】:

我有一个 UDF 说

val testUDF = udf{s: string=>s.toUpperCase}

我想在一个单独的方法中创建这个 UDF,或者可能是其他类似实现类的东西,并将它传递给另一个使用它的类。有可能吗?

假设我有一个 A 类

class A(df: DataFrame) {
    def testMethod(): DataFrame = {
        val demo=df.select(testUDF(col))
    }
}

A 类应该能够使用 UDF。这可以实现吗?

【问题讨论】:

  • 是的,绝对有可能:)
  • 您是在问如何从方法中的函数创建 UDF?如何为泛型函数创建 UDF?如何将其传递给 testMethod?最终目标是什么?
  • 您决定接受答案了吗?

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


【解决方案1】:

给定一个dataframe

+----+
|col1|
+----+
|abc |
|dBf |
|Aec |
+----+

还有一个udf 函数

import org.apache.spark.sql.functions._
val testUDF = udf{s: String=>s.toUpperCase}

您绝对可以将另一个类中的 udf 函数用作

val demo = df.select(testUDF(col("col1")).as("upperCasedCol"))

这应该给你

+-------------+
|upperCasedCol|
+-------------+
|ABC          |
|DBF          |
|AEC          |
+-------------+

但我建议您尽可能使用other functions 因为 udf 函数需要对列进行序列化和反序列化,这将比其他可用函数消耗更多时间和内存。 UDF 函数应该是最后的选择

您可以使用upper function 处理您的情况

 val demo = df.select(upper(col("col1")).as("upperCasedCol"))

这将生成与原始udf 函数相同的输出

希望回答对你有帮助

更新

由于您的问题是询问有关如何调用另一个类或对象中定义的 udf 函数的信息,因此这里是方法

假设您有一个对象,您在其中定义了 udf 函数或我建议的函数

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

object UDFs {

  def testUDF = udf{s: String=>s.toUpperCase}

  def testUpper(column: Column) = upper(column)
}

您的 A 类与您的问题一样,我只是添加了另一个功能

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

class A(df: DataFrame) {
  def testMethod(): DataFrame = {
    val demo = df.select(UDFs.testUDF(col("col1")))
    demo
  }

  def usingUpper() = {
    df.select(UDFs.testUpper(col("col1")))
  }
}

然后你可以从main调用函数如下

import org.apache.spark.sql.SparkSession

object TestUpper {

  def main(args: Array[String]): Unit = {
    val sparkSession = SparkSession.builder().appName("Simple Application")
      .master("local")
      .config("", "")
      .getOrCreate()
    import sparkSession.implicits._

    val df = Seq(
      ("abc"),
      ("dBf"),
      ("Aec")
    ).toDF("col1")

    val a = new A(df)
    //calling udf function
    a.testMethod().show(false)

    //calling upper function
    a.usingUpper().show(false)
  }
}

我想这不仅仅是有用的

【讨论】:

  • 答案没有帮助吗?
【解决方案2】:

如果我理解正确,您实际上希望某种工厂为特定的 A 类创建此 user-defined-function。 这可以使用隐式注入的类型类来实现。

例如(我必须定义 UDF 和 DataFrame 才能对此进行测试)

type UDF = String => String

case class DataFrame(col: String) {
  def select(in: String) = s"col:$col, in:$in"
}

trait UDFFactory[A] {
  def testUDF: UDF
}
implicit object UDFFactoryA extends UDFFactory[AClass] {
  def testUDF: UDF = _.toUpperCase
}

class AClass(df: DataFrame) {
  def testMethod(implicit factory: UDFFactory[AClass]) = {
    val demo = df.select(factory.testUDF(df.col))
    println(demo)
  }
}

val a = new AClass(DataFrame("test"))
a.testMethod // prints 'col:test, in:TEST'

【讨论】:

  • 有没有办法可以将 UDF 传递给一个类。例如在你的例子中,AClass 将有两个参数 Aclass(df, UDF)
  • @KishoreKumar 是的,class Aclass(df: DataFrame, myUDF: UserDefinedFunction)
【解决方案3】:

就像您提到的,在您的对象主体或伴随类中创建一个与您的 UDF 完全相同的方法,

val myUDF = udf((str:String) => { str.toUpperCase }) 

然后对于一些数据框df 这样做,

val res=df withColumn("NEWCOLNAME", myUDF(col("OLDCOLNAME")))

这会改变这样的事情,

+-------------------+
|     OLDCOLNAME    |
+-------------------+
|        abc        |
+-------------------+

+-------------------+-------------------+
|     OLDCOLNAME    |     NEWCOLNAME    |
+-------------------+-------------------+
|        abc        |        ABC        | 
+-------------------+-------------------+

如果这有帮助,请告诉我,干杯。

【讨论】:

  • myUDF 缺少 udf 方法调用(和类型修饰符)。您定义了一个常规函数...
  • 是的,我在写地图的时候就想到了地图。我已经编辑了我的答案
【解决方案4】:

是的,这是可能的,因为函数是 scala 中可以传递的对象:

import org.apache.spark.sql.expressions.UserDefinedFunction

class A(df: DataFrame, testUdf:UserDefinedFunction) {    
    def testMethod(): DataFrame = {
        df.select(testUdf(col))
    }
}

【讨论】:

    猜你喜欢
    • 2018-02-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-01
    • 2021-10-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多