Spark DSL风格实现连续登录问题

需求

统计用户连续交易的总额、连续登陆天数、连续登陆开始和结束时间、间隔天数

分析就不聊了,主要是熟悉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实现连续登录,通常代码运行不太行,能跑起来的代码没几个

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值