在Spark数据帧中为每个组创建索引

Jon*_*aus 5 apache-spark apache-spark-sql

我在Spark中有一个带有2列的数据框,group_id并且value,其中value是double.我想根据group_id每个组的顺序对数据进行分组value,然后添加第三列index,表示value组中值的排序位置.

例如,考虑以下输入数据:

+--------+-----+
|group_id|value|
+--------+-----+
|1       |1.3  |
|2       |0.8  |
|1       |3.4  |
|1       |-1.7 |
|2       |2.3  |
|2       |5.9  |
|1       |2.7  |
|1       |0.0  |
+--------+-----+
Run Code Online (Sandbox Code Playgroud)

那么输出就像是

+--------+-----+-----+
|group_id|value|index|
+--------+-----+-----+
|1       |-1.7 |1    |
|1       |0.0  |2    |
|1       |1.3  |3    |
|1       |2.7  |4    |
|1       |3.4  |5    |
|2       |0.8  |1    |
|2       |2.3  |2    |
|2       |5.9  |3    |
+--------+-----+-----+
Run Code Online (Sandbox Code Playgroud)

如果索引是从0开始并且排序是升序还是降序,则不重要.

作为后续行动,请考虑extra原始数据中存在第三列的情况,该数据对于某些(group_id, value)组合采用多个值.一个例子是:

+--------+-----+-----+
|group_id|value|extra|
+--------+-----+-----+
|1       |1.3  |1    |
|1       |1.3  |2    |
|2       |0.8  |1    |
|1       |3.4  |1    |
|1       |3.4  |2    |
|1       |3.4  |3    |
|1       |-1.7 |1    |
|2       |2.3  |1    |
|2       |5.9  |1    |
|1       |2.7  |1    |
|1       |0.0  |1    |
+--------+-----+-----+
Run Code Online (Sandbox Code Playgroud)

有没有办法添加一index列,以便extra不考虑列但仍保留?在这种情况下的输出将是

+--------+-----+-----+-----+
|group_id|value|extra|index|
+--------+-----+-----+-----+
|1       |-1.7 |1    |1    |
|1       |0.0  |1    |2    |
|1       |1.3  |1    |3    |
|1       |1.3  |2    |3    |
|1       |2.7  |1    |4    |
|1       |3.4  |1    |5    |
|1       |3.4  |2    |5    |
|1       |3.4  |3    |5    |
|2       |0.8  |1    |1    |
|2       |2.3  |1    |2    |
|2       |5.9  |1    |3    |
+--------+-----+-----+-----+
Run Code Online (Sandbox Code Playgroud)

我知道可以通过复制数据,删除extra列来实现这一点

  1. 复制数据
  2. 删除extra
  3. 执行 distinct操作,这将导致原始示例中的数据
  4. index使用原始解决方案计算列
  5. 使用第二个示例中的数据加入结果

但是,这将涉及大量额外的计算和开销.

mto*_*oto 7

您可以使用Window函数创建基于以下value分区的排名列group_id:

from pyspark.sql.window import Window
from pyspark.sql.functions import rank, dense_rank
# Define window
window = Window.partitionBy(df['group_id']).orderBy(df['value'])
# Create column
df.select('*', rank().over(window).alias('index')).show()
+--------+-----+-----+
|group_id|value|index|
+--------+-----+-----+
|       1| -1.7|    1|
|       1|  0.0|    2|
|       1|  1.3|    3|
|       1|  2.7|    4|
|       1|  3.4|    5|
|       2|  0.8|    1|
|       2|  2.3|    2|
|       2|  5.9|    3|
+--------+-----+-----+
Run Code Online (Sandbox Code Playgroud)

因为,您首先选择'*',您还使用上面的代码保留所有其他变量.但是,您的第二个示例显示您正在查找该函数dense_rank(),该函数作为没有间隙的排名列:

df.select('*', dense_rank().over(window).alias('index'))
Run Code Online (Sandbox Code Playgroud)