[英]How to convert RDD[GenericRecord] to dataframe in scala?
我从Avf(序列化器和反序列化器)的kafka主题获得了推文。 然后,我创建一个Spark使用者,该使用者在RDD [GenericRecord]的Dstream中提取推文。 现在,我想将每个rdd转换为数据帧,以通过SQL分析这些推文。 任何将RDD [GenericRecord]转换为数据帧的解决方案吗?
我花了一些时间试图使它工作(特别是如何正确地反序列化数据,但看起来您已经涵盖了这件事)...更新
//Define function to convert from GenericRecord to Row
def genericRecordToRow(record: GenericRecord, sqlType : SchemaConverters.SchemaType): Row = {
val objectArray = new Array[Any](record.asInstanceOf[GenericRecord].getSchema.getFields.size)
import scala.collection.JavaConversions._
for (field <- record.getSchema.getFields) {
objectArray(field.pos) = record.get(field.pos)
}
new GenericRowWithSchema(objectArray, sqlType.dataType.asInstanceOf[StructType])
}
//Inside your stream foreachRDD
val yourGenericRecordRDD = ...
val schema = new Schema.Parser().parse(...) // your schema
val sqlType = SchemaConverters.toSqlType(new Schema.Parser().parse(strSchema))
var rowRDD = yourGeneircRecordRDD.map(record => genericRecordToRow(record, sqlType))
val df = sqlContext.createDataFrame(rowRDD , sqlType.dataType.asInstanceOf[StructType])
如您所见,我正在使用SchemaConverter从您用于反序列化的架构中获取数据帧结构(使用架构注册表可能会更麻烦)。 为此,您需要以下依赖项
<dependency>
<groupId>com.databricks</groupId>
<artifactId>spark-avro_2.11</artifactId>
<version>3.2.0</version>
</dependency>
您将需要根据自己的需要更改spark版本。
更新:上面的代码仅适用于平面 avro模式。
对于嵌套结构,我使用了一些不同的东西。 您可以复制SchemaConverters类,它必须在com.databricks.spark.avro
内部(它使用了databricks包中的一些受保护的类),也可以尝试使用spark-bigquery依赖项。 默认情况下将无法访问该类,因此您需要在com.databricks.spark.avro
包中创建一个类以访问工厂方法。
package com.databricks.spark.avro
import com.databricks.spark.avro.SchemaConverters.createConverterToSQL
import org.apache.avro.Schema
import org.apache.spark.sql.types.StructType
class SchemaConverterUtils {
def converterSql(schema : Schema, sqlType : StructType) = {
createConverterToSQL(schema, sqlType)
}
}
之后,您应该能够像
val schema = .. // your schema
val sqlType = SchemaConverters.toSqlType(schema).dataType.asInstanceOf[StructType]
....
//inside foreach RDD
var genericRecordRDD = deserializeAvroData(rdd)
///
var converter = SchemaConverterUtils.converterSql(schema, sqlType)
...
val rowRdd = genericRecordRDD.flatMap(record => {
Try(converter(record).asInstanceOf[Row]).toOption
})
//To DataFrame
val df = sqlContext.createDataFrame(rowRdd, sqlType)
即使这样可能对您有帮助,
val stream = ...
val dfStream = stream.transform(rdd:RDD[GenericRecord]=>{
val df = rdd.map(_.toSeq)
.map(seq=> Row.fromSeq(seq))
.toDF(col1,col2, ....)
df
})
我想为您推荐一种替代方法。 使用Spark 2.x,您可以跳过创建DStreams
的整个过程。 相反,您可以通过结构化流媒体执行类似的操作,
val df = ss.readStream
.format("com.databricks.spark.avro")
.load("/path/to/files")
这将为您提供一个可以直接查询的数据框。 在这里, ss
是spark会话的实例。 /path/to/files
是从kafka转储所有avro文件的地方。
PS:您可能需要导入spark-avro
libraryDependencies += "com.databricks" %% "spark-avro" % "4.0.0"
希望这会有所帮助。 干杯
https://stackoverflow.com/a/48828303/5957143和https://stackoverflow.com/a/47267060/5957143的组合适合我。
我使用以下内容创建MySchemaConversions
package com.databricks.spark.avro
import org.apache.avro.Schema
import org.apache.avro.generic.GenericRecord
import org.apache.spark.sql.Row
import org.apache.spark.sql.types.DataType
object MySchemaConversions {
def createConverterToSQL(avroSchema: Schema, sparkSchema: DataType): (GenericRecord) => Row =
SchemaConverters.createConverterToSQL(avroSchema, sparkSchema).asInstanceOf[(GenericRecord) => Row]
}
然后我用
val myAvroType = SchemaConverters.toSqlType(schema).dataType
val myAvroRecordConverter = MySchemaConversions.createConverterToSQL(schema, myAvroType)
// unionedResultRdd为unionRDD [GenericRecord]
var rowRDD = unionedResultRdd.map(record => MyObject.myConverter(record, myAvroRecordConverter))
val df = sparkSession.createDataFrame(rowRDD , myAvroType.asInstanceOf[StructType])
在对象MyObject中使用myConverter的优点是您不会遇到序列化问题(java.io.NotSerializableException)。
object MyObject{
def myConverter(record: GenericRecord,
myAvroRecordConverter: (GenericRecord) => Row): Row =
myAvroRecordConverter.apply(record)
}
您可以使用createDataFrame(rowRDD:RDD [Row],schema:StructType),它在SQLContext对象中可用。 转换旧DataFrame的RDD的示例:
import sqlContext.implicits.
val rdd = oldDF.rdd
val newDF = oldDF.sqlContext.createDataFrame(rdd, oldDF.schema)
请注意,无需显式设置任何架构列。 我们重用了旧的DF的架构,该架构属于StructType类,可以轻松扩展。 但是,这种方法有时是不可能的,并且在某些情况下可能不如第一种方法有效。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.