我们正在努力将Spark DataFrame转换为具有动态列的case类的DataSet。每个用例的起点都是DataFrame,如下所示:
root
|-- id: string (nullable = true)
|-- time: long (nullable = true)
|-- c: struct (nullable = true)
| |-- d: long (nullable = true)
| |-- e: string (nullable = true)
| |-- f: array (nullable = true)
| | |-- element: struct (containsNull = true)
| | | |-- g: long (nullable = true)
|-- x: string (nullable = true)
|-- y: string (nullable = true)
所有这些DataFrame都有id
和time
列。其他列(c
,x
,y
)是可配置的,可以根据计数的每个用例进行更改(在某些DataFrame中,我们在id
旁边最多有10列和time
)并输入。
为了简化这些事件的复杂处理,我们希望将每一行转换为scala案例类,如下所示:
case class Event(id: String, time: Long, payload: Seq[Any])
或:
case class Event(id: String, time: Long, payload: Map[String, Any]) // payload: <column-name>/<value>
可悲的是,df.as[Event]
无法开箱即用。任何想法,如何做到这一点?
为每个用例编写案例类不是一种选择,因为我们只想通过YAML文件配置作业,而不想为每个新用例调整代码。我们需要更通用的东西! (我考虑过在运行时生成一个case类,但这很复杂......)
感谢您的帮助!
更新 - 解决方案
我们现在想出了以下解决方案:
case class Event(id:String,time:Long,payload:Seq [Array [Byte]])
并在kryo
:
val idIndex = df.columns.indexOf("id")
val timeIndex = df.columns.indexOf("time")
val otherColumns = List.range(0, df.columns.length).filterNot(i => i == idIndex && i == timeIndex)
val kryo = new Kryo()
val ds = df.map(row => {
Event(
row.getAs[String](idIndex),
row.getAs[Long](timeIndex),
otherColumns.map(index => {
val output = new Output(192, 8192)
kryo.writeObject(output, row.get(index))
output.getBuffer
})
)
})
ds.printSchema()
谢谢你的帮助。