Apache spark 如何创建与列大小相关的Pyspark窗口函数
尝试使用pyspark中的窗口函数在依赖于列的前几行上累积值时,我遇到了一个意外错误 下面是重现我遇到的错误的最小工作示例(MWE):Apache spark 如何创建与列大小相关的Pyspark窗口函数,apache-spark,pyspark,apache-spark-sql,window-functions,Apache Spark,Pyspark,Apache Spark Sql,Window Functions,尝试使用pyspark中的窗口函数在依赖于列的前几行上累积值时,我遇到了一个意外错误 下面是重现我遇到的错误的最小工作示例(MWE): from pyspark.sql import Window import pandas as pd df = sqlContext.createDataFrame( [("A", 0.1, pd.datetime(2020,12,1), 0), ("A"
from pyspark.sql import Window
import pandas as pd
df = sqlContext.createDataFrame( [("A", 0.1, pd.datetime(2020,12,1), 0),
("A", 2.1, pd.datetime(2020,12,5), 3),
("A", 1.1, pd.datetime(2020,12,7), 1),
("A", 3.1, pd.datetime(2020,12,9), 3),
],
["id", "value","timestamp", "previous_rows_to_consider"] )
df.show()
# +---+-----+-------------------+-------------------------+
# | id|value| timestamp|previous_rows_to_consider|
# +---+-----+-------------------+-------------------------+
# | A| 0.1|2020-12-01 00:00:00| 0|
# | A| 2.1|2020-12-05 00:00:00| 3|
# | A| 1.1|2020-12-07 00:00:00| 1|
# | A| 3.1|2020-12-09 00:00:00| 3|
# +---+-----+-------------------+-------------------------+
import pyspark.sql.functions as F
w = Window.partitionBy('id').orderBy( F.col('timestamp') ).rowsBetween( -F.col('previous_rows_to_consider'),0 )
df = df.withColumn('value_cumsum_on_previous_rows', F.sum('value').over(w) )
df.show()
这将产生ValueError:无法将列转换为布尔值:在构建数据帧布尔表达式时,请使用“&”表示“and”,使用“|”表示“or”,使用“~”表示“not”。
我还尝试了一些解决方法,使用rangeBetween
而不是rowsBetween
,但我得到了相同的错误
正如@murtihash在一篇相关文章中指出的,我确实怀疑rowsBetween
/rangeBetween
根本不接受列类型作为输入,但在一些在线资源中,我发现至少rangeBetween
应该(例如,请参阅提供的概述)
- 是否有人能够理解是什么阻止了我的MWE,或者确认
和介于
仅接受整数值作为输入?行之间的范围
- 如果是后一种情况,有人能建议一种解决方法来计算与列相关的范围/行数的累积和吗?
请注意,此解决方案适用于前面有行的窗口。如果需要跟随行,请删除
F.reverse
确实,您无法将列放入范围/行窗口。作为一种变通方法(灵感来源于):
请注意,此解决方案适用于前面有行的窗口。如果您需要以下行,请删除
F.reverse我试图通过将F.expr
的输入转换为“pure pyspark”来分解它,以便更好地理解单个步骤。我设法理解了这个命令的大部分功能。但是,我无法将该步骤转换为“纯pyspark”,因为(1)在slice
之后,仅使用F.expr
接受列输入;(2) 我找不到一种“纯pyspark”方法来对数组列的元素进行求和,这种方法不依赖于F.expr('aggregate(…)')
。pyspark不支持所有Spark SQL函数,当它支持时,有时它不支持使用列作为参数。不幸的是,这是pyspark的一个限制,但幸运的是,我们总是可以使用F.exprAlso,因为我想它可能对像我这样不熟悉F.expr
的人有用,让我在这里补充一下我对其用法的理解:(1)slice
只需要前面的第一行就可以考虑值列表的元素,(2)aggregate
,使用(x,y)->x+y
第三个参数对结果列的元素进行汇总,(3)我猜cast
要么指定聚合函数产生的列的类型,要么指定如何处理0/null值)。如果我错了,请告诉我。我认为你是对的。在没有演员阵容的情况下尝试,看看会出现什么错误;)这会告诉你演员阵容是什么好吧,我试过了,但我真的想不出来。看起来cast
中的值类似于一种偏移量(如果我将0更改为标量值x,则列cumsum将被移位为x)。无论如何,在这一点上,我觉得一切都很好。谢谢!我试图通过将F.expr
的输入转换为“pure pyspark”来分解它,以便更好地理解单个步骤。我设法理解了这个命令的大部分功能。但是,我无法将该步骤转换为“纯pyspark”,因为(1)在slice
之后,仅使用F.expr
接受列输入;(2) 我找不到一种“纯pyspark”方法来对数组列的元素进行求和,这种方法不依赖于F.expr('aggregate(…)')
。pyspark不支持所有Spark SQL函数,当它支持时,有时它不支持使用列作为参数。不幸的是,这是pyspark的一个限制,但幸运的是,我们总是可以使用F.exprAlso,因为我想它可能对像我这样不熟悉F.expr
的人有用,让我在这里补充一下我对其用法的理解:(1)slice
只需要前面的第一行就可以考虑值列表的元素,(2)aggregate
,使用(x,y)->x+y
第三个参数对结果列的元素进行汇总,(3)我猜cast
要么指定聚合函数产生的列的类型,要么指定如何处理0/null值)。如果我错了,请告诉我。我认为你是对的。在没有演员阵容的情况下尝试,看看会出现什么错误;)这会告诉你演员阵容是什么好吧,我试过了,但我真的想不出来。看起来cast
中的值类似于一种偏移量(如果我将0更改为标量值x,则列cumsum将被移位为x)。无论如何,在这一点上,我觉得一切都很好。谢谢!
df2 = df.withColumn(
'value_list',
F.reverse(
F.collect_list('value').over(Window.partitionBy('id').orderBy('timestamp'))
)
).withColumn(
'cumsum',
F.expr('''aggregate(
slice(value_list, 1, previous_rows_to_consider + 1),
cast(0 as double),
(x, y) -> x + y
)''')
)
df2.show()
+---+-----+-------------------+-------------------------+--------------------+------+
| id|value| timestamp|previous_rows_to_consider| value_list|cumsum|
+---+-----+-------------------+-------------------------+--------------------+------+
| A| 0.1|2020-12-01 00:00:00| 0| [0.1]| 0.1|
| A| 2.1|2020-12-05 00:00:00| 3| [2.1, 0.1]| 2.2|
| A| 1.1|2020-12-07 00:00:00| 1| [1.1, 2.1, 0.1]| 3.2|
| A| 3.1|2020-12-09 00:00:00| 3|[3.1, 1.1, 2.1, 0.1]| 6.4|
+---+-----+-------------------+-------------------------+--------------------+------+