spark---dateframe和dateset
Dateframe与DateSet使用
1.代码
import org.apache.spark.sql.SparkSession /** * 计算部门的平均薪资和年龄 * * 需求: * 1、只统计年龄在20岁以上的员工 * 2、根据部门名称和员工性别为粒度来进行统计 * 3、统计出每个部门分性别的平均薪资和年龄 * */ object DepartmentAvgSalaryAndAgeStat { def main(args: Array[String]) { // 创建SparkSession val spark = SparkSession .builder() .appName("DepartmentAvgSalaryAndAgeStat") .master("local") .config("spark.sql.warehouse.dir", "C:\\Users\\Administrator\\Desktop\\spark-warehouse") .getOrCreate() // 导入spark的隐式转换 import spark.implicits._ // 导入spark sql的functions import org.apache.spark.sql.functions._ // 首先将两份数据文件加载进来,形成两个dataframe(untyped类型的操作入门) val employee = spark.read.json("C:\\Users\\Administrator\\Desktop\\employee.json") val department = spark.read.json("C:\\Users\\Administrator\\Desktop\\department.json") // 进行计算操作 employee // 先对employee进行过滤,只统计20岁以上的员工 .filter("age > 20") // 需要跟department数据进行join,然后才能根据部门名称和员工性别进行聚合 // 注意:untyped join,两个表的字段的连接条件,需要使用三个等号 下面的$是scala的语法 .join(department, $"depId" === $"id") // 根据部门名称和员工性别进行分组 .groupBy(department("name"), employee("gender")) // 最后执行聚合函数 agg是聚合函数,avg:求均值 .agg(avg(employee("salary")), avg(employee("age"))) // 执行action操作,将结果显示出来(注销下面的.show()方法也会显示) .show() // dataframe == dataset[Row] // dataframe的类型是Row,所以是untyped类型,弱类型 // dataset的类型通常是我们自定义的case class,所以是typed类型,强类型 // dataset开发,与rdd开发有很多的共同点 // 比如说,dataset api也分成transformation和action,transformation是lazy特性的 // action会触发实际的计算和操作 // dataset也是有持久化的概念的 } }
2.测试用例
---department.json
{"id": 1, "name": "Technical Department"} {"id": 2, "name": "Financial Department"} {"id": 3, "name": "HR Department"}
---employee.json
{"name": "Leo", "age": 25, "depId": 1, "gender": "male", "salary": 20000}
{"name": "Marry", "age": 30, "depId": 2, "gender": "female", "salary": 25000}
{"name": "Jack", "age": 35, "depId": 1, "gender": "male", "salary": 15000}
{"name": "Tom", "age": 42, "depId": 3, "gender": "male", "salary": 18000}
{"name": "Kattie", "age": 21, "depId": 3, "gender": "female", "salary": 21000}
Action的方法测试
1.代码
package cn.ibeifeng.spark import org.apache.spark.sql.SparkSession /** * action操作详解 * * collect、count、first、foreach、reduce、show、take * */ object ActionOperation { def main(args: Array[String]) { val spark = SparkSession .builder() .appName("ActionOperation") .master("local") .config("spark.sql.warehouse.dir", "C:\\Users\\Administrator\\Desktop\\spark-warehouse") .getOrCreate() import spark.implicits._ val employee = spark.read.json("C:\\Users\\Administrator\\Desktop\\employee.json") // collect:将分布式存储在集群上的分布式数据集(比如dataset),中的所有数据都获取到driver端来 employee.collect().foreach { println(_) } // count:对dataset中的记录数进行统计个数的操作 println(employee.count()) // first:获取数据集中的第一条数据 println(employee.first()) // foreach:遍历数据集中的每一条数据,对数据进行操作,这个跟collect不同,collect是将数据获取到driver端进行操作 // foreach是将计算操作推到集群上去分布式执行 // foreach(println(_))这种,真正在集群中执行的时候,是没用的,因为输出的结果是在分布式的集群中的,我是看不到的 employee.foreach { println(_) } // reduce:对数据集中的所有数据进行归约的操作,多条变成一条 // 用reduce来实现数据集的个数的统计 println(employee.map(employee => 1).reduce(_ + _)) // show,默认将dataset数据打印前20条 employee.show() // take,从数据集中获取指定条数 employee.take(3).foreach { println(_) } } }
jar包上传集群运行要在maven添加这三个插件
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifest>
<mainClass></mainClass>
</manifest>
</archive>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.2.1</version>
<executions>
<execution>
<goals>
<goal>exec</goal>
</goals>
</execution>
</executions>
<configuration>
<executable>java</executable>
<includeProjectDependencies>true</includeProjectDependencies>
<includePluginDependencies>false</includePluginDependencies>
<classpathScope>compile</classpathScope>
<mainClass>cn.spark.study.App</mainClass>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
</configuration>
</plugin>
在集群执行脚本
/usr/local/spark/bin/spark-submit \ --class cn.spark.SparkSQLDemo \ --master spark://spark2upgrade01:7077 \ --num-executors 1 \ --driver-memory 500m \ --executor-memory 500m \ --executor-cores 1 \ /usr/local/test_spark_app/spark2-upgrade-0.0.1-SNAPSHOT-jar-with-dependencies.jar \
Dateframe和Dateset相互转化及持久化
import org.apache.spark.sql.SparkSession /** * 基础操作 * * 持久化:cache、persist * 创建临时视图:createTempView、createOrReplaceTempView * 获取执行计划:explain * 查看schema:printSchema * 写数据到外部存储:write * dataset与dataframe互相转换:as、toDF * */ object BasicOperation { // 7.为下面dateframe和dateset相互转化做准备,这个要放到main外 case class Employee(name: String, age: Long, depId: Long, gender: String, salary: Long) def main(args: Array[String]) { val spark = SparkSession .builder() .appName("BasicOperation") .master("local") .config("spark.sql.warehouse.dir", "C:\\Users\\Administrator\\Desktop\\spark-warehouse") .getOrCreate() import spark.implicits._ val employee = spark.read.json("C:\\Users\\Administrator\\Desktop\\employee.json")// 持久化,如果要对一个dataset重复计算两次的话,那么建议先对这个dataset进行持久化再进行操作,避免重复计算 // employee.cache() // println(employee.count()) // println(employee.count()) // 创建临时视图,主要是为了,可以直接对数据执行sql语句 // employee.createOrReplaceTempView("employee") // spark.sql("select * from employee where age > 30").show() // 获取spark sql的执行计划 // dataframe/dataset,比如执行了一个sql语句获取的dataframe,实际上内部包含一个logical plan,逻辑执行计划 // 设计执行的时候,首先会通过底层的catalyst optimizer,生成物理执行计划,比如说会做一些优化,比如push filter // 还会通过whole-stage code generation技术去自动化生成代码,提升执行性能 // spark.sql("select * from employee where age > 30").explain() //查看源数据和spark推断的类型 // employee.printSchema() // 下面这个运行会错,写hdfs是肯定没有问题的 // val employeeWithAgeGreaterThen30DF = spark.sql("select * from employee where age > 30") // employeeWithAgeGreaterThen30DF.write.json("C:\\Users\\Administrator\\Desktop\\employeeWithAgeGreaterThen30DF.json")
// 7.dateframe和dateset相互转化 val employeeDS = employee.as[Employee] employeeDS.show() employeeDS.printSchema() val employeeDF = employeeDS.toDF() } }
partition重新分区和去重
1.代码
import org.apache.spark.sql.SparkSession /** * typed操作 */ object TypedOperation { case class Employee(name: String, age: Long, depId: Long, gender: String, salary: Long) def main(args: Array[String]) { val spark = SparkSession .builder() .appName("TypedOperation") .master("local") .config("spark.sql.warehouse.dir", "C:\\Users\\Administrator\\Desktop\\spark-warehouse") .getOrCreate() import spark.implicits._ val employee = spark.read.json("C:\\Users\\Administrator\\Desktop\\employee.json") val employeeDS = employee.as[Employee]
// 查看默认分区 // println(employeeDS.rdd.partitions.size) // coalesce和repartition操作 // 都是用来重新定义分区的 // 区别在于:coalesce,只能用于减少分区数量,而且可以选择不发生shuffle // repartiton,可以增加分区,也可以减少分区,必须会发生shuffle,相当于是进行了一次重分区操作 // val employeeDSRepartitioned = employeeDS.repartition(7); // 看一下它的分区情况 // println(employeeDSRepartitioned.rdd.partitions.size) // val employeeDSCoalesced = employeeDSRepartitioned.coalesce(3); // println(employeeDSCoalesced.rdd.partitions.size) // employeeDSCoalesced.show()
// distinct和dropDuplicates
// 都是用来进行去重的,区别在哪儿呢?
// distinct,是根据每一条数据,进行完整内容的比对和去重
// dropDuplicates,可以根据指定的字段进行去重
val distinctEmployeeDS = employeeDS.distinct();
distinctEmployeeDS.show()
val dropDuplicatesEmployeeDS = employeeDS.dropDuplicates(Seq("name"))
dropDuplicatesEmployeeDS.show()
}
}
其他函数
// except:获取在当前dataset中有,但是在另外一个dataset中没有的元素 // filter:根据我们自己的逻辑,如果返回true,那么就保留该元素,否则就过滤掉该元素 // intersect:获取两个数据集的交集 // employeeDS.except(employeeDS2).show() // employeeDS.filter { employee => employee.age > 30 }.show() // employeeDS.intersect(employeeDS2).show()
浙公网安备 33010602011771号