Spark SQL的数据分析应用

  • 1. 实验室名称:
  • 2. 实验项目名称:
  • 3. 实验学时:
  • 4. 实验原理:
  • 5. 实验目的:
  • 6. 实验内容:
  • 7. 实验器材(设备、虚拟机名称):
  • 8. 实验步骤:
  • 8.1 启动Spark与zeppelin
  • 8.2 Spark SQL内置函数
  • 8.3 使用自定义函数(UDF)
  • 9. 实验结果及分析:
  • 10. 实验结论:
  • 11. 总结及心得体会:



申明: 未经许可,禁止以任何形式转载,若要引用,请标注链接地址


全文共计8855字,阅读大概需要3分钟

1. 实验室名称:

大数据实验教学系统

2. 实验项目名称:

Spark SQL的数据分析应用

3. 实验学时:

4. 实验原理:

DataFrame API 的设计目的是在数据集中操作或转换单个行,如过滤或分组。如果我们想要转换一个数据集中的每一行的列的值,例如将字符串从大写字母转换成驼峰命名形式,那么我们将使用一个函数来实现这一点。函数基本上就是应用于列的方法。Spark SQL 提供了一组通常需要的函数,同时也提供了创建新函数的简单方法。
  尽管 Spark SQL 为大多数常见用例提供了大量的内置函数,但总会有一些情况下,这些功能都不能提供您的用例所需要的功能。Spark SQL 提供了一个相当简单的工具来编写用户定义的函数(UDF),并在 Spark 数据处理逻辑或应用程序中使用它们,就像使用内置函数一样。UDFs 实际上是您可以扩展 Spark 的功能以满足您的特定需求的一种方式。我最喜欢Spark 的另一件事是 UDFs 可以用 Python、Java 或 Scala 来写,它们可以利用和集成任何必要的库。因为您能够使用您最熟悉的编程语言来编写 UDFs,所以开发和测试 UDFs 是非常简单和快速的。

5. 实验目的:

掌握Spark SQL常用内置函数的使用。
  掌握Spark SQL自定义函数(UDF)的使用。

6. 实验内容:

1、学习Spark SQL常用内置函数的使用。
  2、学习Spark SQL自定义函数(UDF)的使用。

7. 实验器材(设备、虚拟机名称):

硬件:x86_64 ubuntu 16.04服务器
  软件:JDK 1.8,Spark-2.3.2,Hadoop-2.7.3,zeppelin-0.8.1

8. 实验步骤:

8.1 启动Spark与zeppelin

1、在终端窗口下,输入以下命令,分别启动Spark集群和Zeppelin服务器:

1.	$ cd /opt/spark
2.	$ ./sbin/start-all.sh
3.	$ zeppelin-daemon.sh start

2、启动浏览器,打开zeppelin notebook首页,点击【Create new note】链接,创建一个新的笔记本,如下图所示:

spark sql 分割字符串 spark sql数据分析_spark sql 分割字符串

8.2 Spark SQL内置函数

Spark SQL提供了很多内置函数,包括处理日期时间函数、处理字符串函数和数学处理函数。
  1、 处理日期时间函数。日期和时间转换函数有助于将字符串转换为日期、时间戳或 Unix 时间戳,反之亦然。
在内部,它使用 Java 日期格式模式语法。这些函数使用的默认的日期格式是 yyyy-mm-dd HH:mm:ss。
  下面的示例显示了将字符串类型的日期和时间戳转换为 Spark date 和 timestamp 类型。
  在zeppelin中执行如下代码:

1.	//日期数据,最后两列不遵循默认日期格式
2.	val testDate = Seq((1, "2018-01-01", "2018-01-01 15:04:58:865", "01-01-2018", "12-05-2017 45:50"))
3.	     
4.	//添加列名
5.	val testDateTSDF = testDate.toDF("id","date", "timestamp","date_str", "ts_str")
6.	     
7.	// 将这些字符串转换为 date、timestamp 和 unix timestamp,并指定一个自定义的 date 和 timestamp格式
8.	val testDateResultDF= testDateTSDF.select(
9.	    to_date('date).as("date1"), 
10.	    to_timestamp('timestamp).as("ts1"), 
11.	    to_date('date_str,"MM-dd-yyyy").as("date2"), 
12.	    to_timestamp('ts_str,"MM-dd-yyyy mm:ss").as("ts2"), 
13.	    unix_timestamp('timestamp).as("unix_ts"))
14.	     
15.	//输出DataFrame中的数据结构信息,即为schema
16.	testDateResultDF.printSchema
17.	     
18.	//输出DataFrame
19.	testDateResultDF.show

执行以上代码,输出结果如下:

1.	 |-- date1: date (nullable = true)
2.	 |-- ts1: timestamp (nullable = true)
3.	 |-- date2: date (nullable = true)
4.	 |-- ts2: timestamp (nullable = true)
5.	 |-- unix_ts: long (nullable = true)
6.	     
7.	+----------+----+----------+-------------------+----------+
8.	|     date1| ts1|     date2|                ts2|   unix_ts|
9.	+----------+----+----------+-------------------+----------+
10.	|2018-01-01|null|2018-01-01|2017-12-05 00:45:50|1514790298|
11.	+----------+----+----------+-------------------+----------+

将日期或时间戳转换为时间字符串是很容易的,方法是使用 date_format 函数和定制日 期格式,或者使用 from_unixtime 函数将 Unix 时间戳(以秒为单位)转换成字符串。
  在zeppelin中执行如下代码:

1.	// 将日期、时间戳和 Unix 时间戳转换成字符串 
2.	testDateResultDF.select(date_format('date1,"dd-MM-YYYY").as("date_str"), 
3.	                        date_format('ts1,"dd-MM-YYYY HH:mm:ss").as("ts_str"),
4.	                        from_unixtime('unix_ts,"dd-MM-YYYY HH:mm:ss").as("unix_ts_str")).show

执行以上代码,输出结果如下:

1.	+----------+------+-------------------+
2.	|  date_str|ts_str|        unix_ts_str|
3.	+----------+------+-------------------+
4.	|01-01-2018|  null|01-01-2018 15:04:58|
5.	+----------+------+-------------------+

在处理时间序列数据(time-series data)时,能够提取日期或时间戳值的特定字段(如 年、月、小时、分钟和秒)的能力是非常方便的。例如,当需要按季度、月或周对所有股票 交易进行分组时,就可以从交易日期提取该信息,并按这些值分组。下面的代码展示了如何从日期或时间戳中提取字段。
  在zeppelin中执行如下代码:

1.	// 从一个日期值中提取指定的字段 
2.	val valentimeDateDF=Seq(("2018-02-14 05:35:55")).toDF("date") 
3.	     
4.	valentimeDateDF.select(
5.	    year('date).as("year"), 
6.	    quarter('date).as("quarter"), 
7.	    month('date).as("month"), 
8.	    weekofyear('date).as("woy"), 
9.	    dayofmonth('date).as("dom"), 
10.	    dayofyear('date).as("doy"), 
11.	    hour('date).as("hour"), 
12.	    minute('date).as("minute"), 
13.	    second('date).as("second")
14.	).show

结果:

1.	+----+-------+-----+---+---+---+----+------+------+
2.	|year|quarter|month|woy|dom|doy|hour|minute|second|
3.	+----+-------+-----+---+---+---+----+------+------+
4.	|2018|      1|    2|  7| 14| 45|   5|    35|    55|
5.	+----+-------+-----+---+---+---+----+------+------+

2、处理字符串函数。SparkSQL 内置的字符串函数 提供了操作这类列的通用和强大的方法。
  有很多方法可以转换字符串。最常见的是去空格、填充、大写、小写和连接。下面的代 码展示了使用各种内置字符串函数转换字符串的各种方法。
  在zeppelin中执行如下代码:

1.	//原始数据
2.	val sparkDF=Seq((" Spark ")).toDF("name")
3.	//原始数据展示
4.	sparkDF.show
5.	     
6.	//trimming 
7.	sparkDF.select(trim('name).as("trim"),ltrim('name).as("ltrim"),rtrim('name).as("rtrim")).show
8.	     
9.	// 用给定的 pad 字符串将字符串填充到指定长度 // 首先去掉"Spark"前后的空格,然后填充到 8 个字符长 
10.	sparkDF.select(trim('name).as("trim"))
11.	       .select(lpad('trim, 8, "-").as("lpad"),rpad('trim, 8, "=").as("rpad"))
12.	       .show
13.	     
14.	// 使用 concatenation,uppercase,lowercase 和 reverse 转换一个字符串 
15.	val sparkAwesomeDF= Seq(("Spark","is","awesome")).toDF("subject","verb","adj") 
16.	sparkAwesomeDF.select(concat_ws(" ",'subject,'verb, 'adj).as("sentence"))
17.	              .select(lower('sentence).as("lower"), 
18.	                      upper('sentence).as("upper"), 
19.	                      initcap('sentence).as("initcap"), 
20.	                      reverse('sentence).as("reverse")
21.	                      )
22.	              .show
23.	     
24.	// 从一个字符转换到另一个字符 
25.	sparkAwesomeDF.select('subject,translate('subject,"ar", "oc").as("translate")).show

【shift+enter】对程序进行输出。输出结果如下所示:

1.	+-------+
2.	|   name|
3.	+-------+
4.	| Spark |
5.	+-------+
6.	     
7.	+-----+------+------+
8.	| trim| ltrim| rtrim|
9.	+-----+------+------+
10.	|Spark|Spark | Spark|
11.	+-----+------+------+
12.	     
13.	+--------+--------+
14.	|    lpad|    rpad|
15.	+--------+--------+
16.	|---Spark|Spark===|
17.	+--------+--------+
18.	     
19.	+----------------+----------------+----------------+----------------+
20.	|           lower|           upper|         initcap|         reverse|
21.	+----------------+----------------+----------------+----------------+
22.	|spark is awesome|SPARK IS AWESOME|Spark Is Awesome|emosewa si krapS|
23.	+----------------+----------------+----------------+----------------+
24.	     
25.	+-------+---------+
26.	|subject|translate|
27.	+-------+---------+
28.	|  Spark|    Spock|
29.	+-------+---------+

正则表达式是一种强大而灵活的方式,可以替换字符串的某些部分或从字符串中提取子 字符串。regexp_extract 和 regexp_replace 函数是专门为这些目的而设计的。Spark 利用 Java 正则表达式库来实现这两个字符串函数的底层实现。 regexp_extract 函数的输入参数是字符串列、匹配的模式和组索引。在字符串中可能会 有多个匹配模式;因此,需要组索引(从 0 开始)来确定是哪一个。如果没有指定模式的匹 配,则该函数返回空字符串。

1.	// 使用 regexp_extract字符串函数来提取"fox",使用一个模式 
2.	val rhymeDF = Seq(("A fox saw a crow sitting on a tree singing \"Caw! Caw! Caw!\"")).toDF("rhyme")
3.	     
4.	// 使用一个模式 
5.	rhymeDF.select(regexp_extract('rhyme,"[a-z]*o[xw]",0).as("substring")).show

【shift+enter】对程序进行输出。输出结果如下所示:

1.	+---------+
2.	|substring|
3.	+---------+
4.	|      fox|
5.	+---------+

regexp_replace 字符串函数的输入参数是字符串列、匹配的模式、以及替换的值。

1.	// 用 regexp_replace字符串函数将“fox”和“Caw”替换为“animal” 
2.	val rhymeDF1 = Seq(("A fox saw a crow sitting on a tree singing \"Caw! Caw! Caw!\"")).toDF("rhyme1")
3.	     
4.	// 下面两行产生相同的输出
5.	rhymeDF1.select(regexp_replace('rhyme1,"fox|crow", "animal").as("new_rhyme")).show(false) 
6.	rhymeDF1.select(regexp_replace('rhyme1,"[a-z]*o[xw]", "animal").as("new_rhyme")).show(false)

【shift+enter】对程序进行输出。输出结果如下所示:

1.	+----------------------------------------------------------------+
2.	|new_rhyme                                                       |
3.	+----------------------------------------------------------------+
4.	|A animal saw a animal sitting on a tree singing "Caw! Caw! Caw!"|
5.	+----------------------------------------------------------------+
6.	     
7.	+----------------------------------------------------------------+
8.	|new_rhyme                                                       |
9.	+----------------------------------------------------------------+
10.	|A animal saw a animal sitting on a tree singing "Caw! Caw! Caw!"|
11.	+----------------------------------------------------------------+

3、处理 Math 函数。最常见的列类型是数值类型。一个非常有用和常用的名为 round 的函数,它根据给定的规模执行一个数值的半向上的四舍五入。

1.	//初始数据集
2.	val numberDF = Seq((3.25,4.45)).toDF("pie", "gpa")
3.	     
4.	numberDF.show()
5.	     
6.	//处理 Math 函数 
7.	numberDF.select(round('pie).as("pie0"), round('pie,1).as("pie1"), round('pie,2).as("pie2"), round('gpa).as("gpa")).show
8.	// 因为它是一个半向上的四舍五入,gpa 的值四舍五入到 4.0

【shift+enter】对程序进行输出。输出结果如下所示:

1.	+----+----+
2.	| pie| gpa|
3.	+----+----+
4.	|3.25|4.45|
5.	+----+----+
6.	     
7.	+----+----+----+---+
8.	|pie0|pie1|pie2|gpa|
9.	+----+----+----+---+
10.	| 3.0| 3.3|3.25|4.0|
11.	+----+----+----+---+

8.3 使用自定义函数(UDF)

使用 UDFs 涉及有三个步骤。第一步是编写一个函数并进行测试。第二步是通过将函数 名及其签名传递给Spark的udf函数来注册该函数。 最后一步是在DataFrame代码或发出SQL 查询时使用 UDF。在 SQL 查询中使用 UDF 时,注册过程略有不同。

1.	// 在 Scala 中一个简单的 UDF,将数字级别转换为字母等级 
2.	// 创建学生成绩 DataFrame 
3.	import sqlContext.implicits._
4.	import org.apache.spark.sql.functions.udf
5.	
6.	case class Student(name:String,score:Int)
7.	     
8.	val studentDF= Seq(Student("Joe",85),Student("Jane",90),Student("Mary",55)).toDF()
9.	// 注册为视图 
10.	studentDF.createOrReplaceTempView("students")
11.	     
12.	// 创建一个函数将成绩转换到字母等级 createa functiontoconvert gradetoletter grade 
13.	def letterGrade(score:Int): String= { 
14.	    score match{ 
15.	        case score if score>100 =>"Cheating" 
16.	        case score if score>=90 =>"A" 
17.	        case score if score>=80 =>"B" 
18.	        case score if score>=70 =>"C" 
19.	        case _ =>"F" } 
20.	
21.	}
22.	     
23.	// 注册为一个 UDF
24.	val letterGradeUDF =udf(letterGrade(_:Int):String)
25.	     
26.	// 使用该 UDF 将成绩转换为字母等级 
27.	studentDF.select($"name",$"score",letterGradeUDF($"score").as("grade")).show
28.	     
29.	// 注册为 UDF,在 SQL 中使用
30.	spark.udf.register("letterGrade",letterGrade(_: Int):String)
31.	spark.sql("select name,score,letterGrade(score) as grade from students").show

【shift+enter】对程序进行输出。输出结果如下所示:

1.	 +----+-----+-----+
2.	|name|score|grade|
3.	+----+-----+-----+
4.	| Joe|   85|    B|
5.	|Jane|   90|    A|
6.	|Mary|   55|    F|
7.	+----+-----+-----+
8.	     
9.	+----+-----+-----+
10.	|name|score|grade|
11.	+----+-----+-----+
12.	| Joe|   85|    B|
13.	|Jane|   90|    A|
14.	|Mary|   55|    F|
15.	+----+-----+-----+

9. 实验结果及分析:

实验结果运行准确,无误

10. 实验结论:

经过本节实验的学习,通过学习Spark SQL的数据分析应用,进一步巩固了我们的Spark基础。

11. 总结及心得体会:

经过本节实验的学习,通过学习Spark SQL常用内置函数的使用,Spark SQL自定义函数(UDF)的使用。

spark sql 分割字符串 spark sql数据分析_spark_02