繁体   English   中英

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

[英]How to extract character n-grams based on a large text

给定一个大文本文件,我想使用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()方法读取该文件。

您可以使用如下函数:

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_

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

注意:由于我们将从每个分区的开头和结尾错过很少的三元组,因此得到的三元组可能不是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)

可能有更短的方法来做到这一点,

假设整个字符串(包括新行)是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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM