我正在学习使用Scala的Apache Spark,并希望用它来处理跨越多行的DNA数据集:
ATGTAT
ACATAT
ATATAT
我想将此映射为固定大小 k 的组并计算组。因此,对于k = 3,我们将使用接下来的两个字符获得每个字符的组:
ATG TGT GTA TAT ATA TAC
ACA CAT ATA TAT ATA TAT
ATA TAT ATA TAT
...然后计算群组(如字数):
(ATA,5), (TAT,5), (TAC,1), (ACA,1), (CAT,1), (ATG,1), (TGT,1), (GTA,1)
问题在于“单词”跨越多行,上例中的TAC
也是如此。它跨越了换行。我不想只计算每一行中的组,但是在整个文件中,忽略行结尾。
换句话说,我想将整个序列作为宽度 k 的滑动窗口处理整个文件,就像没有换行符一样。当我到达一行时,问题是向前(或返回)到下一个RDD行以完成一个窗口。
我有两个想法:
ATATATAC ACATATAT ATATAT
我尝试使用Spark SQL lead()函数,但是当我尝试执行flatMap时,我得到了WindowSpec的NotSerializableException。还有其他方法可以引用下一行吗?我需要编写自定义输入格式吗?
ATATATACATATATATAT
有没有办法读取多行,以便它们可以作为一个处理?如果是这样,它是否都需要适合单个机器的存储器?
我意识到这些中的任何一个都可以作为预处理步骤来完成。我想知道最好的方法是在Spark中做到这一点。一旦我用这些格式之一,我知道如何做其余的,但我被困在这里。
答案 0 :(得分:1)
你可以创建单个字符串的rdd,而不是将它们作为一行连接,因为这会使结果成为一个无法分发的字符串:
val rdd = sc.textFile("gene.txt")
// rdd: org.apache.spark.rdd.RDD[String] = gene.txt MapPartitionsRDD[4] at textFile at <console>:24
只需使用flatMap
将行拆分为字符列表:
rdd.flatMap(_.split("")).collect
// res4: Array[String] = Array(A, T, G, T, A, T, A, C, A, T, A, T, A, T, A, T, A, T)
从this answer借来的更完整的解决方案:
val rdd = sc.textFile("gene.txt")
// create the sliding 3 grams for each partition and record the edges
val rdd1 = rdd.flatMap(_.split("")).mapPartitionsWithIndex((i, iter) => {
val slideList = iter.toList.sliding(3).toList
Iterator((slideList, (slideList.head, slideList.last)))
})
// collect the edge values, concatenate edges from adjacent partitions and broadcast it
val edgeValues = rdd1.values.collect
val sewedEdges = edgeValues zip edgeValues.tail map { case (x, y) => {
(x._2 ++ y._1).drop(1).dropRight(1).sliding(3).toList
}}
val sewedEdgesMap = sc.broadcast(
(0 until rdd1.partitions.size) zip sewedEdges toMap
)
// sew the edge values back to the result
rdd1.keys.mapPartitionsWithIndex((i, iter) => iter ++ List(sewedEdgesMap.value.getOrElse(i, Nil))).
flatMap(_.map(_ mkString "")).collect
// res54: Array[String] = Array(ATG, TGT, GTA, TAT, ATA, TAC, ACA, CAT, ATA, TAT, ATA, TAT, ATA, TAT, ATA, TAT)