Scala:实现 wordCount 需懂得的基础知识,真不简单

x33g5p2x  于2021-11-21 转载在 Scala  
字(5.1k)|赞(0)|评价(0)|浏览(701)

1、读取文件

  1. // 1、导入包
  2. import scala.io.Source
  3. // 2、按行读取文件
  4. val lines = Source.fromFile("/usr/hadoop/badou/The_Man_of_Property.txt").getLines
  5. -- 会显示:
  6. -- scala.io.BufferedSource = non-empty iterator --这个资源结果是非空的迭代器
  7. --iterator 表示这是一个迭代器
  8. --it.next(): 获取迭代器中下一个元素
  9. --it.hasNext():判断集合中是否还有元素
  10. // 3、toList: 将上面迭代器中放入列表中进行返回
  11. val lines = Source.fromFile("/usr/hadoop/badou/The_Man_of_Property.txt").getLines.toList

1.1 判断数据数量是否一致?

  1. lines.length
  2. // res4: Int = 2866
  3. wc -l The_Man_of_Property.txt

2、序列化、区间类型

  1. val a = Range(0,5) // [0,5) 步长是1
  2. val b = 0 until 5 // [0,5) 步长是1
  3. ---
  4. val c = 1 to 5 // [0,5] 步长是1
  5. val d = 1.to(5) // [0,5] 步长是1
  6. 如何把Range类型转换为List类型?
  7. a.toList
  8. val list1 = (1 to 10).toList

2.1 理解map、Vector

  1. val a = Range(0,5)
  2. // ...immutable.Range = Range(0, 1, 2, 3, 4)
  3. a.map(x=>x*2) // 读取变量a的元素,作为x,再进行操作*2
  4. // Vector(0, 2, 4, 6, 8)
  5. --Vector:可以认为是保存数据的容器,也称为集合
  6. -- 1、创建Vector对象
  7. val v1 =Vector(1,2,3)
  8. -- 获取Vector元素,索引下标从0 开始
  9. println(v1(0))
  10. -- 2Vector 遍历
  11. for(i<- v1) print(i+" ")

2.2 理解 _ ,作用是通配符

  1. (1) _代表集合中每一个元素
  2. a.map(_*2) <==> a.map(x=>x*2)
  3. (2) 获取tuple中的元素
  4. val s = ("hello","badou")
  5. s._1
  6. s._2
  7. (3) 导入所有包
  8. import scala.collection.immutable.xxx 指定具体包
  9. import scala.collection.immutable._ 导入 immutable的全部包
  10. (4)初始化变量
  11. val a=1 val定义的变量不能被修改,而var可以修改
  12. var name:String=_ // name: String = null
  13. var score:Int=_ // score: Int = 0

2.3 理解 split: 以什么为切分条件?

  1. val s = "The Man of Property"
  2. s.split(" ") //切分结果以 Array[String] 存储
  3. // res7: Array[String] = Array(The, Man, of, Property)
  4. -- 结合: map+split
  5. lines.map(x=>x.split(" "))
  6. lines.map(_.split(" "))
  7. // res8: List[Array[String]]

返回的是List (Array(), Array(),…)

我们不想要有Array,将Array进行打平
用 flatten 函数

  1. val s1 = List(Range(0,5), Range(0,5), Range(0,5))
  2. val s2 = s1.flatten // 打平Array
  3. s2: List[Int] = List(0, 1, 2, 3, 4, 0, 1, 2, 3, 4, 0, 1, 2, 3, 4)
  4. --每个元素遍历操作
  5. s2.map(x=>x*2) <==> s2.map(_*2)
  6. --直接针对s1进行处理
  7. s1.map(x=>x.map(x=>x*2))
  8. s1.map(_.map(_*2)) // 先读取x的数据,再对x进行x*2操作
  9. // List(Vector(0, 2, 4, 6, 8), Vector(0, 2, 4, 6, 8), Vector(0, 2, 4, 6, 8))
  10. --将上面Vector进行打散 flatten flatMap
  11. s1.map(_.map(_*2)).flatten
  12. s1.flatMap(_.map(_*2))
  13. --映射到lines
  14. -- map + flatten <==> flatMap
  15. lines.map(x=>x.split(" ")).flatten
  16. lines.flatMap(_.split(" "))

3、进行 MapReduce 的 Map 操作

  1. val r1 = lines.flatMap(x=>x.split(" "))
  2. r1.map((_,1)) // 把r1的结果作为_, 合成(_,1)形式
  3. // 进行Map操作
  4. lines.flatMap(x=>x.split(" ")).map(x=>(x,1))
  5. lines.flatMap(_.split(" ")).map(x=>(x,1))
  6. lines.flatMap(_.split(" ")).map((_,1))

把单词作为key,通过key进行分组groupBy 操作。

  1. lines.flatMap(_.split(" ")).map((_,1)).groupBy(_._1)
  2. --Map(forgotten -> List((forgotten,1), (forgotten,1), (forgotten,1)))
  3. tuple 中(forgotten,1) 获取第一个单词 forgotten 作为key;
  4. 将整个tuple作为value,收集到一个List中;
  5. 这样对应的value:
  6. _1: forgotten
  7. _2: List((forgotten,1), (forgotten,1), (forgotten,1))

统计词频,也就是单词出现的个数,在上面代表的List的大小 == 就是forgotten 出现的次数。

  1. lines.flatMap(_.split(" ")).map((_,1)).groupBy(_._1).map(x=>(x._1, x._2.length))
  2. 等价于:
  3. lines.flatMap(_.split(" ")).map((_,1)).groupBy(_._1).map(x=>(x._1, x._2.size))

3.1 理解数组求和方式

  1. val a1 = List((1,2), (3,4),(5,6))
  2. a1.map(_._2).sum
  3. a1.map(_._2).reduce(_+_)

  1. val l = lines.flatMap(_.split(" ")).map((_,1)).groupBy(_._1)
  2. l.map(x=>(x._1,x._2.map(_._2).sum))
  3. 等价于
  4. l.map(x=>(x._1,x._2.map(_._2).reduce(_+_)))
  5. 等价于
  6. l.mapValues(_.size) // 获取map中的Value进行统计

这样得到的结果就是类似 wordCount。

3.2 对 wordCount 结果获取前10个与排序

  1. scala> val a1=List((3,2),(1,0),(4,4),(5,1))
  2. a1: List[(Int, Int)] = List((3,2), (1,0), (4,4), (5,1))
  3. // _._2 表示按照tuple中第二个元素进行排序
  4. // sortBy():从小到大的排序,默认升序
  5. scala> a1.sortBy(_._2)
  6. res1: List[(Int, Int)] = List((1,0), (5,1), (3,2), (4,4))
  7. // reverse 颠倒结果
  8. scala> a1.sortBy(_._2).reverse
  9. res2: List[(Int, Int)] = List((4,4), (3,2), (5,1), (1,0))
  10. // sortWith(第二个元素>另一个第二个元素)
  11. scala> a1.sortWith(_._2 > _._2)
  12. res3: List[(Int, Int)] = List((4,4), (3,2), (5,1), (1,0))
  13. // slice(0,2) 分片[0-2)
  14. scala> a1.sortWith(_._2 > _._2).slice(0,2)
  15. res4: List[(Int, Int)] = List((4,4), (3,2))

【注意】: 排序,切分是在List类型进行的。

  1. import scala.io.Source
  2. val lines = Source.fromFile("/usr/hadoop/badou/The_Man_of_Property.txt").getLines.toList
  3. val l = lines.flatMap(_.split(" ")).map((_,1)).groupBy(_._1).map((x=>(x._1,x._2.size)))
  4. // l: scala.collection.immutable.Map[String,Int]
  5. -- 这里我们要对变量lMap[String,Int]中的Int进行排序,切分。有:
  6. // 1、转换为List,再进行排序,切分
  7. l.toList.sortBy(_._2)
  8. l.toList.sortBy(_._2).reverse
  9. --方式一:
  10. l.toList.sortBy(_._2).reverse.slice(0,10)
  11. --方式二:
  12. l.toList.sortWith(_._2 > _._2).slice(0,10)
  13. --方式三:
  14. lines.flatMap(_.split(" ")).map((_,1)).groupBy(_._1).mapValues(_.size).toArray.sortWith(_._2 > _._2).slice(0,10)

4、过滤特殊字符(用正则)

在上面我们发现,进行split,map 时候,key是含有特殊字符的,我们要过滤掉它们。

  1. // 定义正则
  2. val p = "[0-9]+".r // 只要数字
  3. val s = "a12!@#$a^&*vbdd12309++9"
  4. p.findAllIn(s).toArray
  5. // res13: Array[String] = Array(12, 12309, 9)
  6. p.findAllIn(s).foreach(x=>println(x))

用 mkString() 可以把结果转为String类型。

  1. p.findAllIn(s).mkString(" ")
  2. // res18: String = 12 12309 9
  3. p.findAllIn(s).mkString("["," ","]")
  4. // res20: String = [12 12309 9]

接下来我们对lines进行操作

  1. val p = "[0-9a-zA-Z]+".r
  2. --1、切分后--> map(x=>(过滤,1))
  3. val ll = lines.flatMap(_.split(" ")).map(x=>(p.findAllIn(x).mkString(""),1))
  4. --2、分组,统计次数
  5. ll.groupBy(_._1).mapValues(_.size)
  6. --3、转换类型、排序、分片
  7. ll.groupBy(_._1).mapValues(_.size).toList.sortWith(_._2 > _._2).slice(0,10)

可以发现,经过过滤的wordCount词频统计,才是我们所需要的结果!!
scala一个wordCount 真不简单!

相关文章