列出和计算不同特定单词的有效方法

时间:2018-02-24 22:16:26

标签: scala apache-spark bigdata distributed-computing

从简单的text file我想要计算并列出不同的词语:

  • p(A)
  • 开头
  • p(B)
  • 结束
  • p(A∩B)
  • 开头和结尾

因此,需要A,B,A∩B的基数以及A∩B中所有项目的列表。

这是我的解决方案;请注意,我不区分大写和小写,解析也非常简单:

val source = "http://www.gutenberg.org/files/100/100-0.txt"

def p(w:String) : Int = {
    (if (w.charAt(0) == 'p') 1 else 0) +
        (if (w.charAt(w.length - 1) == 'p') 2 else 0)
}

sc.addFile(source)

val r = sc.textFile(
    org.apache.spark.SparkFiles.get(source.split("/").last)
).flatMap(
    line => line.split("[\\s]").map(
         word => word.toLowerCase()
            .replaceAll("[^\\p{IsAlphabetic}\\p{IsDigit}']", "")
    ).filter(w => !w.isEmpty && p(w) > 0)
).distinct().flatMap(w => {
    val k = p(w)

    if (k == 3) {
        Seq((1, w), (2, w), (3, w))
    } else {
        Seq((k, w))
    }
})

r.countByKey().foreach(println)
r.filter(t => t._1 == 3).map(t => t._2).foreach(println)

如果我错了,请纠正我,但我认为我在这里使用了一个广泛的转换(distinct)和一个动作(countByKey)。因此,对于基数,我应该有三个阶段,而对于A和B的交叉项,我应该有两个阶段:

Lineage for the cardinality Lineage for the items of the intersection

假设我会在这里处理一组非常庞大的文件(开头是parallelize)。给定的方法是否合适?如果不是我怎么能改善它?例如。在谱系中仅调用p(w)一次,减少阶段,迭代等。

1 个答案:

答案 0 :(得分:0)

正如评论中所提到的,我使用代码进行了变体,该代码使用模式匹配来查找单词,从' p'开头或结尾,或两者。

import scala.io._

object Pworp {
    val r = (Source.fromFile ("./dialektik-der-aufklaerung.txt").getLines.filter (_.size > 1).flatMap (
          line => line.split ("""[\]\[ ,.?!»«0-9-]""").filter (_.size > 2).map (_.replaceAll ("^(P|p)?(.*)(p)?$", "$1-$3:$1$2$3")))).toList.groupBy (s => (s(0),s(1))).toList

    ((0 to r.size-1).map (i => r(i)._1 match {
            case (('P'|'p'), 'p') => Some (r(i)._2.distinct)
            case (('P'|'p'), '-') => Some (r(i)._2.distinct)
            case (   '-'   , 'p') => Some (r(i)._2.distinct)
            case (   '-'   , ':') => None // Some (r(i)._2.distinct) -- too many
            case _  => None // Shouldn't happen, but happens, if the 
                            // split-expression is bogus
        })).flatten.map (ri => println (s"${ri.distinct.size}: ${ri.distinct}\n"))

    def main (args: Array[String]) {
        val ex = Pworp
    }
}

结果中不包含P .... p或p .... p形式的单词,甚至不包含以...结尾的单词。现在,写一下,我发现它有点可疑并用grep做了一个测试,在这里我发现" Kyklop"甚至" Prinzip"。所以某处必定存在错误。也许有人看到它。 它是一个德国文本,所以与莎士比亚不同的P字分布并不常见。

我最终获得了283个P字:

283: List(P-:Philosophical, P-:Programm, P-:Philosophie, P-:Prahlerei, P-:Praxis, P-:Prinzips, P-:Persephone, P-:Prinzipien, P-:Platons ...

和245 p-Words:

245: List(p-:planlose, p-:patriarchal:, p-:plausiblen, p-:philosophische, p-:patriarchalen, p-:philosophischen, p-:platonischen, ...

在具有大约5英寸固态硬盘的2x2核心笔记本电脑上运行时间约为2秒,以比较速度。

算法的大O符号并不属于我的专业领域,需要研究不同的库方法,尤其是“替换所有”库。给出一个结果,所以在你自己的数据上测试它并比较一下结果可能更实际。

我想,对于大量的短语,它们应该可以在逐个书籍的基础上进行并行化,并且每个文本都非常独立,所以如果你使用典型的输入大小进行测量,我猜它将是线性可扩展的如果它们具有同质大小,则为文本数量。

时间安排:

scalac shakesp.scala && /usr/bin/time scala -cp .:$CLASSPATH Pworp
2.10user 0.24system 0:01.13elapsed 207%CPU (0avgtext+0avgdata 92288maxresident)k
24inputs+64outputs (0major+18144minor)pagefaults 0swaps

输入数据是416行,70581个字,501661个字节/字符,用wc计算,因此它与方式不同,行被分割,主要是标点符号。