需求
统计用户连续交易的总额、连续登陆天数、连续登陆开始和结束时间、间隔天数
分析就不聊了,主要是熟悉DSL语句
数据预览
id,datestr,amount
1,2019-02-08,6214.23
1,2019-02-08,6247.32
1,2019-02-09,85.63
1,2019-02-09,967.36
1,2019-02-10,85.69
1,2019-02-12,769.85
1,2019-02-13,943.86
1,2019-02-14,538.42
1,2019-02-15,369.76
1,2019-02-16,369.76
1,2019-02-18,795.15
1,2019-02-19,715.65
1,2019-02-21,537.71
2,2019-02-08,6214.23
2,2019-02-08,6247.32
2,2019-02-09,85.63
2,2019-02-09,967.36
2,2019-02-10,85.69
2,2019-02-12,769.85
2,2019-02-13,943.86
2,2019-02-14,943.18
2,2019-02-15,369.76
2,2019-02-18,795.15
2,2019-02-19,715.65
2,2019-02-21,537.71
3,2019-02-08,6214.23
3,2019-02-08,6247.32
3,2019-02-09,85.63
3,2019-02-09,967.36
3,2019-02-10,85.69
3,2019-02-12,769.85
3,2019-02-13,943.86
3,2019-02-14,276.81
3,2019-02-15,369.76
3,2019-02-16,369.76
3,2019-02-18,795.15
3,2019-02-19,715.65
3,2019-02-21,537.71
Spark SQL
-- 只是SQL语句是这样,Spark SQL不是这样写的,你要用spark.sql(sql语句)执行
select t1.id
,t1.grp
,round(sum(t1.sum_amount),3) as total_amount -- 连续交易总额
,count(1) as total_days -- 连续登录天数
,min(datestr) as start_date -- 连续登录开始的时间
,max(datestr) as end_date -- 连续登录结束的时间
,datediff(t1.grp,lag(t1.grp,1) over(partition by t1.id order by t1.grp)) as interval_days -- 间隔天数
from (
select id
,datestr
,round(sum(amount),3) as sum_amount
,date_sub(datestr,row_number() over(partition by id order by datestr)) as grp
from deal_tb
group by id,datestr
) t1 group by t1.id,t1.grp;
Spark DSL语句
语法不一定是最优的,只是尽可能的使用更多的函数来了解一下DSL语句,其实SQL语句可使用的语法,DSL也差不多,可能就是方法名和一些调用不一样。自定义函数后期会添加。
// 导入对应的包,可使用开窗函数 其他的函数报错,直接shift + alt + ctrl解决就行了
import org.apache.spark.sql.functions._
import org.apache.spark.sql.expressions.Window
// 代码主题
val sparkConf = new SparkConf().setMaster("local[*]").setAppName("sparkSQL")
val spark = SparkSession.builder().config(sparkConf).getOrCreate()
// 导入这个的话,可以使用$"columnName",不导入的话,select等语句都是重载失败,而且你的$还报错
import spark.implicits._
// 加载数据 function 02 这个写法是在官网找的,个人觉得不错
val dataFrame = spark.read
.format("csv")
.option("sep", ",")
.option("inferSchema", "true")
.option("header", "true")
.load("D:\\code\\Spark\\PersonalDemo\\src\\main\\data\\test.txt")
// 数据去重
val dataset = dataFrame
.groupBy($"id", $"datestr")
.sum("amount")
// 重命名列名 dataset类型不是很了解,可能方法类似,但为了更熟悉dataframe,还是转一下
val v1 = dataset.toDF().withColumnRenamed("sum(amount)", "amounts")
val v2 = v1.select(
$"id"
, $"datestr"
, $"amounts"
// window这个函数是Spark包里面的,别导其他的包 date_sub官网说,3.0之后才支持两个列相减,之前的话,第二
, date_sub($"datestr", row_number() over (Window.partitionBy("id").orderBy("datestr"))).as("grp")
)
// agg里面可以放一堆聚合函数,可理解为批处理?
v2.groupBy($"id",$"grp").agg(
round(sum($"amounts"),3).as("日交易额")
,count($"datestr").as("连续登录时间")
,min($"datestr").as("连续登录开始的时间")
,max($"datestr").as("连续登录结束的时间")
)
// 添加列,具体参数,idea都会有提示的,仔细看一下就行
.withColumn("间隔天数",datediff($"grp",lag($"grp",1) over(Window.partitionBy($"id").orderBy($"grp"))))
.show()
// TODO 关闭环境
spark.close()
UDF简单使用
// 还有很多种,我喜欢用SQL一点,所以也没了解更多,如果有人总结了其他用法,可以评论区给我留言,我会贴一下你的连接,以后再学习就方便很多
// function 1
// 假设上面的date是string类型哈,非要犟的话,你就强制转换就好了
// 抽取日期的月份
spark.udf.register("chooseMonth", (str: String) => str.split("-")[1])
//spark sql中使用udf
spark.sql("select datestr,chooseMonth(datestr) as month from test").show()
// function 2
import org.apache.spark.sql.functions._
// 注册udf
val chooseMonth= udf((str: String) => str.split("-")[1])
//使用udf
userDF.select($"datestr", chooseMonth($"datestr").as("mon")).show()
写在后面
从0到了解用法,记录一下成长过程,以及踩得坑。
如何了解Spark DSL语法,我认为优先级顺序考虑如下
1、官网有案例就看案例,官网解释的一般都是最全的 (通常很难找到,对于初学者来说)
2、A.xxx ,那是不是说A是一个类,搜A的方法,这里是dataFrame的常用方法
类似于这种
不对啊,你这开窗函数怎么写的,这也不是A的方法啊,我个人理解哈,SQL有开窗,如果DSL模块没有这种开窗的话,那DSL存在的意义在哪里嘞,要多想能不能偷懒,
3、搜一下一些案例,优先搜DSL实现连续登录,通常代码运行不太行,能跑起来的代码没几个