将大量JSON文件读入Spark Dataframe

时间:2016-12-09 19:31:37

标签: json scala apache-spark dataframe

我有一个大的嵌套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的某些行包含前面提到的新字段...因此需要稍后填写。

2 个答案:

答案 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)