使用udf进行操作时,在数据框中将空值保持为空

时间:2019-02-11 17:19:12

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

我有一个数据框,其中一个列的值为空。

我将列传递给udf以进行数学乘法。我想跳过该udf为空值。我不想使用na.fill替换为空。

DF的架构如下所示 DataFrame1

root
|-- Name: string (nullable =true)
|-- Value: decimal(38,0) (nullable=true) //This is the col
|-- powValue: integer (nullable=true)
|-- mulValue: integer (nullable=true)


def udfFn(val1: Integer, powVal:Integer, mulVal:Integer) = {
val bd1 = new BigDecimal(val1);
val bd2 =bd1.scakeByPowerTen(-powVal)

val bd3 = new BigDecimal(mulVal)
val bd4=bd2.multiply(bd3)
}
val calUDF=udf({(val1: Integer, powVal:Integer, mulVal:Integer)=> 
udfFn(val1,powVal,mulVal)})

val newDf=DataFrame1.withColumn("Final_Value",calUDF(col("Value"),col("powValue"),col("mulValue")))

我的 DataFrame1

列中可以包含0个空数字。

1 个答案:

答案 0 :(得分:0)

基于类型val1,看来ValueBigDecimal实际上应该是decimal(38,0),所以我将在下面的代码中进行假设。

执行此操作的快速方法只是使用一个好的'ole if-else语句。这可能也是最有效的方式,具体取决于:

def udfFn(val1: BigDecimal, powVal: Int, mulVal: Int): BigDecimal =
  if (val1 != null && powVal != null && mulVal != null) {
    val mul = new BigDecimal(mulVal)
    val1.scaleByPowerTen(-powVal).multiply(mul)
  } else {
    null
  }

我认为这看起来有点丑陋,如果您想使其更好地阅读,这是函数编程的工作! Optionfor的救援知识! (请注意,如果性能是一个问题,那么第一个解决方案可能是您的最佳选择)

您可以执行以下操作:

def udfFn(val1: BigDecimal, powVal: Int, mulVal: Int): Option[BigDecimal] =
  val r = for {
    bd1 <- Option(val1)
    pow <- Option(powVal)
    mul <- Option(mulVal).map(new BigDecimal(_))
  } yield (bd1.scaleByPowerTen(-pow).multiply(mul))

仅当每个输入forOption时,对Some的理解将产生Option的{​​{1}},否则为Some

我个人更喜欢使用None而不是Dataset来完成此操作,因为我认为它使转换更易于理解,并且使每个步骤的架构都非常明确,并且允许您在不依赖UDF的情况下编写转换,但是绝对最好做您和/或组织更满意的事情。对于DataFrame解决方案,我将创建几个案例类:

Dataset

然后执行转换的代码将是这样:

case class NewData(name: Option[String], val1: Option[BigDecimal], powVal: Option[Int], mulVal: Option[Int], finalValue: Option[BigDecimal])

case class SomeData(name: Option[String], val1: Option[BigDecimal], powVal: Option[Int], mulVal: Option[Int]) {
  def toNewData: NewData = {
    val fv = for {
      bd1 <- val1
      pow <- powVal
      mul <- mulVal.map(new BigDecimal(_))
    } yield (bd1.scaleByPowerTen(-pow).multiply(mul))

    NewData(name, val1, powVal, mulVal, fv)
  }
}