Hadoop+Spark大数据技术 第七章 Spark RDD编程1(实验)

Apache Spark中,`sc` 和 `reduce` 分别代表 SparkContext 和 reduce 函数。

1. SparkContext (`sc`):是 Spark 的主要入口点,它代表与 Spark 集群的连接。
     在 Spark 应用程序中,需要首先创建一个 SparkContext 对象,负责与集群通信并管理资源。通过 SparkContext,可以创建 RDDs(Resilient Distributed Datasets)、Broadcast Variables、Accumulators 等。
2. Reduce 函数:在分布式计算中,reduce 函数是一个用于聚合数据的操作。
      在 Spark 中,reduce 函数通常用于对 RDD(Resilient Distributed Dataset)中的元素执行聚合操作。
      接受一个函数作为参数,函数定义了如何将两个元素聚合成一个新元素。
       reduce 函数会在集群中的多个节点上并行执行,并在最后将结果聚合起来。
      常见的用例包括对数字进行求和、查找最大值或最小值等。

使用文本文件创建RDD

注意:需要先在hadoop分布式文件系统中创建文件

1.先在本地文件系统创建data.txt文件

cd /usr/local/hadoop/input

gedit data.txt

2.启动hadoop分布式文件系统

./sbin/start-dfs.sh

3.上传本地文件data.txt到hadoop分布式文件系统

hdfs dfs -put /usr/local/hadoop/input/data.txt input

查看分布式文件系统中是否存在data.txt
hdfs dfs -ls input
e92c49aae00645cb8a704563a14caca6.png

textFile 函数用于读取文本文件中的内容,并将其转换为RDD。

rdd.map(line => line.length):这一行代码将每一行文本映射为其长度,即字符数。

.map()函数对RDD中的每个元素应用给定的函数,并返回结果组成的新RDD。

.reduce(_ + _):这一行代码对RDD中的所有元素进行累加,得到它们的总和。.reduce()函数将RDD中的元素两两合并,直到只剩下一个元素,这里使用的匿名函数 _ + _ 表示将两个元素相加。

val wordCount = rdd.map(line => line.length).reduce(_ + _):这行代码将文本文件中所有行的字符数求和,并将结果保存在wordCount变量中。

wordCount:最后,wordCount 变量中保存着文本文件中所有行的字符总数。

val rdd = sc.textFile("/user/hadoop/input/data.txt")
rdd: org.apache.spark.rdd.RDD[String] = /user/hadoop/input/data.txt MapPartitionsRDD[31] at textFile at <console>:25

val wordCount = rdd.map(line => line.length).reduce(_ + _)
wordCount

wordCount: Int = 274
res11: Int = 274

一些JSON数据

{
  "name": "中国",
  "province": [
    {
      "name": "河南",
      "cities": [
        {
          "city": ["郑州","洛阳"]
      ]
    }
  ]
}
```

{
  "code": 0,
  "msg": "",
  "count": 2,
  "data": [
    {
      "id": 101,
      "username": "zhangsan",
      "city": "xiamen"
    },
    {
      "id": 102,
      "username": "liming",
      "city": "zhengzhou"
    }
  ]
}
{"学号":"106","姓名":"李明","数据结构":"92"}

{"学号":"242","姓名":"李乐","数据结构":"93"}

{"学号":"107","姓名":"冯涛","数据结构":"99"}

完整程序练习

seq是一个包含元组的列表,每个元组都有一个字符串键和一个字符串列表值。

sc.parallelize方法将seq转换为一个RDD。

rddP.partitions.size是用来获取RDD的分区数量的。RDD的分区是数据的逻辑划分,决定了数据在集群中的分布方式。每个分区都可以在集群的不同节点上进行并行处理。

//使用程序中的数据创建RDD

val arr = Array(1,2,3,4,5,6)

val rdd = Array(1,2,3,4,5,6)

arr: Array[Int] = Array(1, 2, 3, 4, 5, 6)
rdd: Array[Int] = Array(1, 2, 3, 4, 5, 6)

val sum = rdd.reduce(_ + _)

sum: Int = 21

//parallelize() 创建RDD

val seq = List( ("num", List("one","two","three") ), ("study",List("Scala","Python","Hadoop")) , ("color", List("blue","white","black")) )

val rddP = sc.parallelize(seq)   

rddp.partitions.size

seq: List[(String, List[String])] = List((num,List(one, two, three)), (study,List(Scala, Python, Hadoop)), (color,List(blue, white, black)))
rddP: org.apache.spark.rdd.RDD[(String, List[String])] = ParallelCollectionRDD[7] at parallelize at <console>:31
res5: Int = 2

//makeRDD() 创建RDD

val rddM = sc.makeRDD(seq)

rddM.partitions.size

rddM: org.apache.spark.rdd.RDD[String] = ParallelCollectionRDD[8] at makeRDD at <console>:30
res6: Int = 3

//hdfs的文件创建RDD

val rdd = sc.textFile("/user/hadoop/input/data.txt")

rdd: org.apache.spark.rdd.RDD[String] = /user/hadoop/input/data.txt MapPartitionsRDD[31] at textFile at <console>:25

val wordCount = rdd.map(line => line.length).reduce(_ + _)

wordCount

wordCount: Int = 274
res11: Int = 274

//本地文件创建RDD

val rdd = sc.textFile("file:/home/hadoop/data.txt")

val wordCount = rdd.map(line => line.length).reduce(_ + _)

wordCount

rdd: org.apache.spark.rdd.RDD[String] = file:/home/hadoop/data.txt MapPartitionsRDD[34] at textFile at <console>:29
wordCount: Int = 274
res12: Int = 274

val rddw1 = sc.textFile("file:/home/hadoop/input")

rddw1.collect()

rddw1: org.apache.spark.rdd.RDD[String] = file:/home/hadoop/input MapPartitionsRDD[37] at textFile at <console>:25
res13: Array[String] = Array(Hello Spark!, Hello Scala!)

val rddw1 = sc.wholeTextFiles("file:/home/hadoop/input")

rddw1.collect()

rddw1: org.apache.spark.rdd.RDD[(String, String)] = file:/home/hadoop/input MapPartitionsRDD[39] at wholeTextFiles at <console>:27
res14: Array[(String, String)] =
Array((file:/home/hadoop/input/text1.txt,"Hello Spark!
"), (file:/home/hadoop/input/text2.txt,"Hello Scala!
"))

val jsonStr = sc.textFile("file:/home/hadoop/student.json")

jsonStr.collect()

jsonStr: org.apache.spark.rdd.RDD[String] = file:/home/hadoop/student.json MapPartitionsRDD[48] at textFile at <console>:28
res18: Array[String] = Array({"学号":"106","姓名":"李明","数据结构":"92"}, {"学号":"242","姓名":"李乐","数据结构":"93"}, {"学号":"107","姓名":"冯涛","数据结构":"99"})

import scala.util.parsing.json.JSON

val jsonStr = sc.textFile("file:/home/hadoop/student.json")

val result  = jsonStr.map(s => JSON.parseFull(s))

result.foreach(println)

Some(Map(学号 -> 107, 姓名 -> 冯涛, 数据结构 -> 99))
Some(Map(学号 -> 106, 姓名 -> 李明, 数据结构 -> 92))
Some(Map(学号 -> 242, 姓名 -> 李乐, 数据结构 -> 93))

import scala.util.parsing.json.JSON
jsonStr: org.apache.spark.rdd.RDD[String] = file:/home/hadoop/student.json MapPartitionsRDD[50] at textFile at <console>:31
result: org.apache.spark.rdd.RDD[Option[Any]] = MapPartitionsRDD[51] at map at <console>:32

import java.io.StringReader

import au.com.bytecode.opencsv.CSVReader

val gradeRDD = sc.textFile("file:/home/hadoop/sparkdata/grade.csv")

val result = gradeRDD.map{line => val reader = new CSVReader(new StringReader(line));reader.readNext()}

result.collect().foreach(x => println(x(0),x(1),x(2)))

(101,LiNing,95)
(102,LiuTao,90)
(103,WangFei,96)

import java.io.StringReader
import au.com.bytecode.opencsv.CSVReader
gradeRDD: org.apache.spark.rdd.RDD[String] = file:/home/hadoop/sparkdata/grade.csv MapPartitionsRDD[53] at textFile at <console>:31
result: org.apache.spark.rdd.RDD[Array[String]] = MapPartitionsRDD[54] at map at <console>:32

练习代码分析

第一部分:基本操作

  1. val arr = Array(1,2,3,4,5,6)
    • 定义一个名为 arr 的不可变数组,其中包含元素 1、2、3、4、5、6。
  2. val rdd = Array(1,2,3,4,5,6)
    • 定义一个名为 rdd 的不可变数组,其中包含元素 1、2、3、4、5、6。
  3. arr: Array[Int] = Array(1, 2, 3, 4, 5, 6)
    • 输出 arr 的类型和值,表明 arr 是一个 Int 类型的数组,包含元素 1、2、3、4、5、6。
  4. rdd: Array[Int] = Array(1, 2, 3, 4, 5, 6)
    • 输出 rdd 的类型和值,表明 rdd 是一个 Int 类型的数组,包含元素 1、2、3、4、5、6。
  5. val sum = rdd.reduce(_ + _)
    • 定义一个名为 sum 的变量,并使用 reduce 函数对 rdd 中的元素进行累加操作。_ + _ 表示使用加法运算符对两个元素进行累加。
  6. sum: Int = 21
    • 输出 sum 的类型和值,表明 sum 是一个 Int 类型的变量,其值为 21。

第二部分:创建 RDD

  1. val seq = List( ("num", List("one","two","three") ), ("study",List("Scala","Python","Hadoop")) , ("color", List("blue","white","black")) )
    • 定义一个名为 seq 的不可变列表,其中包含三个元组,每个元组包含一个字符串和一个字符串列表。
  2. val rddP = sc.parallelize(seq)
    • 使用 SparkContext 的 parallelize 方法将 seq 列表转换为一个 RDD,并将结果存储在 rddP 变量中。
  3. rddp.partitions.size
    • 获取 rddP 的分区数量,并输出结果。
  4. seq: List[(String, List[String])] = List((num,List(one, two, three)), (study,List(Scala, Python, Hadoop)), (color,List(blue, white, black)))
    • 输出 seq 的类型和值,表明 seq 是一个包含三个元组的列表,每个元组包含一个字符串和一个字符串列表。
  5. rddP: org.apache.spark.rdd.RDD[(String, List[String])] = ParallelCollectionRDD[7] at parallelize at <console>:31
    • 输出 rddP 的类型和值,表明 rddP 是一个包含字符串和字符串列表元组的 RDD,并且是由 parallelize 方法创建的。
  6. res5: Int = 2
    • 输出 rddP 的分区数量,表明 rddP 被分成了两个分区。
  7. val rddM = sc.makeRDD(seq)
    • 使用 SparkContext 的 makeRDD 方法将 seq 列表转换为一个 RDD,并将结果存储在 rddM 变量中。
  8. rddM.partitions.size
    • 获取 rddM 的分区数量,并输出结果。
  9. rddM: org.apache.spark.rdd.RDD[String] = ParallelCollectionRDD[8] at makeRDD at <console>:30
    • 输出 rddM 的类型和值,表明 rddM 是一个包含字符串的 RDD,并且是由 makeRDD 方法创建的。
  10. res6: Int = 3
  • 输出 rddM 的分区数量,表明 rddM 被分成了三个分区。

第三部分:从文件创建 RDD

1. 从本地文件创建 RDD

val rdd = sc.textFile("file:/home/hadoop/data.txt") 
  • sc 对象是 SparkContext,用于连接 Spark 集群并执行 Spark 操作。
  • textFile 方法用于从指定路径读取文本文件,并创建包含每行文本的 RDD。
  • "file:/home/hadoop/data.txt" 是本地文件路径,使用 file: 前缀表示它是本地文件。
  • rdd 是包含每行文本的 RDD,类型为 org.apache.spark.rdd.RDD[String]
val wordCount = rdd.map(line => line.length).reduce(_ + _)
  • map 方法对 RDD 中的每个元素进行转换,这里将每行文本转换为其长度。
  • line => line.length 是匿名函数,接收一行文本作为输入,并返回其长度。
  • reduce 方法对 RDD 中的元素进行聚合操作,这里使用 _ + _ 匿名函数将所有长度进行累加。
  • wordCount 变量存储了所有文本长度的总和,类型为 Int
wordCount
  • 输出 wordCount 的值,这里应该是文本长度的总和。
rdd: org.apache.spark.rdd.RDD[String] = file:/home/hadoop/data.txt MapPartitionsRDD[34] at textFile at <console>:29
wordCount: Int = 274
res12: Int = 274
  • 这是 Spark REPL 的输出,展示了 rdd 的类型、RDD 的来源和 wordCount 的值。

2. 读取目录下的所有文件

val rddw1 = sc.textFile("file:/home/hadoop/input")
  • 使用 textFile 方法读取 /home/hadoop/input 目录下的所有文件,并创建包含每行文本的 RDD。
  • rddw1 是包含每行文本的 RDD。
rddw1.collect()
  • collect 方法将 RDD 中的所有元素收集到一个数组中。
rddw1: org.apache.spark.rdd.RDD[String] = file:/home/hadoop/input MapPartitionsRDD[37] at textFile at <console>:25
res13: Array[String] = Array(Hello Spark!, Hello Scala!)
  • 输出 rddw1 的类型和值,表明 rddw1 是包含字符串的 RDD,并展示了读取的文本内容。

3. 读取文件内容

val rddw1 = sc.wholeTextFiles("file:/home/hadoop/input")
  • 使用 wholeTextFiles 方法读取 /home/hadoop/input 目录下的所有文件,并将每个文件的路径和内容作为键值对存储在 RDD 中。
  • rddw1 是包含 (路径, 内容) 元组的 RDD。
rddw1.collect()
  • collect 方法将 RDD 中的所有元素收集到一个数组中。
rddw1: org.apache.spark.rdd.RDD[(String, String)] = file:/home/hadoop/input MapPartitionsRDD[39] at wholeTextFiles at <console>:27
res14: Array[(String, String)] =
Array((file:/home/hadoop/input/text1.txt,"Hello Spark!
"), (file:/home/hadoop/input/text2.txt,"Hello Scala!
"))
  • 输出 rddw1 的类型和值,展示了读取的文件路径和内容。

4. 读取 JSON 文件

val jsonStr = sc.textFile("file:/home/hadoop/student.json")
  • 使用 textFile 方法读取 /home/hadoop/student.json 文件,并创建包含每行 JSON 字符串的 RDD。
  • jsonStr 是包含 JSON 字符串的 RDD。
jsonStr.collect()
  • collect 方法将 RDD 中的所有元素收集到一个数组中。
jsonStr: org.apache.spark.rdd.RDD[String] = file:/home/hadoop/student.json MapPartitionsRDD[48] at textFile at <console>:28
res18: Array[String] = Array({"学号":"106","姓名":"李明","数据结构":"92"}, {"学号":"242","姓名":"李乐","数据结构":"93"}, {"学号":"107","姓名":"冯涛","数据结构":"99"})
  • 输出 jsonStr 的类型和值,展示了读取的 JSON 字符串数组。

5. 解析 JSON 数据

import scala.util.parsing.json.JSON
  • 导入 scala.util.parsing.json.JSON 包,用于解析 JSON 数据。
val jsonStr = sc.textFile("file:/home/hadoop/student.json") 
val result  = jsonStr.map(s => JSON.parseFull(s))
  • map 方法对 RDD 中的每个 JSON 字符串进行解析,使用 JSON.parseFull 方法将其解析为 Option[Any] 类型。
  • result 是包含解析结果的 RDD。
result.foreach(println)
  • foreach 方法对 RDD 中的每个元素进行遍历,并输出解析后的结果。
Some(Map(学号 -> 107, 姓名 -> 冯涛, 数据结构 -> 99))
Some(Map(学号 -> 106, 姓名 -> 李明, 数据结构 -> 92))
Some(Map(学号 -> 242, 姓名 -> 李乐, 数据结构 -> 93))
  • 输出解析后的 JSON 数据。
import scala.util.parsing.json.JSON
jsonStr: org.apache.spark.rdd.RDD[String] = file:/home/hadoop/student.json MapPartitionsRDD[50] at textFile at <console>:31
result: org.apache.spark.rdd.RDD[Option[Any]] = MapPartitionsRDD[51] at map at <console>:32
  • 这是 Spark REPL 的输出,展示了 jsonStr 和 result 的类型。

6. 读取 CSV 文件

import java.io.StringReader
import au.com.bytecode.opencsv.CSVReader
  • 导入 java.io.StringReader 和 au.com.bytecode.opencsv.CSVReader 包,用于读取 CSV 文件。
val gradeRDD = sc.textFile("file:/home/hadoop/sparkdata/grade.csv") 
val result = gradeRDD.map{line => val reader = new CSVReader(new StringReader(line));reader.readNext()}
  • gradeRDD 是包含每行 CSV 数据的 RDD。
  • map 方法对 RDD 中的每行 CSV 数据进行解析,使用 CSVReader 对象将其解析为一个字符串数组。
  • result 是包含解析后的字符串数组的 RDD。
result.collect().foreach(x => println(x(0),x(1),x(2)))
  • collect 方法将 RDD 中的所有元素收集到一个数组中,并使用 foreach 方法遍历数组,将每个数组元素的第一个、第二个和第三个元素输出。
(101,LiNing,95)
(102,LiuTao,90)
(103,WangFei,96)
  • 输出解析后的 CSV 数据。
import java.io.StringReader
import au.com.bytecode.opencsv.CSVReader
gradeRDD: org.apache.spark.rdd.RDD[String] = file:/home/hadoop/sparkdata/grade.csv MapPartitionsRDD[53] at textFile at <console>:31
result: org.apache.spark.rdd.RDD[Array[String]] = MapPartitionsRDD[54] at map at <console>:32
  • 这是 Spark REPL 的输出,展示了 gradeRDD 和 result 的类型。
  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值