我们在操作DataFrame的方法中可以随时调用内置函数进行业务需要的处理,这对于我们构建附件的业务逻辑而言是可以极大的减少不必要的时间消耗,让我们聚焦在数据分析上,有助于提高工程师的生产力。
DataFrame天生就是"A distributed collection of data organized into named columns.",这就为数据的复杂分析建立了坚实的基础并提供了极大的方便性
Spark SQL 函数
类别 | 函数举例 |
---|---|
聚合函数 | countDistinct、sumDistinct |
集合函数 | sort_array、explode |
日期、时间函数 | hour、quarter、next_day |
数字函数 | asin、atan、sqrt、round |
开窗函数 | row_number |
字符串函数 | concat、format_number、regexp_extract |
其他函数 | isNaN、sha、randn、callUDF |
统计每日uv和销售额
根据每天的用户访问日志和用户购买日志,统计每日的uv和销售额
UV:每天都有很多用户来访问,但是每个用户可能每天都会访问很多次,所以uv,指的是对用户进行去重以后的访问次数(去重统计总数),这里使用内置函数 countDistinct
- 首先,对DataFrame调用groupBy()方法,对某一列进行分组
- 然后调用agg()方法,第一个参数,必须必须必须传入之前在groupBy()方法中出现的字段
- 第二个参数,传入countDistinct('userid),sum,first等,Spark提供的内置函数
- 内置函数中,传入的参数,也是用单引号作为前缀的
userAccessLogRowDF.groupBy("date")
.agg('date,countDistinct('userid))
.map(row => Row(row(1),row(2)))
.collect()
.foreach(println)
//开始进行每日销售额的统计
userSaleRowDF.groupBy("date")
.agg('date,sum('sale))
.map(row => Row(row(1),row(2)))
.collect()
.foreach(println)
完整代码
Scala版本countDistinct代码
package com.carve.spark
import org.apache.spark.SparkConf
import org.apache.spark.SparkContext
import org.apache.spark.sql.SQLContext
import org.apache.spark.sql.Row
import org.apache.spark.sql.types.StructType
import org.apache.spark.sql.types.StructField
import org.apache.spark.sql.types.StringType
import org.apache.spark.sql.types.IntegerType
import org.apache.spark.sql.functions._
object DailyUV {
def main(args: Array[String]){
val conf = new SparkConf()
.setMaster("local")
.setAppName("DailyUV")
val sc = new SparkContext(conf)
val sqlContext = new SQLContext(sc)
// 要使用Spark SQL的内置函数,就必须在这里导入SQLContext下的隐式转换
import sqlContext.implicits._
// 构造用户访问日志数据,并创建DataFrame
// 模拟用户访问日志,日志用逗号隔开,第一列是日期,第二列是用户id
val userAccessLog = Array(
"2015-10-01,1122",
"2015-10-01,1122",
"2015-10-01,1123",
"2015-10-01,1124",
"2015-10-01,1124",
"2015-10-02,1122",
"2015-10-02,1121",
"2015-10-02,1123",
"2015-10-02,1123");
val userAccessLogRDD = sc.parallelize(userAccessLog, 5)
// 将模拟出来的用户访问日志RDD,转换为DataFrame
// 首先,将普通的RDD,转换为元素为Row的RDD
val userAccessLogRowRDD = userAccessLogRDD
.map{ log => Row(log.split(",")(0), log.split(",")(1).toInt)}
// 构造DataFrame的元数据
val structType = StructType(Array(
StructField("date", StringType, true),
StructField("userid", IntegerType, true)))
// 使用SQLContext创建DataFrame
val userAccessLogRowDF = sqlContext.createDataFrame(userAccessLogRowRDD, structType)
userAccessLogRowDF.groupBy("date")
.agg('date, countDistinct('userid))
.map { row => Row(row(1), row(2)) }
.collect()
.foreach(println)
}
}
Scala版本sum代码
package com.carve.spark
import org.apache.spark.SparkConf
import org.apache.spark.SparkContext
import org.apache.spark.sql.SQLContext
import org.apache.spark.sql.Row
import org.apache.spark.sql.types.StructType
import org.apache.spark.sql.types.StructField
import org.apache.spark.sql.types.StringType
import org.apache.spark.sql.types.DoubleType
import org.apache.spark.sql.functions._
object DailySale {
def main(args: Array[String]){
val conf = new SparkConf()
.setMaster("local")
.setAppName("DailyUV")
val sc = new SparkContext(conf)
val sqlContext = new SQLContext(sc)
import sqlContext.implicits._
// 模拟数据
val userSaleLog = Array("2015-10-01,55.05,1122",
"2015-10-01,23.15,1133",
"2015-10-01,15.20,",
"2015-10-02,56.05,1144",
"2015-10-02,78.87,1155",
"2015-10-02,113.02,1123")
val userSaleLogRDD = sc.parallelize(userSaleLog, 5)
// 进行有效销售日志的过滤
val filteredUserSaleLogRDD = userSaleLogRDD
.filter {log =>if (log.split(",").length == 3) true else false }
val userSaleLogRowRDD = filteredUserSaleLogRDD
.map { log => Row(log.split(",")(0), log.split(",")(1).toDouble) }
val structType = StructType(Array(
StructField("date", StringType, true),
StructField("sale_amount", DoubleType, true)))
val userSaleLogDF = sqlContext.createDataFrame(userSaleLogRowRDD, structType)
userSaleLogDF.groupBy("date")
.agg('date, sum('sale_amount))
.map{ row => Row(row(1),row(2)) }
.collect()
.foreach(println)
}
}