DataFrame的基础操作
创建一个DataFrame
首先看一下SparkSQL可以接受那些类型:
scala> spark.read.
csv format jdbc json load option options
orc parquet schema table text textFile
注意:
本文中的spark是sparkSession对象默认的名称
然后去spark安装目录下将people.json文件上传至hdfs:
[root@cm1 /]# cd /usr/hdk/spark2/examples/src/main/resources
[root@cm1 resources]# ll
总用量 36
-rw-r--r-- 1 root root 130 7月 13 2017 employees.json
-rw-r--r-- 1 root root 240 7月 13 2017 full_user.avsc
-rw-r--r-- 1 root root 5812 7月 13 2017 kv1.txt
-rw-r--r-- 1 root root 73 7月 13 2017 people.json
-rw-r--r-- 1 root root 32 7月 13 2017 people.txt
-rw-r--r-- 1 root root 185 7月 13 2017 user.avsc
-rw-r--r-- 1 root root 334 7月 13 2017 users.avro
-rw-r--r-- 1 root root 615 7月 13 2017 users.parquet
[root@cm1 resources]# su hdfs
[hdfs@cm1 resources]$ hdfs dfs -put people.json /
[hdfs@cm1 resources]$ su root
[root@cm1 resources]# spark2-shell --master local[2]
从hdfs读取文件创建一个DataFrame
scala> val df = spark.read.json("/people.json")
df: org.apache.spark.sql.DataFrame = [age: bigint, name: string]
scala> df.show()
+----+-------+
| age| name|
+----+-------+
|null|Michael|
| 30| Andy|
| 19| Justin|
+----+-------+
创建一个临时表
临时表只在当前SparkSession有效,退出后就失效了。
创建表以后,就可以使用sql语句对表进行操作。
scala> df.createOrReplaceTempView("people")
scala> val sqlDF = spark.sql("SELECT * FROM people")
sqlDF: org.apache.spark.sql.DataFrame = [age: bigint, name: string]
scala> sqlDF.show
+----+-------+
| age| name|
+----+-------+
|null|Michael|
| 30| Andy|
| 19| Justin|
+----+-------+
注意:
什么是SparkSession
SparkSession是Spark最新的SQL查询起始点。
在最开始编写SpqrkSQL程序时需要创建一个变量包含相关配置,最开始是:sc = SparkContext(conf=conf) 或 hivec = HiveContext(sc)
后来spark对这两个方法进行了封装,就有了SparkSession:
sess = SparkSession.builder \ .appName("aaa") \ .config("spark.driver.extraClassPath", sparkClassPath) \ .master("local") \ .enableHiveSupport() \ # sparkSQL 连接 hive 时需要这句 .getOrCreate() # builder 方式必须有这句
临时表只在当前SparkSession生效,也就是临时表会随着作业的结束而释放。
创建一个全局表
创建全局表需要使用createGlobalTempView
函数,在写SQL语句中需要全路径调用这个表。
scala> df.createGlobalTempView("people")
scala> spark.sql("SELECT * FROM global_temp.people").show()
+----+-------+
| age| name|
+----+-------+
|null|Michael|
| 30| Andy|
| 19| Justin|
+----+-------+
查看DataFrame的Schema信息
scala> df.printSchema
root
|-- age: long (nullable = true)
|-- name: string (nullable = true)
数据的提取
- 查看
name
列的数据
scala> df.select("name").show()
+-------+
| name|
+-------+
|Michael|
| Andy|
| Justin|
+-------+
- 将
age
列的数据+1返回
scala> df.select($"age" + 1).show()
+---------+
|(age + 1)|
+---------+
| null|
| 31|
| 20|
+---------+
- 查询
age
大于21的数据
scala> df.select($"age" > 21).show()
+----------+
|(age > 21)|
+----------+
| null|
| true|
| false|
+----------+
- 按照
age
分组查看
scala> df.groupBy("age").count().show()
+----+-----+
| age|count|
+----+-----+
| 19| 1|
|null| 1|
| 30| 1|
+----+-----+
RDD转换为DateFrame
无论RDD转换为DF还是DS,必须导入一个包spark.implicits._
。
scala> import spark.implicits._
import spark.implicits._
scala> val peopleRDD = sc.textFile("/people.txt")
peopleRDD: org.apache.spark.rdd.RDD[String] = /people.txt MapPartitionsRDD[4] at textFile at <console>:27
scala> peopleRDD.collect()
res10: Array[String] = Array(Michael, 29, Andy, 30, Justin, 19)
scala> val df = peopleRDD.map{x=>val para = x.split(",");(para(0),para(1).trim.toInt)}.toDF("name","age")
df: org.apache.spark.sql.DataFrame = [name: string, age: int]
scala> df.show()
+-------+---+
| name|age|
+-------+---+
|Michael| 29|
| Andy| 30|
| Justin| 19|
+-------+---+
还有一种使用映射类的方式
scala> import spark.implicits._
import spark.implicits._
scala> val peopleRDD = sc.textFile("/people.txt")
peopleRDD: org.apache.spark.rdd.RDD[String] = /people.txt MapPartitionsRDD[1] at textFile at <console>:27
scala> case class People(name:String, age:Int)
defined class People
scala> val df = peopleRDD.map{ x => val para = x.split(",");People(para(0),para(1).trim.toInt)}.toDF
df: org.apache.spark.sql.DataFrame = [name: string, age: int]
scala> df.show()
+-------+---+
| name|age|
+-------+---+
|Michael| 29|
| Andy| 30|
| Justin| 19|
+-------+---+
DateFrame转换为RDD
转换成RDD只需要调用一个rdd
即可。
scala> val dfRDD = df.rdd
dfRDD: org.apache.spark.rdd.RDD[org.apache.spark.sql.Row] = MapPartitionsRDD[11] at rdd at <console>:33
scala> dfRDD.collect
res13: Array[org.apache.spark.sql.Row] = Array([Michael,29], [Andy,30], [Justin,19])
DataFrame转DataSet
将DF转换为DS需要使用样例类与as
方法。
scala> val df = spark.read.json("/people.json")
df: org.apache.spark.sql.DataFrame = [age: bigint, name: string]
scala> case class Person(name: String, age: Long)
defined class Person
scala> val ds = df.as[Person]
ds: org.apache.spark.sql.Dataset[Person] = [age: bigint, name: string]
scala> ds.show()
+----+-------+
| age| name|
+----+-------+
|null|Michael|
| 30| Andy|
| 19| Justin|
+----+-------+
DataSet的基础操作
创建一个DataSet
DataSet的创建必须基于样例类。
scala> case class Person(name: String, age: Long)
defined class Person
scala> val caseClassDS = Seq(Person("Andy", 32)).toDS()
caseClassDS: org.apache.spark.sql.Dataset[Person] = [name: string, age: bigint]
RDD转换为DataSet
RDD转换为DataSet也需要通过case类属性进行反射。
scala> val peopleRDD = sc.textFile("/people.txt")
peopleRDD: org.apache.spark.rdd.RDD[String] = /people.txt MapPartitionsRDD[10] at textFile at <console>:24
scala> case class Person(name: String, age: Long)
defined class Person
scala> val ds = peopleRDD.map(line => {val para = line.split(",");Person(para(0),para(1).trim.toInt)}).toDS()
ds: org.apache.spark.sql.Dataset[Person] = [name: string, age: bigint]
scala> ds.show()
+-------+---+
| name|age|
+-------+---+
|Michael| 29|
| Andy| 30|
| Justin| 19|
+-------+---+
DataSet转换为RDD
scala> val rdd = ds.rdd
rdd: org.apache.spark.rdd.RDD[Person] = MapPartitionsRDD[16] at rdd at <console>:30
scala> rdd.collect
res2: Array[Person] = Array(Person(Michael,29), Person(Andy,30), Person(Justin,19))
DataSet转DataFrame
需要使用方法toDF
。
scala> val df = ds.toDF
df: org.apache.spark.sql.DataFrame = [name: string, age: bigint]
scala> df.show()
+-------+---+
| name|age|
+-------+---+
|Michael| 29|
| Andy| 30|
| Justin| 19|
+-------+---+
RDD、DataFrame、DataSet三者的异同
首先,DS包含DF与RDD,DF与RDD互相独立。
共性
- RDD、DataFrame、Dataset全都是spark平台下的分布式弹性数据集,为处理超大型数据提供便利
- 三者都有惰性机制,在进行创建、转换,如map方法时,不会立即执行,只有在遇到Action如foreach时,三者才会开始遍历运算。
- 三者都会根据spark的内存情况自动缓存运算,这样即使数据量很大,也不用担心会内存溢出。
- 三者都有partition的概念
- 三者有许多共同的函数,如filter,排序等
- 在对DataFrame和Dataset进行操作许多操作都需要
import spark.implicits._
进行支持 - DataFrame和Dataset均可使用模式匹配获取各个字段的值和类型
区别
- RDD
- RDD一般和spark mlib同时使用
- RDD不支持sparksql操作
- DataFrame:
- 与RDD和Dataset不同,DataFrame每一行的类型固定为Row,每一列的值没法直接访问,只有通过解析才能获取各个字段的值
- DataFrame与Dataset一般不与spark mlib同时使用
- DataFrame与Dataset均支持sparksql的操作,比如select,groupby之类,还能注册临时表/视窗,进行sql语句操作
- DataFrame与Dataset支持一些特别方便的保存方式,比如保存成csv,可以带上表头,这样每一列的字段名一目了然
- Dataset:
- Dataset和DataFrame拥有完全相同的成员函数,区别只是每一行的数据类型不同。
- DataFrame也可以叫Dataset[Row],每一行的类型是Row,不解析,每一行究竟有哪些字段,各个字段又是什么类型都无从得知