如何更改数组中的Spark Dataframe列数据类型

时间:2017-01-26 16:08:04

标签: json scala apache-spark spark-dataframe parquet

关于我的一个更大问题,我遇到两个小问题:我想每天读一次JSON数据并将其保存为Parquet,以便以后与数据相关的工作。使用实木复合地板的速度要快得多。但是我坚持的事实是,当阅读那个镶木地板时,Spark总是试图从模式文件中获取模式,或者只从第一个镶木地板文件中获取模式,并假设所有文件的模式都相同。但有些情况下我们在某些专栏中几天没有任何数据。

因此,假设我有一个包含以下架构的数据的JSON文件:

root
 |-- Id: long (nullable = true)    
 |-- People: array (nullable = true)
 |    |-- element: struct (containsNull = true)
 |    |    |-- Name: string (nullable = true)
 |    |    |-- Amount: double (nullable = true)

然后我有另一个JSON文件,其中没有“People”列的数据。因此架构如下:

root
 |-- Id: long (nullable = true)    
 |-- People: array (nullable = true)
 |    |-- element: string (containsNull = true)

当我和read.json一起阅读它们时,Spark浏览所有文件并从这些文件中推断出合并的模式,更具体地说是从第一个文件中推断出来,只是将第二个文件中的行留空,但是架构是正确的。

但是当我单独阅读这些并单独写入镶木地板时,我无法一起阅读它们,因为对于Parquet,架构不匹配而且我收到错误。

我的第一个想法是在文件中读取缺少的数据并通过转换列类型手动更改其架构以匹配第一个架构,但是这个手动转换是错误的,它可能不同步,我甚至都不知道如何将此字符串类型转换为数组或结构类型。

另一个问题是当“Amount”字段只有完整的整数时,Spark会根据需要读取它们但不会加倍。但如果我使用:

val df2 = df.withColumn("People.Amount", col("People.Amount").cast(org.apache.spark.sql.types.ArrayType(org.apache.spark.sql.types.DoubleType,true)))

然后它不会更改原始列的类型,而是添加一个名为People.Amount的新列

1 个答案:

答案 0 :(得分:1)

我认为您可以通过架构合并来解决问题(请参阅文档here)。如果您拥有的第一个镶木地板具有正确的架构,那么您可以执行类似的操作将该架构应用于新的镶板吗?

// Read the partitioned table
val mergedDF = spark.read.option("mergeSchema", "true").parquet("data/test_table")
mergedDF.printSchema()

修改

你说有200多列,你知道它们吗?我看到两种方式前进,可能有很多方法可以实现这一目标。一个是你提前定义你可以看到的所有字段。我过去所做的是创建一个带有单个虚拟记录的json文件,该记录包含我想要的所有字段,并且输入完全符合我的要求。然后,您可以随时在" Monday"的同时加载该记录。或"星期二"数据集并在加载后将其剥离。这可能不是最好的做法,但这就是我绊倒前进的方式。

另一种方法是停止尝试在正确的架构中加载/保存单个数据集,并在加载所有数据后设置架构。听起来不像你想要的路径,但至少你没有遇到这个特定的问题。