多行Spark滑动窗口

时间:2017-02-15 18:48:09

标签: scala apache-spark

我正在学习使用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行以完成一个窗口。

我有两个想法:

  1. 从下一行添加k-1个字符:
  2. ATATATAC
    ACATATAT
    ATATAT
    

    我尝试使用Spark SQL lead()函数,但是当我尝试执行flatMap时,我得到了WindowSpec的NotSerializableException。还有其他方法可以引用下一行吗?我需要编写自定义输入格式吗?

    1. 将整个序列作为一行读取(或在阅读后连接行):
    2. ATATATACATATATATAT
      

      有没有办法读取多行,以便它们可以作为一个处理?如果是这样,它是否都需要适合单个机器的存储器?

      我意识到这些中的任何一个都可以作为预处理步骤来完成。我想知道最好的方法是在Spark中做到这一点。一旦我用这些格式之一,我知道如何做其余的,但我被困在这里。

1 个答案:

答案 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)