我有一个大的嵌套NDJ(新行分隔的JSON)文件,我需要将其读入单个spark数据帧并保存到镶木地板。在尝试呈现模式时,我使用此函数:
def flattenSchema(schema: StructType, prefix: String = null) : Array[Column] = {
schema.fields.flatMap(f => {
val colName = if (prefix == null) f.name else (prefix + "." + f.name)
f.dataType match {
case st: StructType => flattenSchema(st, colName)
case _ => Array(col(colName))
}
})
}
通过
读取返回的数据框上的
val df = sqlCtx.read.json(sparkContext.wholeTextFiles(path).values)
我还将其切换为val df = spark.read.json(path)
,这样只适用于NDJ而不是多行JSON - 同样的错误。
这会导致工作人员出现内存不足错误
java.lang.OutOfMemoryError: Java heap space
。
我已经改变了jvm内存选项和spark执行器/驱动程序选项无济于事
有没有办法流式传输文件,展平架构,并逐步添加到数据框? JSON的某些行包含前面提到的新字段...因此需要稍后填写。
答案 0 :(得分:2)
没有解决方法。问题在于JVM对象限制。我最终使用了scala json解析器并手动构建了数据帧。
答案 1 :(得分:0)
您可以通过多种方式实现这一目标。
首先,在阅读时,您可以提供数据帧的架构来读取json,或者您可以允许spark自己推断架构。
一旦json处于数据框架中,您可以按照以下方法展平它。
一个。在数据框架上使用explode()来展平它。 湾使用spark sql并使用访问嵌套字段。运营商。您可以找到示例here
最后,如果要向dataframe添加新列 一个。第一个选项,使用withColumn()是一种方法。但是,对于添加的每个新列和整个数据集,都将执行此操作。 湾使用sql从现有生成新的数据帧 - 这可能是最简单的 C。最后,使用map,然后访问元素,获取旧模式,添加新值,创建新模式并最终获得新df - 如下所示
一个withColumn将适用于整个rdd。因此,对于要添加的每个列,通常使用该方法并不是一个好习惯。有一种方法可以在map函数中处理列及其数据。由于一个map函数在这里完成工作,因此添加新列及其数据的代码将并行完成。
一个。您可以根据计算收集新值
湾将这些新列值添加到主rdd,如下所示
val newColumns: Seq[Any] = Seq(newcol1,newcol2)
Row.fromSeq(row.toSeq.init ++ newColumns)
这里的row是map方法中行的引用
℃。创建新架构,如下所示
val newColumnsStructType = StructType{Seq(new StructField("newcolName1",IntegerType),new StructField("newColName2", IntegerType))
d。添加到旧架构
val newSchema = StructType(mainDataFrame.schema.init ++ newColumnsStructType)
即使用新列创建新数据框
val newDataFrame = sqlContext.createDataFrame(newRDD, newSchema)