1、前言
DataFrame并不是Spark最新提出使用的,DataFrame最早是在R、Python语言中使用的。
Dataset是一个分布式的数据集;而DataFrame是一个以列(包括列名、列的类型、列值)的形式构成的分布式数据集,并且按照列赋予不同的名称。在概念层次上,我们可以把DataFrame理解为关系型数据里面的一张表。DataFrame可以通过结构化的数据创建,或者通过Hive中的一张表进行创建,还可以通过已经存在的RDD进行创建等。
RDD vs DataFrame:
2、DataFrame API基本操作
import org.apache.spark.sql.SparkSession
/**
* @author YuZhansheng
* @desc DataFrameAPI基本操作
* @create 2019-03-04 16:45
*/
object DataFrameAPP {
def main(args: Array[String]): Unit = {
val spark = SparkSession.builder().appName("DataFrameAPP").master("local[2]").getOrCreate()
//将json文件加载成一个DataFrame
val peopleDF = spark.read.format("json").load("file:/soft/spark/examples/src/main/resources/people.json")
//1、输出DataFrame对应的schema信息
peopleDF.printSchema()
//2、输出DataFrame里面的数据,不加参数默认输出前20条
peopleDF.show(100)
//3、查询DataFrame里面某一列数据:select name from table;
peopleDF.select("name").show()
//4、查询某几列所有数据,并对列进行计算:select name age+10 from table;
peopleDF.select(peopleDF.col("name"),peopleDF.col("age") + 10).show()
//5、起个别名:age+10 ==> age2
peopleDF.select(peopleDF.col("name"),(peopleDF.col("age") + 10).as("age2")).show()
//6、过滤,输出年龄大于19岁的
peopleDF.filter(peopleDF.col("age") > 19).show()
//7、根据某一列进行分组,然后再进行聚合操作:select age,count(1) from table group by age
peopleDF.groupBy("age").count().show()
//关闭资源
spark.stop()
}
}
3、DataFrame和RDD互操作方式一:反射
import org.apache.spark.sql.SparkSession
/**
* @author YuZhansheng
* @desc DataFrame和RDD互操作方式一
* @create 2019-03-05 9:57
*/
object DataFrameRDDApp {
def main(args: Array[String]): Unit = {
val spark = SparkSession.builder().appName("DataFrameRDDApp").master("local[2]").getOrCreate()
//使用SparkSession生成RDD
val rdd = spark.sparkContext.textFile("file:/root/DataSet/infos.txt")
//将RDD转换为DataFrame
//注意:需要先导入隐式转换
import spark.implicits._
val infoDF = rdd.map(_.split(",")).map(line => Info(line(0).toInt,line(1),line(2).toInt)).toDF()
//转成DataFrame之后,剩下的操作就和之前的例子一样了!
infoDF.show()
//把DataFrame注册为一张表,可以使用SparkSQL语句操作,更简单,参数是要注册成的表名
infoDF.createOrReplaceTempView("infos")
spark.sql("select * from infos where age > 30").show()
spark.stop()
}
case class Info(id:Int,name:String,age:Int)
}
4、DataFrame和RDD互操作方式二:通过编程方式实现
比较繁琐。
import org.apache.spark.sql.types.{IntegerType, StringType, StructField, StructType}
import org.apache.spark.sql.{Row, SparkSession}
/**
* @author YuZhansheng
* @desc DataFrame和RDD互操作方式二:通过编程的方式实现
* @create 2019-03-05 10:20
*/
object DataFrameRDD2App {
def main(args: Array[String]): Unit = {
val spark = SparkSession.builder().appName("DataFrameRDD2App").master("local[2]").getOrCreate()
//使用SparkSession生成RDD
val rdd = spark.sparkContext.textFile("file:/root/DataSet/infos.txt")
//转换为一个Row
val infoRDD = rdd.map(_.split(",")).map(line => Row(line(0).toInt,line(1),line(2).toInt))
//定义一个StructType
val structType = StructType(Array(StructField("id",IntegerType,true),
StructField("name",StringType,true),
StructField("age",IntegerType,true)))
//获取DataFrame,剩下的就是可以通过DataFrame的API进行操作,也可以使用SQL的方式进行操作
val infoDF = spark.createDataFrame(infoRDD,structType)
//打印DataFrame的信息看看是否正确
infoDF.printSchema()
infoDF.show()
}
}
总结:DataFrame和RDD互操作的两种方式,反射方式需要事先知道你的字段和字段类型;优先使用第一种,但是当第一种情况不能满足需求的时候(即事先不知道列信息),再考虑使用第二种编程方式。
5、DataFrame的其他API操作
import org.apache.spark.sql.SparkSession
/**
* @author YuZhansheng
* @desc DataFrame的其他操作
* @create 2019-03-05 10:46
*/
object DataFrameCase {
def main(args: Array[String]): Unit = {
val spark = SparkSession.builder().appName("DataFrameCase").master("local[2]").getOrCreate()
//使用SparkSession生成RDD
val rdd = spark.sparkContext.textFile("file:/root/DataSet/student.data")
//将RDD转换为DataFrame
//注意:需要先导入隐式转换
import spark.implicits._
//分隔符“|”前面要加上转义字符,不然会出错
val studentDF = rdd.map(_.split("\\|")).map(line => Student(line(0).toInt,line(1),line(2),line(3))).toDF()
//1、不加参数默认显示前20条
studentDF.show()
//2、显示30条,不截取
studentDF.show(30,false)
//3、获取前十行
studentDF.take(10)
//4、取得第一条
studentDF.first()
//5、截取指定的列
studentDF.select("name","email").show(30,false)
//6、获取name等于空或者等于NULL
studentDF.filter("name='' OR name='NULL'").show()
//7、获取名字以m开头的人
studentDF.filter("SUBSTR(name,0,1)='m'").show()
//8、按名字排序
studentDF.sort(studentDF("name")).show()
//降序排
studentDF.sort(studentDF("name").desc).show()
//9、按多个字段排序,默认升序
studentDF.sort("name","id").show()
//名字的升序,ID的降序排
studentDF.sort(studentDF("name").asc,studentDF("id").desc).show()
//10、对列进行重命名
studentDF.select(studentDF("name").as("student_name")).show()
//11、join操作
val studentDF2 = rdd.map(_.split("\\|")).map(line => Student(line(0).toInt,line(1),line(2),line(3))).toDF()
studentDF.join(studentDF2,studentDF.col("id") === studentDF2.col("id")).show
//关闭资源
spark.stop()
}
//使用反射方式将RDD转为DataFrame
case class Student(id:Int,name:String,phone:String,email:String)
}
6、Dataset概述及使用
虽然DataFrame提供了关系操作和更高的性能,但是他缺乏类型安全性,这会导致运行时错误,因此在Spark1.6中引入DataSetAPI,它结合了RDD和DataFrame这两方面的优势:静态类型、更容易实现RDD的功能特性,以及DataFrame卓越性能特性。在1.6版本中,DataFrame和Dataset还是单独的类,到2.0版本中,Spark将DataFrame和Dataset API统一起来,为开发人员提供了单一的API,DataFrame是一个特定的Dataset[T],其中T=行的类型,因此DataFrame和Dataset享有相同的方法。
import org.apache.spark.sql.SparkSession
/**
* @author YuZhansheng
* @desc Dataset操作
* @create 2019-03-06 15:45
*/
object DatasetApp {
def main(args: Array[String]): Unit = {
val spark = SparkSession.builder().appName("DatasetApp").master("local[2]").getOrCreate()
val path = "file:/root/DataSet/sales.csv"
//spark解析CSV文件
val df = spark.read.option("header","true").option("inferSchema","true").csv(path)
df.show
//注意:需要导入隐式转换
import spark.implicits._
//获取Dataset
val ds = df.as[Sales]
//只取出一行的itemId
ds.map(line => line.itemId).show()
spark.stop()
}
case class Sales(transactionId:Int,customerId:Int,itemId:Int,amountPaid:Double)
}