Spark:如何使用Scala将数据集[(String,array [int])]转换为Dataset [(String,int,int,int,int,...)]

时间:2017-05-05 05:24:35

标签: scala apache-spark

我的数据集具有以下架构:[String, array[int]]。现在,我想使用以下架构将其转换为数据集(或Dataframe):[String, int, int, int, ...]。请注意,array[int]是动态的,因此对于不同的行可以具有不同的长度。

2 个答案:

答案 0 :(得分:3)

问题源于元组(String, Array[Int])是特定类型的事实,无论数组中有多少元素,它都是相同的类型。

另一方面,元组(String, Int)是与(String, Int, Int)不同的类型,它与(String, Int, Int, Int)不同,依此类推。作为一种强类型语言,Scala不容易允许一种方法将一种类型作为输入,并产生许多可能的,不相关的类型之一作为输出。

也许如果你描述为什么你认为你想要这样做,我们可以为你的情况提供更好的解决方案。

答案 1 :(得分:2)

正如@jwvh建议的那样,您可能无法以类型安全的数据集方式执行此操作。如果你放松类型安全,你可以使用DataFrames来做这件事(假设你的数组不是很疯狂 - 我相信当前列只限于Int.MaxValue列数)。

以下是在Spark 2.0.2上使用(主要)DataFrames的解决方案:

我们从玩具示例开始:

import org.apache.spark.sql.functions._
import spark.implicits._

val ds = spark.createDataset(("Hello", Array(1,2,3)) :: ("There", Array(1,2,10,11,100))  :: ("A", Array(5,6,7,8)) :: Nil)
// ds: org.apache.spark.sql.Dataset[(String, Array[Int])] = [_1: string, _2: array<int>]

ds.show()
+-----+-------------------+
|   _1|                 _2|
+-----+-------------------+
|Hello|          [1, 2, 3]|
|There|[1, 2, 10, 11, 100]|
|    A|       [5, 6, 7, 8]|
+-----+-------------------+

接下来我们计算我们所拥有的数组的最大长度(我们希望在这里不要疯狂):

val maxLen = ds.select(max(size($"_2")).as[Long]).collect().head

接下来,我们想要一个函数在特定索引处选择数组的元素。我们将数组选择函数表示为UDF:

val u = udf((a: Seq[Int], i: Int) => if(a.size <= i) null.asInstanceOf[Int] else a(i))

现在我们创建我们想要生成的所有列:

val columns = ds.col("_1") +: (for(i <- 0 until maxLen.toInt ) yield u(ds.col("_2"), lit(i)).as(s"a[$i]"))

然后希望我们完成了:

ds.select(columns:_*).show()

+-----+----+----+----+----+----+
|   _1|a[0]|a[1]|a[2]|a[3]|a[4]|
+-----+----+----+----+----+----+
|Hello|   1|   2|   3|   0|   0|
|There|   1|   2|  10|  11| 100|
|    A|   5|   6|   7|   8|   0|
+-----+----+----+----+----+----+

以下是复制粘贴的完整代码

import org.apache.spark.sql.functions._
import spark.implicits._

val ds = spark.createDataset(("Hello", Array(1,2,3)) :: ("There", Array(1,2,10,11,100))  :: ("A", Array(5,6,7,8)) :: Nil)

val maxLen = ds.select(max(size($"_2")).as[Long]).collect().head

val u = udf((a: Seq[Int], i: Int) => if(a.size <= i) null.asInstanceOf[Int] else a(i))

val columns = ds.col("_1") +: (for(i <- 0 until maxLen.toInt ) yield u(ds.col("_2"), lit(i)).as(s"a[$i]"))

ds.select(columns:_*).show()