我有一个数据框,我希望按列进行分组,然后将这些组重新转换为具有相同模式的数据框。原因是我希望在组中映射具有签名DataFrame -> String
的函数。以下是我正在尝试的内容:
val df = sc.parallelize(Seq((1,2,3),(1,2,4),(2,3,4))).toDF
val schema = df.schema
val groups = df.rdd.groupBy(x => x(0))
.mapValues(g => sqlContext.createDataFrame(sc.makeRDD(g.toList), schema))
.take(1)
这就是我所希望的:
scala> groups(0)._2.collect
Array[org.apache.spark.sql.Row] = Array([1,2,3], [1,2,4])
但是它没有工作(任务失败了NullPointerException
)...我猜你不能映射一个引用火花上下文的函数,但我不知道还有什么其他的实现这个目标?
答案 0 :(得分:1)
我猜你不能映射一个引用火花上下文的函数
正确 - 您不能在传递给任何Spark的高阶函数的函数内使用Spark的任何上下文对象(或RDD或Dataframe),因为这需要序列化这些对象和将它们发送给执行程序,但它们是故意不可序列化的,因为它没有意义(每个执行程序必须表现得像另一个驱动程序应用程序)。
要实现仅包含一个“组”的数据框,我建议您使用filter
代替groupBy
:您可以先collect
所有组密钥,然后将每个组映射到过滤后的Dataframe:
val df = sc.parallelize(Seq((1,2,3),(1,2,4),(2,3,4))).toDF
df.cache() // EDIT: this might speed this up significantly, as DF will be reused instead of recalculated for each key
val groupKeys: Array[Int] = df.map { case Row(i: Int, _, _) => i }.distinct().collect()
val dfPerKey: Array[DataFrame] = groupKeys.map(k => df.filter($"_1" === k))
dfPerKey.foreach(_.show())
// prints:
// +---+---+---+
// | _1| _2| _3|
// +---+---+---+
// | 1| 2| 3|
// | 1| 2| 4|
// +---+---+---+
//
// +---+---+---+
// | _1| _2| _3|
// +---+---+---+
// | 2| 3| 4|
// +---+---+---+