如何在Spark中展平元组?

时间:2017-03-21 15:44:42

标签: scala apache-spark rdd

我正在寻找扁平的元组RDD(使用无操作映射),但是我遇到了类型错误:

val fromTuples = sc.parallelize( List((1,"a"), (2, "b"), (3, "c")) )
val flattened = fromTuples.flatMap(x => x)
println(flattened.collect().toNiceString)

给出

  

错误:类型不匹配;

     

发现:(Int,String)    必需:TraversableOnce [?]

     

val flattened = fromMap.flatMap(x => x)

ListArray的等效列表可以正常工作,例如:

val fromList = sc.parallelize(List(List(1, 2), List(3, 4)))
val flattened = fromList.flatMap(x => x)
println(flattened.collect().toNiceString)

Scala可以处理这个吗?如果没有,为什么不呢?

5 个答案:

答案 0 :(得分:8)

元组不是收藏品。与Python不同,其中元组本质上只是一个不可变列表,Scala中的元组更像是一个类(或更像是一个Python namedtuple)。你不能压扁"一个元组,因为它是一组不同的领域。

可以通过在其上调用.productIterator将元组转换为可迭代的元素,但是你得到的是Iterable[Any]。你当然可以压扁这样的东西,但是你已经失去了所有编译时类型的保护。 (大多数Scala程序员对类型为Any的集合的想法感到不寒而栗。)

答案 1 :(得分:3)

没有很好的方法,但你可以用这种方法坚持类型的安全:

val fromTuples = session.sparkContext.parallelize(List((1, "a"), (2, "b"), (3, "c")))
val flattened = fromTuples.flatMap(t => Seq(t._1, t._2))
println(flattened.collect().mkString)

flatten的类型将是元组中所有类型的父级的RDD。在这种情况下,是Any,但如果列表为List(("1", "a"), ("2", "b")),则会保留String类型。

答案 2 :(得分:1)

  val fromTuples = sc.parallelize(List((1, "a"), (2, "b"), (3, "c")))
  val flattened = fromTuples.flatMap(x => Array(x))
  flattened.collect()

您的错误原因是

  

flatMap(func)与map类似,但每个输入项可以映射到0   或更多输出项(因此func应返回Seq而不是单个   项)。

答案 3 :(得分:1)

来自Lyuben的comment,这实际上可以偷偷摸摸地完成:

sc.parallelize(List(("a", 1), ("c", 2), ("e", 4))).flatMap(_.productIterator).collect()

尊敬他。 (虽然为Brian notes,这将放弃类型安全。)

答案 4 :(得分:1)

正如其他人所说,没有一种很好的方法可以做到这一点,特别是在类型安全方面。

但是,如果您只想以漂亮的平面格式打印RDD,则只需映射RDD并使用mkString

scala> val myRDD = sc.parallelize( List((1,"a"), (2, "b"), (3, "c")) )
myRDD: org.apache.spark.rdd.RDD[(Int, String)] = ParallelCollectionRDD[3] at parallelize at <console>:24

scala> myRDD.map{case (a,b) => s"$a,$b"}.collect.mkString(",")
res0: String = 1,a,2,b,3,c