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

Scala 列出和计算不同特定单词的有效方法,scala,apache-spark,bigdata,distributed-computing,Scala,Apache Spark,Bigdata,Distributed Computing,从一个简单的例子中,我想数一数并列出不同的单词: 从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

从一个简单的例子中,我想数一数并列出不同的单词:

  • 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的交点:


假设我在这里处理一组非常庞大的文件(开始时使用
parallelize
)。给定的方法是否合适?如果没有,我如何改进它?例如,在沿袭中只调用一次
p(w)
,减少阶段、迭代次数等。

如评论中所述,我对代码进行了修改,使用模式匹配来查找以“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的单词,甚至不包含以…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字:

245: List(p-:planlose, p-:patriarchal:, p-:plausiblen, p-:philosophische, p-:patriarchalen, p-:philosophischen, p-:platonischen, ...
运行时间,在2x2核心笔记本电脑上运行约2秒,配备约5y'o SSD,以比较速度

算法的Big-O表示法不属于我的专业领域,它需要研究不同的库方法,尤其是“replaceAll”来给出结果,因此在您自己的数据上测试它并比较裸结果可能更实际

我想,对于大量的文本,它们应该是可以按书并行的,并且每个文本都是彼此独立的,所以如果你用一个典型的输入大小来衡量,我想如果它们是同基因大小的,它将是线性可伸缩的,与文本的数量成正比

时间:

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
计数,因此它不同于拆分行的方式,主要是标点符号

在我看来,第二张平面图是不必要的。您可以在平面映射文件的线条时执行相同的处理
countByKey
实际上不需要洗牌。我的直觉告诉我你可能过早地优化了。在此之前,我有一个版本,没有第二个
flatMap
,但后来我开始做一些事情来翻转键值,因为我不知道一个值的
countByKey
的替代方法。所以我想要么我可以按
(分区,单词)
的值分组,要么我可以按
(单词,分区)
的值计数,或者我目前没有看到的任何其他方法来删除第二个
平面图。关于优化,这个问题目前更多的是某种培训,以便更熟悉spark^^。谢谢你关于countByKey的提示,我将更新我的问题@hoyland看来,
countByKey
是一个返回
HashMap
的操作(请参阅)。所以现在我想说这确实需要一次洗牌。你怎么认为?这将是一个更好的选择,因为它提供了另一个
RDD
而不是
HashMap
?受这篇文章和代码的启发,我尝试在我的机器上运行它,使用书本大小的文本,416行,70581个单词,501661个字节/字符。但我不知道sc是什么,而是使用Source.fromFile。从那里开始,需要进行一些级联修改,并生成aioor-ex。因此,我编写了自己的方法,使用正则表达式。在2x2core 5y'o笔记本电脑上以2.2秒的速度运行,配有旧SSD,无需进行性能调整,只需在末尾调用,且没有“toLowerCase”。只有scala-2.11和一个导入:scala.io.\。如果你感兴趣的话,我可以在回答中发布代码。结果是(P | P)-(P)-Rest。它不需要洗牌,因为您不需要先按键排列数据——您可以对每个执行器按键计数,然后聚合计数。(文档“背景”部分的最后一句提到了这一点,尽管有点间接。)
countByKey
就是您在这里想要的——如果您使用
reduceByKey
,您将进行洗牌(另一个
byKey
s进行洗牌),然后只收集
RDD
,以获得相同的结果。如果密钥数量非常大,或者您需要结果作为RDD,那么这将是正确的选择。谢谢!你能分享文本语料库的链接吗?哦,我现在意识到,你没有使用ApacheSpark!这是我问题的一个重要部分。检查标签;)