我从Avaf(序列化器和反序列化器)获取kafka主题的推文。 然后我创建了一个Spark消费者,它在RDD [GenericRecord]的Dstream中提取推文。 现在我想将每个rdd转换为数据帧,以通过SQL分析这些推文。 任何将RDD [GenericRecord]转换为数据帧的解决方案吗?
答案 0 :(得分:7)
我花了一些时间尝试完成这项工作(特别是如何正确地反序列化数据,但看起来你已经覆盖了这个)...更新
//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>
您需要根据自己的需要更改火花版本。
更新:上面的代码仅适用于平面 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)
答案 1 :(得分:0)
即使这样的事情对你有帮助,
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"
希望这有帮助。干杯
答案 2 :(得分:0)
https://stackoverflow.com/a/48828303/5957143和https://stackoverflow.com/a/47267060/5957143的组合对我有用。
我使用以下内容创建MySchemaConversions
c:\Users\bharat.c.rupare.> call c:\Users\bharat.c.ruparel\AppData\Local\Continuum\anaconda3\Scripts\activate.bat
然后我用
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]
}
// unionedResultRdd是unionRDD [GenericRecord]
val myAvroType = SchemaConverters.toSqlType(schema).dataType
val myAvroRecordConverter = MySchemaConversions.createConverterToSQL(schema, myAvroType)
在对象MyObject中使用myConverter的优点是您不会遇到序列化问题(java.io.NotSerializableException)。
var rowRDD = unionedResultRdd.map(record => MyObject.myConverter(record, myAvroRecordConverter))
val df = sparkSession.createDataFrame(rowRDD , myAvroType.asInstanceOf[StructType])
答案 3 :(得分:-3)
您可以使用SQLContext对象中提供的createDataFrame(rowRDD:RDD [Row],schema:StructType)。转换旧DataFrame的RDD的示例:
import sqlContext.implicits.
val rdd = oldDF.rdd
val newDF = oldDF.sqlContext.createDataFrame(rdd, oldDF.schema)
请注意,无需显式设置任何架构列。我们重用旧的DF架构,它是StructType类,可以很容易地扩展。但是,这种方法有时是不可能的,在某些情况下效率可能低于第一种方法。