我的数据集具有以下架构:[String, array[int]]
。现在,我想使用以下架构将其转换为数据集(或Dataframe):[String, int, int, int, ...]
。请注意,array[int]
是动态的,因此对于不同的行可以具有不同的长度。
答案 0 :(得分:3)
问题源于元组(String, Array[Int])
是特定类型的事实,无论数组中有多少元素,它都是相同的类型。
另一方面,元组(String, Int)
是与(String, Int, Int)
不同的类型,它与(String, Int, Int, Int)
不同,依此类推。作为一种强类型语言,Scala不容易允许一种方法将一种类型作为输入,并产生许多可能的,不相关的类型之一作为输出。
也许如果你描述为什么你认为你想要这样做,我们可以为你的情况提供更好的解决方案。
答案 1 :(得分:2)
正如@jwvh建议的那样,您可能无法以类型安全的数据集方式执行此操作。如果你放松类型安全,你可以使用DataFrames来做这件事(假设你的数组不是很疯狂 - 我相信当前列只限于Int.MaxValue列数)。
我们从玩具示例开始:
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()