我正在寻找扁平的元组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)
List
或Array
的等效列表可以正常工作,例如:
val fromList = sc.parallelize(List(List(1, 2), List(3, 4)))
val flattened = fromList.flatMap(x => x)
println(flattened.collect().toNiceString)
Scala可以处理这个吗?如果没有,为什么不呢?
答案 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