使用窗口和分区提取值

时间:2019-05-10 07:21:54

标签: apache-spark pyspark

我在pyspark中有一个数据框

id | value

1     0
1     1
1     0
2     1
2     0
3     0
3     0
3     1

我想提取同一id组中value列中第一次出现1之后的所有行。我已经创建了带有ID分区的Window,但是不知道如何获取值1之后的行。

我希望结果是

 id | value

    1     1
    1     0
    2     1
    2     0
    3     1

2 个答案:

答案 0 :(得分:1)

以下解决方案可能与此有关(对于小数据来说,它工作得很好,但如果id在多个分区上,则可能在大数据中引起问题)

df = sqlContext.createDataFrame([
         [1, 0],
         [1, 1],
         [1, 0],
         [2, 1],
         [2, 0],
         [3, 0],
         [3, 0],
         [3, 1]
    ],
    ['id', 'Value']
)
df.show()
+---+-----+
| id|Value|
+---+-----+
|  1|    0|
|  1|    1|
|  1|    0|
|  2|    1|
|  2|    0|
|  3|    0|
|  3|    0|
|  3|    1|
+---+-----+

#importing Libraries
from pyspark.sql import functions as F
from pyspark.sql.window import Window as W
import sys

#This way we can generate a cumulative sum for values 
df.withColumn(
    "sum",
    F.sum(
        "value"
    ).over(W.partitionBy(["id"]).rowsBetween(-sys.maxsize, 0))
).show()
+---+-----+-----+
| id|Value|sum  |
+---+-----+-----+
|  1|    0|    0|
|  1|    1|    1|
|  1|    0|    1|
|  3|    0|    0|
|  3|    0|    0|
|  3|    1|    1|
|  2|    1|    1|
|  2|    0|    1|
+---+-----+-----+

#Filter all those which are having sum > 0
df.withColumn(
    "sum",
    F.sum(
        "value"
    ).over(W.partitionBy(["id"]).rowsBetween(-sys.maxsize, 0))
).where("sum > 0").show()

+---+-----+-----+
| id|Value|sum  |
+---+-----+-----+
|  1|    1|    1|
|  1|    0|    1|
|  3|    1|    1|
|  2|    1|    1|
|  2|    0|    1|
+---+-----+-----+
  

在运行此命令之前,必须确保与ID相关的数据应进行分区,并且ID不能位于2个分区上。

答案 1 :(得分:0)

理想情况下,您需要:

  1. 创建一个由id分区的窗口,并按照数据框已经存在的相同顺序进行排序
  2. 仅保留窗口中前面有“ 1”的行

AFAIK,Spark的Windows中没有查找功能。但是,您可以遵循这个想法并做出一些努力。首先创建数据并导入函数和窗口。

import pyspark.sql.functions as F
from pyspark.sql.window import Window

l = [(1, 0), (1, 1), (1, 0), (2, 1), (2, 0), (3, 0), (3, 0), (3, 1)]
df = spark.createDataFrame(l, ['id', 'value'])

然后,让我们在数据框上添加一个索引(它是免费的)以便能够订购窗口。

indexedDf = df.withColumn("index", F.monotonically_increasing_id())

然后,我们创建一个窗口,该窗口仅查看当前行之前的值,并按该索引排序并按ID进行分区。

w = Window.partitionBy("id").orderBy("index").rowsBetween(Window.unboundedPreceding, 0)

最后,我们使用该窗口来收集每行的一组先前值,并过滤掉不包含1的那些值。 (可选)我们以index的顺序退货,因为窗口不保留id列的订单。

indexedDf\
    .withColumn('set', F.collect_set(F.col('value')).over(w))\
    .where(F.array_contains(F.col('set'), 1))\
    .orderBy("index")\
    .select("id", "value").show()

+---+-----+
| id|value|
+---+-----+
|  1|    1|
|  1|    0|
|  2|    1|
|  2|    0|
|  3|    1|
+---+-----+