在pyspark中写入镶木地板时忽略缺少的值

时间:2018-04-02 12:16:29

标签: apache-spark pyspark parquet

我需要转换镶木地板文件的内部结构。

目前我有一个存储数组或数组的字段。我打算把它变成一个结构数组。

所以从这个:

root
  -array
     -array

我想得到这个:

root
  -array
     -struct

我正在按以下方式执行转换:

我为新结构定义了一个模式:

newtype = ArrayType(StructType(
        [
            StructField("one", FloatType()),
            StructField("two", FloatType()),
            StructField("three", FloatType()),
            StructField("four", FloatType()),
            StructField("five", FloatType())
        ]))

我将'空'udf应用于列。有趣的是我指定了udf的模式。

def convert(arr):
   return arr

df = spark.read.parquet("....")
spark.udf.register(name="convert", f=convert, returnType=newtype)
df = df.withColumn("col", expr("convert(col)"))

最后我把它写回实木复合地板。

我得到的问题是:

  

输入行没有预期的值所需的值   架构。需要5个字段,同时提供3个值。

确实如此。一些阵列曾经有3个值。稍后会添加更多值,因此较新的数组有5个值。

为什么会发生这种情况?我将这些字段定义为可以为空,所以我希望这可以工作。 我的选择是什么?

1 个答案:

答案 0 :(得分:3)

 

将数据转换为新的结构模式时,必须为每个字段提供一个值。如果你不提供它们,Spark并不想假设要放入什么值。如果长度错误,只需为剩余值提供None。你的转换函数看起来也不像处理嵌套数组。这是一个工作示例,其中convert已更新为pad with pad' s。

from pyspark.sql.types import *
from pyspark.sql.functions import * 

old_type = StructType([
    StructField("col", ArrayType(ArrayType(FloatType())))
])

new_type = ArrayType(StructType([
    StructField("one", FloatType()),
    StructField("two", FloatType()),
    StructField("three", FloatType()),
    StructField("four", FloatType()),
    StructField("five", FloatType())
]))

data = [
    ([[1., 2., 3.], [1., 2., 3., 4., 5.]],)
]


rdd = spark.sparkContext.parallelize(data)
df = sqlContext.createDataFrame(rdd, old_type)


def convert(arr):
    vals = []
    for v in arr:
        padding = [None] * (5 - len(v))
        vals.append(v + padding)
    return vals

spark.udf.register(name="convert", f=convert, returnType=new_type)
df = df.withColumn("col", expr("convert(col)"))
df.show(10, False)