从Spark写入时,避免丢失分区数据的数据类型

时间:2017-10-10 02:55:10

标签: apache-spark spark-dataframe parquet

我有一个如下的数据框。

itemName, itemCategory
Name1, C0
Name2, C1
Name3, C0

我想将此数据框保存为分区镶木地板文件:

df.write.mode("overwrite").partitionBy("itemCategory").parquet(path)

对于这个数据帧,当我读回数据时,它将具有String itemCategory的数据类型。

但有时,我的其他租户的数据框如下所示。

itemName, itemCategory
Name1, 0
Name2, 1
Name3, 0

在这种情况下,在作为分区写入后,当回读时,结果数据帧将具有Int,数据类型为itemCategory

Parquet文件具有描述数据类型的元数据。如何指定分区的数据类型,以便将其作为String而不是Int?

读回

3 个答案:

答案 0 :(得分:0)

当您按itemCategory列进行分区时,此数据将存储在文件结构中,而不是实际的csv文件中。 Spark根据值推断数据类型,如果所有值都是整数,则列类型将为int。

一个简单的解决方案是在读取数据后将列转换为StringType

import spark.implicits._
df.withColumn("itemCategory", $"itemCategory".cast(StringType))

另一种选择是复制列本身。然后,其中一列将用于分区,因此将保存在文件结构中。但是,其他重复列将正常保存在镶木地板文件中。要复制,只需使用:

df.withColumn("itemCategoryCopy", $"itemCategory")

答案 1 :(得分:0)

如果将“ spark.sql.sources.partitionColumnTypeInference.enabled”设置为“ false”,spark将推断所有分区列为字符串。

在spark 2.0或更高版本中,您可以这样设置:

spark.conf.set("spark.sql.sources.partitionColumnTypeInference.enabled", "false")

在1.6中,像这样:

sqlContext.setConf("spark.sql.sources.partitionColumnTypeInference.enabled", "false")

缺点是,每次读取数据时都必须这样做,但至少可以正常工作。

答案 2 :(得分:0)

使用架构读取它:

import spark.implicits._
val path = "/tmp/test/input"
val source = Seq(("Name1", "0"), ("Name2", "1"), ("Name3", "0")).toDF("itemName", "itemCategory")
source.write.partitionBy("itemCategory").parquet(path)
spark.read.schema(source.schema).parquet(path).printSchema() 
// will print 
// root
// |-- itemName: string (nullable = true)
// |-- itemCategory: string (nullable = true)

请参见https://www.zepl.com/viewer/notebooks/bm90ZTovL2R2aXJ0ekBnbWFpbC5jb20vMzEzZGE2ZmZjZjY0NGRiZjk2MzdlZDE4NjEzOWJlZWYvbm90ZS5qc29u