火花:十进制类型未找到

Posted

技术标签:

【中文标题】火花:十进制类型未找到【英文标题】:Spark: decimalType not found 【发布时间】:2019-08-21 04:41:57 【问题描述】:

我正在尝试使用 DecimalType(18,2)。以下是我的代码:

import org.apache.spark.sql.types.DataTypes._

object ETL 
  //created a DecimalType
  val decimalType = DataTypes.createDecimalType(18,2)

  case class SKU(price_usd: decimalType)
)

我收到错误decimalType not found。如何解决?谢谢

顺便说一句,我试过 BigDecimal,它是 (38, 18)。但我需要 (18, 2)。在我的 spark 工作中,我使用 sql 来获取一些属于 (18, 2) 的列。我想写 UDF 来处理它们。我不知道如何在 UDF 中定义小数的日期类型。

【问题讨论】:

"D" 在 price_usd 中应为大写:DecimalType @C.S.ReddyGadipally 一开始,我使用了DecimalType。仍然有同样的错误 【参考方案1】:

在您的代码中,decimalType 实际上不是 Scala 类型标识符 - 它是 DecimalType 类的值。所以,你不能在编译器需要类型标识符的地方使用它。

为了编写 UDF,您可以只使用 java.math.BigDecimal 作为参数类型。无需指定精度和比例。但是,如果您确实需要为 UDF 中的计算设置这些值,您可以尝试在 MathContext 中指定它们。

package HelloSpec.parser

import com.holdenkarau.spark.testing.DataFrameSuiteBase, SharedSparkContext
import org.apache.spark.sql.types.DecimalType, StructField, StructType
import org.scalatest.FlatSpec


case class SKU(price_usd: BigDecimal)

object Fields 
  val PRICE_USD = "price_usd"


class TestSo extends FlatSpec with DataFrameSuiteBase with SharedSparkContext 

  import Fields._

  it should "not fail" in 
    import spark.implicits._
    val df = Seq(
      SKU(BigDecimal("1.12")),
      SKU(BigDecimal("1234567890123456.12")),
      SKU(BigDecimal("1234567890123456.123")),
      SKU(BigDecimal("12345678901234567.12"))
    ).toDF

    df.printSchema()
    df.show(truncate = false)

    assert(
      df.schema ==
        StructType(Seq(StructField(name = PRICE_USD, dataType = DecimalType(38, 18))))
    )

    val castedTo18_2 = df.withColumn(PRICE_USD, df(PRICE_USD).cast(DecimalType(18, 2)))
    castedTo18_2.printSchema()
    castedTo18_2.show(truncate = false)
    assert(
      castedTo18_2.schema ==
        StructType(Seq(StructField(name = PRICE_USD, dataType = DecimalType(18, 2))))
    )
    assert 
      castedTo18_2.as[Option[BigDecimal]].collect.toSeq.sorted == Seq(
        // this was 12345678901234567.12 before the cast,
        // but the number with 17 digits before the decimal point exceeded the 18-2=16 allowed digits
        None,
        Some(BigDecimal("1.12")),
        Some(BigDecimal("1234567890123456.12")),
        // note, that 1234567890123456.123 was rounded to 1234567890123456.12
        Some(BigDecimal("1234567890123456.12"))
      )
    

    import org.apache.spark.sql.functions.udf, col
    val processBigDecimal = udf(
      // The argument type has to be java.math.BigDecimal, not scala.math.BigDecimal, which is imported by default
      (bd: java.math.BigDecimal) => 
        if (bd == null) 
          null
         else 
          s"$bd.getClass with precision $bd.precision, scale $bd.scale and value $bd"
        
      
    )

    val withUdfApplied = castedTo18_2.
      withColumn("udf_result", processBigDecimal(col(PRICE_USD)))

    withUdfApplied.printSchema()
    withUdfApplied.show(truncate = false)

    assert(
      withUdfApplied.as[(Option[BigDecimal], String)].collect.toSeq.sorted == Seq(
        None -> null,
        Some(BigDecimal("1.12")) -> "class java.math.BigDecimal with precision 19, scale 18 and value 1.120000000000000000",
        Some(BigDecimal("1234567890123456.12")) -> "class java.math.BigDecimal with precision 34, scale 18 and value 1234567890123456.120000000000000000",
        Some(BigDecimal("1234567890123456.12")) -> "class java.math.BigDecimal with precision 34, scale 18 and value 1234567890123456.120000000000000000"
      )
    )
  

【讨论】:

我试过BigDecimal,它属于(38, 18)。但我需要 (18, 2) 用例是什么?为什么将 DataFrame 中的列转换为 (18, 2) 对您不起作用? 在我的 spark 作业中,我使用 sql 来获取一些属于 (18, 2) 的列。我想写 UDF 来处理它们。我不知道如何在 UDF 中定义小数的日期类型。 这使它成为一个不同的问题=)。我添加了 UDF 的示例。

以上是关于火花:十进制类型未找到的主要内容,如果未能解决你的问题,请参考以下文章

错误 C2679:二进制“<<”:未找到采用“std::string”类型右侧操作数的运算符(或没有可接受的转换)

从Greenplum读取数据时,如何在火花中将十进制值转换为字符串?

火花错误 - 小数精度 39 超过最大精度 38

基于连接火花创建新的二进制列

python中的火花:通过使用numpy.fromfile加载二进制数据来创建rdd

呈现问题:二进制 XML 文件行 #-1:未找到开始标记