spark sql窗口大小设定:rowsBetween方法的使用

准备工作

  • 准备依赖库
import org.apache.spark.sql.expressions.Window
import org.apache.spark.sql.types._
import org.apache.spark.sql.functions._

  • 准备数据
case class Salary(depName: String, empNo: Long, name: String, 
    salary: Long, hobby: Seq[String])
val empsalary = Seq(
  Salary("sales",     1,  "Alice",  5000, List("game",  "ski")),
  Salary("personnel", 2,  "Olivia", 3900, List("game",  "ski")),
  Salary("sales",     3,  "Ella",   4800, List("skate", "ski")),
  Salary("sales",     4,  "Ebba",   4800, List("game",  "ski")),
  Salary("personnel", 5,  "Lilly",  3500, List("climb", "ski")),
  Salary("develop",   7,  "Astrid", 4200, List("game",  "ski")),
  Salary("develop",   8,  "Saga",   6000, List("kajak", "ski")),
  Salary("develop",   9,  "Freja",  4500, List("game",  "kajak")),
  Salary("develop",   10, "Wilma",  5200, List("game",  "ski")),
  Salary("develop",   11, "Maja",   5200, List("game",  "farming"))).toDS
empsalary.createTempView("empsalary")
empsalary.show()



范围Frame
可以使用范围函数来改变Frame的边界。通过范围函数可以把计算(比如:sum,min,max,avg等操作)限定在一定的范围(基于当前行的向前或向后的条数)之内。我们先来看一个例子来直观的理解一下什么叫一定范围。

```正常的求和的例子```

val overCategory = Window.partitionBy('depName)
val df = empsalary.withColumn("salaries", collect_list('salary) over overCategory).
								withColumn("total_salary", sum('salary) over overCategory)

df.select("depName", "empNo", "name", "salary", "salaries", "total_salary").show(false)
+---------+-----+------+------+------------------------------+------------+     
|depName  |empNo|name  |salary|salaries                      |total_salary|
+---------+-----+------+------+------------------------------+------------+
|develop  |7    |Astrid|4200  |[4200, 6000, 4500, 5200, 5200]|25100       |
|develop  |8    |Saga  |6000  |[4200, 6000, 4500, 5200, 5200]|25100       |
|develop  |9    |Freja |4500  |[4200, 6000, 4500, 5200, 5200]|25100       |
|develop  |10   |Wilma |5200  |[4200, 6000, 4500, 5200, 5200]|25100       |
|develop  |11   |Maja  |5200  |[4200, 6000, 4500, 5200, 5200]|25100       |
|sales    |1    |Alice |5000  |[5000, 4800, 4800]            |14600       |
|sales    |3    |Ella  |4800  |[5000, 4800, 4800]            |14600       |
|sales    |4    |Ebba  |4800  |[5000, 4800, 4800]            |14600       |
|personnel|2    |Olivia|3900  |[3900, 3500]                  |7400        |
|personnel|5    |Lilly |3500  |[3900, 3500]                  |7400        |
+---------+-----+------+------+------------------------------+------------+


```添加了范围函数后的结果```


假设我们希望计算

val overCategory = Window.partitionBy('depName).rowsBetween(Window.currentRow, 1)
val df = empsalary.withColumn("salaries", collect_list('salary) over overCategory).
				withColumn("total_salary", sum('salary) over overCategory)

df.select("depName", "empNo", "name", "salary", "salaries", "total_salary").show(false)


通过这种方式得到的结果如下:

+---------+-----+------+------+------------+------------+                       
|depName  |empNo|name  |salary|salaries    |total_salary|
+---------+-----+------+------+------------+------------+
|develop  |7    |Astrid|4200  |[4200, 6000]|10200       |
|develop  |8    |Saga  |6000  |[6000, 4500]|10500       |
|develop  |9    |Freja |4500  |[4500, 5200]|9700        |
|develop  |10   |Wilma |5200  |[5200, 5200]|10400       |
|develop  |11   |Maja  |5200  |[5200]      |5200        |
|sales    |1    |Alice |5000  |[5000, 4800]|9800        |
|sales    |3    |Ella  |4800  |[4800, 4800]|9600        |
|sales    |4    |Ebba  |4800  |[4800]      |4800        |
|personnel|2    |Olivia|3900  |[3900, 3500]|7400        |
|personnel|5    |Lilly |3500  |[3500]      |3500        |
+---------+-----+------+------+------------+------------+


可以看到,通过使用范围函数:rowsBetween,把计算限定在一个范围之内。

这相当于一个滑动窗口,这种方式对于求时间序列窗口的数据统计非常实用。

范围Frame(Range Frane)的基本概念
Range Frame是基于与当前输入行的位置的逻辑偏移量,并且具有与ROW帧相似的语法。逻辑偏移是当前输入行的排序表达式的值与Frame的边界行的相同表达式的值之间的差。

由于此定义,当使用RANGE帧时,仅允许单个排序表达式。另外,对于RANGE帧,就边界计算而言,与当前输入行具有相同排序表达式值的所有行都被视为同一行。

使用范围Frame的步骤
使用范围Frame时需要按照以下步骤进行:

1、通过Window.partitionBy创建一个或多个列;
2、通常需要使用 orderBy来对数据进行排序;
3、进行以上两步后,接着再使用rangeBetweenorrowsBetween`函数
4、每一行都有一个对应的Frame
5、Frame的边界通过rangeBetween or rowsBetween进行控制
6、Aggregate/Window函数可以被用在每个row+frame上,来产生单一的值
范围函数
范围函数有两个,定义如下:

def rowsBetween( start: Long, end: Long): WindowSpec

def rangeBetween(start: Long, end: Long): WindowSpec

两个范围函数都接收两个参数,[start,end]包括边界在内。这两个参数,可以是以下的值:

  • Window.unboundedPreceding
  • Window.unboundedFollowing
  • Window.currentRow
  • 整数:相对于Window.currentRow的值,可以是负值或正值。

范围函数使用说明
rowsBetween函数

函数使用说明
.rowsBetween(Window.currentRow, 1)按相对于当前行的下1行进行计算,比如:sum,max等
.rowsBetween(Window.currentRow, 2)按相对于当前行的下2行进行计算,比如:sum,max等
.rowsBetween(-1, Window.currentRow)按相对于当前行的前一行进行计算
.rowsBetween(-2, Window.currentRow) 按相对于当前行的前两行进行计算
.rowsBetween(-1, 1)按相对于当前行的前一行和 后一行进行计算
.rowsBetween(Window.unboundedPreceding, Window.currentRow)计算基于当前行向前,分区内的所有行
.rowsBetween(Window.currentRow, Window.unboundedFollowing) 计算基于当前行向后,分区内的所有行
.rowsBetween(Window.unboundedPreceding, Window.unboundedFollowing)  窗口内所有的行



rangeBetween根据窗口中的行值获取帧边界。与rowsBetween比较的区别是,它与当前行的值进行比较。另外要注意,在比较时,若行的值相等,则会当成一样对待。

它的参数意义如下:

参数名说明
Window.currentRow该参数的值为:0
Window.unboundedPreceding该参数的值为:LongMinValue
Window.unboundedFollowing  该参数的值为:LongMaxValue


使用举例:

使用例子 说明
rangeBetween(Window.currentRow,300)从当前行开始往后的第1行,取值>300的第行。若当前行的值为:100,则取: >400的行
rangeBetween(-100,Window.currentRow)从当前行开始往前,和第1行的值比较,若当前行的前1行的值比当前行的值小100,则取值
rangeBetween(Window.unboundedPreceding,200) 取:当前行后一行>200,当前行的前一行边界不限
rangeBetween(-100,Window.unboundedFollowing) 取:小于当前行值100的行,当前行的后面的行值不限
rangeBetween(-100,300)若当前行的值为:100,则取:0<v<400的前一行和后一行

要注意的是:若比较的行的值相等,则会当成一行对待,会直接取该行

rangeBetween使用实战

val overCategory = Window.partitionBy('depName).orderBy("salary").rangeBetween(Window.currentRow, 300)
val df = empsalary.withColumn("salaries", collect_list('salary) over overCategory).
				withColumn("total_salary", sum('salary) over overCategory)

df.select("depName", "empNo", "name", "salary", "salaries", "total_salary").show(false)



输出结果:

+---------+-----+------+------+------------------+------------+
|depName  |empNo|name  |salary|salaries          |total_salary|
+---------+-----+------+------+------------------+------------+
|develop  |7    |Astrid|4200  |[4200, 4500]      |8700        |
|develop  |9    |Freja |4500  |[4500]            |4500        |
|develop  |10   |Wilma |5200  |[5200, 5200]      |10400       |
|develop  |11   |Maja  |5200  |[5200, 5200]      |10400       |
|develop  |8    |Saga  |6000  |[6000]            |6000        |
|sales    |3    |Ella  |4800  |[4800, 4800, 5000]|14600       |
|sales    |4    |Ebba  |4800  |[4800, 4800, 5000]|14600       |
|sales    |1    |Alice |5000  |[5000]            |5000        |
|personnel|5    |Lilly |3500  |[3500]            |3500        |
|personnel|2    |Olivia|3900  |[3900]            |3900        |
+---------+-----+------+------+------------------+------------+


注意:5200这个值,有相同的行,则当成一样的行对待。

小结
本文讲述了窗口函数的范围函数的使用。
————————————————
版权声明:本文为CSDN博主「一 铭」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zg_hover/article/details/109400563

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值