如何基于大文本提取字符n-gram

时间:2016-01-25 17:49:07

标签: scala apache-spark

给定一个大文本文件,我想使用Apache Spark提取字符n-gram(并行完成任务)。

示例输入(2行文字): 第1行:( Hello World,它) 第2行:(是美好的一天)

输出n-gram: Hel - ell -llo -lo_ - o_W - _Wo - Wor - orl - rld - ld, - d,_ - ,_ i ​​ - _it - it_ - t_i - _is - ...等等。所以我希望返回值是RDD [String],每个字符串都包含n-gram。

请注意,新行在输出n-gram中被视为空格。我把括号中的每一行都清楚了。另外,要清楚字符串或文本不是RDD中的单个条目。我使用sc.textFile()方法读取文件。

3 个答案:

答案 0 :(得分:1)

您可以使用以下功能:

def n_gram(str:String, n:Int) = (str + " ").sliding(n)

我假设在阅读该行时已删除换行符,因此我添加了一个空格来弥补这一点。另一方面,如果保留换行符,则可以将其定义为:

def n_gram(str:String, n:Int) = str.replace('\n', ' ').sliding(n)

使用您的示例:

println(n_gram("Hello World, it", 3).map(_.replace(' ', '_')).mkString(" - "))

将返回:

Hel - ell - llo - lo_ - o_W - _Wo - Wor - orl - rld - ld, - d,_ - ,_i - _it - it_

答案 1 :(得分:1)

主要思想是获取每个分区中的所有行并将它们组合成一个长字符串。接下来,我们用“_”替换“”并在此字符串上调用滑动以并行创建每个分区的三元组。

注意:由于我们将从每个分区的开头和结尾错过很少的三元组,因此得到的三元组可能不是100%准确。鉴于每个分区的长度可能是几百万个字符,保证的损失应该可以忽略不计。这里的主要好处是每个分区可以并行执行。

以下是一些玩具数据。以下所有内容都可以在任何Spark REPL上执行:

scala> val data = sc.parallelize(Seq("Hello World, it","is a nice day"))
data: org.apache.spark.rdd.RDD[String] = ParallelCollectionRDD[12] 

val trigrams = data.mapPartitions(_.toList.mkString(" ").replace(" ","_").sliding(3))
trigrams: org.apache.spark.rdd.RDD[String] = MapPartitionsRDD[14]

在这里,我将收集三元组以显示它们的样子(如果您的数据集很大,您可能不想这样做)

scala> val asCollected = trigrams.collect
asCollected: Array[String] = Array(Hel, ell, llo, lo_, o_W, _Wo, Wor, orl, rld, ld,, d,_, ,_i, _it, is_, s_a, _a_, a_n, _ni, nic, ice, ce_, e_d, _da, day)

答案 2 :(得分:0)

可能有更短的方法,

假设整个字符串(包括新行)是RDD中的单个条目,从flatMap返回以下内容应该可以得到您想要的结果。

val strings = text.foldLeft(("", List[String]())) {
  case ((s, l), c) =>
    if (s.length < 2) {
      val ns = s + c
      (ns, l)
    } else if (s.length == 2) {
      val ns = s + c
      (ns, ns :: l)
    } else {
      val ns = s.tail + c
      (ns, ns :: l)
    }
}._2