如何将mapPartitions的Iterator [String]结果写入一个文件?

时间:2016-04-02 17:35:09

标签: scala apache-spark

我是Spark Scala的新手,这就是为什么我很难完成这个工作的原因。

我打算做的是使用Spark预先处理我的数据与Stanford CoreNLP。据我所知,我必须使用mapPartitions,以便按照this thread中的建议为每个分区设置一个StanfordCoreNLP个实例。但是,我缺乏知识/理解如何从这里开始。

最后,我想在这些数据上训练单词向量,但是现在我很乐意找到如何从这里获取处理过的数据并将其写入另一个文件。

这是我到目前为止所得到的:

import java.util.Properties

import com.google.gson.Gson
import edu.stanford.nlp.ling.CoreAnnotations.{LemmaAnnotation, SentencesAnnotation, TokensAnnotation}
import edu.stanford.nlp.pipeline.{Annotation, StanfordCoreNLP}
import edu.stanford.nlp.util.CoreMap
import masterthesis.code.wordvectors.Review
import org.apache.spark.{SparkConf, SparkContext}

import scala.collection.JavaConversions._

object ReviewPreprocessing {

  def main(args: Array[String]) {

    val resourceUrl = getClass.getResource("amazon-reviews/reviews_Electronics.json")
    val file = sc.textFile(resourceUrl.getPath)

    val linesPerPartition = file.mapPartitions( lineIterator => {

      val props = new Properties()
      props.put("annotators", "tokenize, ssplit, pos, lemma")

      val sentencesAsTextList : List[String] = List()
      val pipeline = new StanfordCoreNLP(props)
      val gson = new Gson()

      while(lineIterator.hasNext) {

        val line = lineIterator.next
        val review = gson.fromJson(line, classOf[Review])
        val doc = new Annotation(review.getReviewText)

        pipeline.annotate(doc)

        val sentences : java.util.List[CoreMap] = doc.get(classOf[SentencesAnnotation])
        val sb = new StringBuilder();

        sentences.foreach( sentence => {
          val tokens = sentence.get(classOf[TokensAnnotation])
          tokens.foreach( token => {
            sb.append(token.get(classOf[LemmaAnnotation]))
            sb.append(" ")
          })
        })
        sb.setLength(sb.length - 1)
        sentencesAsTextList.add(sb.toString)
      }

      sentencesAsTextList.iterator
    })       

    System.exit(0)
  }

}

我将如何将此结果写入一个文件?这里的顺序并不重要 - 我想无论如何这个顺序都会丢失。

1 个答案:

答案 0 :(得分:1)

如果您在saveAsTextFile上使用RDD,您最终会拥有与您拥有的分区一样多的输出文件。为了只有一个,您可以将所有内容合并到一个分区,如

sc.textFile("/path/to/file")
  .mapPartitions(someFunc())
  .coalesce(1)
  .saveAsTextFile("/path/to/another/file")

或者(只是为了好玩)你可以逐个获取驱动程序的所有分区并自己保存所有数据。

val it = sc.textFile("/path/to/file")
  .mapPartitions(someFunc())
  .toLocalIterator

while(it.hasNext) {
  writeToFile(it.next())
}