来自案例类的Spark模式具有正确的可空性

时间:2016-11-27 14:43:22

标签: apache-spark apache-spark-sql apache-spark-ml apache-spark-dataset spark-csv

对于自定义Estimator的transformSchema方法,我需要能够将输入数据帧的模式与案例类中定义的模式进行比较。通常这可以像Generate a Spark StructType / Schema from a case class那样执行,如下所述。但是,使用了错误的可空性:

spark.read.csv().as[MyClass]推断的df的真实架构可能如下所示:

root
 |-- CUSTOMER_ID: integer (nullable = false)

案例类:

case class MySchema(CUSTOMER_ID: Int)

比较我使用:

val rawSchema = ScalaReflection.schemaFor[MySchema].dataType.asInstanceOf[StructType]
  if (!rawSchema.equals(rawDf.schema))

不幸的是,这总是产生false,因为从case类手动推断的新模式设置为nullable为true(因为ja java.Integer实际上可能为null)

root
 |-- CUSTOMER_ID: integer (nullable = true)

如何在创建架构时指定nullable = false

1 个答案:

答案 0 :(得分:5)

可以说,你正在混合那些并非真正属于同一空间的东西。 ML管道本质上是动态的,引入静态类型的对象并没有真正改变它。

此外,类的模式定义为:

case class MySchema(CUSTOMER_ID: Int)

将无法为空CUSTOMER_IDscala.Intjava.lang.Integer

不同
scala> import org.apache.spark.sql.catalyst.ScalaReflection.schemaFor
import org.apache.spark.sql.catalyst.ScalaReflection.schemaFor

scala> case class MySchema(CUSTOMER_ID: Int)
defined class MySchema

scala> schemaFor[MySchema].dataType
res0: org.apache.spark.sql.types.DataType = StructType(StructField(CUSTOMER_ID,IntegerType,false))

如果您想要nullable字段Option[Int]

,那就是说
case class MySchema(CUSTOMER_ID: Option[Int])

如果您不想使用Int,请使用csv

你在这里遇到的另一个问题是,对于Dataset,每个字段都可以按定义为空,这个状态是"继承"由编码spark.read.csv(...) 。所以在实践中:

root
 |-- CUSTOMER_ID: integer (nullable = true)

总是会导致:

nullable

这就是你得到架构不匹配的原因。遗憾的是,对于不执行可空性约束的来源,例如csvjson,无法覆盖spark.createDataFrame( spark.read.csv(...).rdd, schemaFor[MySchema].dataType.asInstanceOf[StructType] ).as[MySchema] 字段。

如果没有可空的架构是一个很难的要求,你可以尝试:

null

此方法仅在您知道数据实际上null空闲时才有效。任何SELECT DISTINCT pegawai.NIP_BARU, pegawai.NAMA_PEGAWAI, pegawai.KODE_PANGKAT_TERAKHIR,pangkat_golongan.NAMA_PANGKAT, pangkat_golongan.GOLONGAN, pegawai.MASA_KERJA_THN_AKHIR, gaji.MASA_KERJA_GOLONGAN, gaji.NOMINAL_GAJI,CASE PEGAWAI.TMT_GOL WHEN (pangkat_golongan.JENIS_GOLONGAN = '2') AND pegawai.MASA_KERJA_THN_AKHIR MOD 2 = 0 AND 12 - pegawai.MASA_KERJA_BLN_AKHIR + MONTH(pegawai.TMT_GOL)> 12 THEN 2 + YEAR(pegawai.TMT_GOL) WHEN (pangkat_golongan.JENIS_GOLONGAN = '1') AND pegawai.MASA_KERJA_THN_AKHIR MOD 2 = 1 AND 12 - pegawai.MASA_KERJA_BLN_AKHIR + MONTH(pegawai.TMT_GOL)> 12 THEN 2 + YEAR(pegawai.TMT_GOL) WHEN (pangkat_golongan.JENIS_GOLONGAN = '1') AND pegawai.MASA_KERJA_THN_AKHIR MOD 2 = 0 AND 12 - pegawai.MASA_KERJA_BLN_AKHIR + MONTH(pegawai.TMT_GOL)< 12 THEN YEAR(pegawai.TMT_GOL) WHEN (pangkat_golongan.JENIS_GOLONGAN = '2') AND pegawai.MASA_KERJA_THN_AKHIR MOD 2 = 1 AND 12 - pegawai.MASA_KERJA_BLN_AKHIR + MONTH(pegawai.TMT_GOL)< 12 THEN YEAR(pegawai.TMT_GOL) ELSE 1 + YEAR(pegawai.TMT_GOL) END AS TAHUN_HITUNG,CASE PEGAWAI.MASA_KERJA_BLN_AKHIR WHEN (12 -(pegawai.MASA_KERJA_BLN_AKHIR) + MONTH(pegawai.TMT_GOL) > 12) THEN (12 - (pegawai.MASA_KERJA_BLN_AKHIR) + MONTH(pegawai.TMT_GOL) - 12) ELSE (12 - (pegawai.MASA_KERJA_BLN_AKHIR) + MONTH(pegawai.TMT_GOL)) END AS BULAN_HITUNG, STR_TO_DATE(TAHUN_HITUNG, BULAN_HITUNG, '01','%Y-%m-%d') AS TMT_HITUNG FROM pegawai, gaji, pangkat_golongan WHERE gaji.KODE_GOLONGAN = pegawai.KODE_PANGKAT_TERAKHIR AND gaji.MASA_KERJA_GOLONGAN = pegawai.MASA_KERJA_THN_AKHIR AND pangkat_golongan.KODE_PANGKAT = pegawai.KODE_PANGKAT_TERAKHIR; 值都会导致运行时异常。