Pyspark数据框中的orderBy如何影响Window.partitionBy?

时间:2018-12-06 08:50:21

标签: pyspark window sql-order-by

我通过一个例子来解释我的问题:
让我们假设我们有一个如下数据框:

original_df = sc.createDataFrame([('x', 10,), ('x', 15,), ('x', 10,), ('x', 25,), ('y', 20,), ('y', 10,), ('y', 20,)], ["key", "price"] )
original_df.show()

输出:

+---+-----+
|key|price|
+---+-----+
|  x|   10|
|  x|   15|
|  x|   10|
|  x|   25|
|  y|   20|
|  y|   10|
|  y|   20|
+---+-----+

并假设我想使用prices为每个key获取window的列表:

w = Window.partitionBy('key')
original_df.withColumn('price_list', F.collect_list('price').over(w)).show()

输出:

+---+-----+----------------+
|key|price|      price_list|
+---+-----+----------------+
|  x|   10|[10, 15, 10, 25]|
|  x|   15|[10, 15, 10, 25]|
|  x|   10|[10, 15, 10, 25]|
|  x|   25|[10, 15, 10, 25]|
|  y|   20|    [20, 10, 20]|
|  y|   10|    [20, 10, 20]|
|  y|   20|    [20, 10, 20]|
+---+-----+----------------+

到目前为止一切都很好。
但是,如果要获取有序列表,然后将orderBy添加到窗口w中,我将得到:

w = Window.partitionBy('key').orderBy('price')
original_df.withColumn('ordered_list', F.collect_list('price').over(w)).show()

输出:

+---+-----+----------------+
|key|price|    ordered_list|
+---+-----+----------------+
|  x|   10|        [10, 10]|
|  x|   10|        [10, 10]|
|  x|   15|    [10, 10, 15]|
|  x|   25|[10, 10, 15, 25]|
|  y|   10|            [10]|
|  y|   20|    [10, 20, 20]|
|  y|   20|    [10, 20, 20]|
+---+-----+----------------+

这意味着orderBy(种类)也更改了窗口中的行(与rowsBetween一样)!这是不应该做的。

不过,我可以通过在窗口中指定rowsBetween来解决它,并获得预期的结果,

w = Window.partitionBy('key').orderBy('price').rowsBetween(Window.unboundedPreceding, Window.unboundedFollowing)

有人可以解释为什么orderBy以这种方式影响window吗?

2 个答案:

答案 0 :(得分:3)

Spark窗口由三个部分指定:分区,顺序和框架。

  1. 如果未指定任何部分,则将整个数据集视为一个窗口。
  2. 使用一列指定分区时,将为该列的每个不同值创建一个窗口。如果仅指定分区,则当对某行评估when时,将考虑该分区中的所有行。这就是为什么您看到分区x中所有行的所有4个值[10、15、10、25]。
  3. 指定分区和顺序时,然后在评估行函数时,它将采用分区中行的排名顺序,并且所有具有相同或较低值(如果指定了默认的asc顺序)的行都将包括在内。在您的情况下,第一行包括[10,10],因为分区中有2行具有相同的排名。
  4. 如果指定了框架规格的rowBetween和rangeBetween,则行评估将仅选择那些与框架规则匹配的行。例如unbounded并且指定了currentRow它将选择当前行以及在其之前出现的所有行。如果指定了orderBy,它将相应地更改在当前行之前出现的行。

专门针对您的问题,orderBy不仅可以对分区的数据进行排序,而且还可以更改行框架的选择

下面是不同的windowspec和相应的输出

Window.orderBy()
+---+-----+----------------------------+
|key|price|price_list                  |
+---+-----+----------------------------+
|x  |15   |[15, 10, 10, 20, 10, 25, 20]|
|x  |10   |[15, 10, 10, 20, 10, 25, 20]|
|y  |10   |[15, 10, 10, 20, 10, 25, 20]|
|y  |20   |[15, 10, 10, 20, 10, 25, 20]|
|x  |10   |[15, 10, 10, 20, 10, 25, 20]|
|x  |25   |[15, 10, 10, 20, 10, 25, 20]|
|y  |20   |[15, 10, 10, 20, 10, 25, 20]|
+---+-----+----------------------------+

Window.partitionBy('key')
+---+-----+----------------+
|key|price|      price_list|
+---+-----+----------------+
|  x|   15|[15, 10, 10, 25]|
|  x|   10|[15, 10, 10, 25]|
|  x|   10|[15, 10, 10, 25]|
|  x|   25|[15, 10, 10, 25]|
|  y|   20|    [20, 10, 20]|
|  y|   10|    [20, 10, 20]|
|  y|   20|    [20, 10, 20]|
+---+-----+----------------+

Window.partitionBy('key').orderBy('price')
+---+-----+----------------+
|key|price|    ordered_list|
+---+-----+----------------+
|  x|   10|        [10, 10]|
|  x|   10|        [10, 10]|
|  x|   15|    [10, 10, 15]|
|  x|   25|[10, 10, 15, 25]|
|  y|   10|            [10]|
|  y|   20|    [10, 20, 20]|
|  y|   20|    [10, 20, 20]|
+---+-----+----------------+

w = Window.partitionBy('key').orderBy(F.desc('price'))
+---+-----+----------------+
|key|price|    ordered_list|
+---+-----+----------------+
|  x|   25|            [25]|
|  x|   15|        [25, 15]|
|  x|   10|[25, 15, 10, 10]|
|  x|   10|[25, 15, 10, 10]|
|  y|   20|        [20, 20]|
|  y|   20|        [20, 20]|
|  y|   10|    [20, 20, 10]|
+---+-----+----------------+

Window.partitionBy('key').orderBy('price').rowsBetween(Window.unboundedPreceding, Window.currentRow)
+---+-----+----------------+
|key|price|    ordered_list|
+---+-----+----------------+
|  x|   10|            [10]|
|  x|   10|        [10, 10]|
|  x|   15|    [10, 10, 15]|
|  x|   25|[10, 10, 15, 25]|
|  y|   10|            [10]|
|  y|   20|        [10, 20]|
|  y|   20|    [10, 20, 20]|
+---+-----+----------------+

Window.partitionBy('key').rowsBetween(Window.unboundedPreceding, Window.currentRow)
+---+-----+----------------+
|key|price|    ordered_list|
+---+-----+----------------+
|  x|   15|            [15]|
|  x|   10|        [15, 10]|
|  x|   10|    [15, 10, 10]|
|  x|   25|[15, 10, 10, 25]|
|  y|   10|            [10]|
|  y|   20|        [10, 20]|
|  y|   20|    [10, 20, 20]|
+---+-----+----------------+

答案 1 :(得分:1)

@ManojSingh的回答是完美的。我仍然想分享自己的观点,以便为您提供帮助。

Window.partitionBy('key')的作用类似于数据框中每个groupBy的{​​{1}},使您可以对所有key执行相同的操作。

orderBy通常在可排序的列中执行时才有意义。以一个名为'month'的列为例,该列包含一年中的所有月份(1,2,3 ... 12),另外一个名为'price'的列具有每个月的价格。如果要求和,均值或每个月以及前面所有值的总和,请使用orderBy。例如,这可以很好地计算月份之间的差异。