Apache spark pyspark:在窗口上计数

Apache spark pyspark:在窗口上计数,apache-spark,pyspark,apache-spark-sql,window-functions,distinct-values,Apache Spark,Pyspark,Apache Spark Sql,Window Functions,Distinct Values,我刚刚尝试在窗口上执行了一个countDistinct,但出现了以下错误: AnalysisException:不支持u'Distinct窗口函数: 计数(不同颜色#1926) 在pyspark中,有没有一种方法可以对窗口进行不同的计数 下面是一些示例代码: from pyspark.sql.window import Window from pyspark.sql import functions as F #function to calculate number of secon

我刚刚尝试在窗口上执行了一个
countDistinct
,但出现了以下错误:

AnalysisException:不支持u'Distinct窗口函数: 计数(不同颜色#1926)

在pyspark中,有没有一种方法可以对窗口进行不同的计数

下面是一些示例代码:

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

#function to calculate number of seconds from number of days
days = lambda i: i * 86400

df = spark.createDataFrame([(17, "2017-03-10T15:27:18+00:00", "orange"),
                    (13, "2017-03-15T12:27:18+00:00", "red"),
                    (25, "2017-03-18T11:27:18+00:00", "red")],
                    ["dollars", "timestampGMT", "color"])
                    
df = df.withColumn('timestampGMT', df.timestampGMT.cast('timestamp'))

#create window by casting timestamp to long (number of seconds)
w = (Window.orderBy(F.col("timestampGMT").cast('long')).rangeBetween(-days(7), 0))

df = df.withColumn('distinct_color_count_over_the_last_week', F.countDistinct("color").over(w))

df.show()
这是我希望看到的输出:

+-------+--------------------+------+---------------------------------------+
|dollars|        timestampGMT| color|distinct_color_count_over_the_last_week|
+-------+--------------------+------+---------------------------------------+
|     17|2017-03-10 15:27:...|orange|                                      1|
|     13|2017-03-15 12:27:...|   red|                                      2|
|     25|2017-03-18 11:27:...|   red|                                      1|
+-------+--------------------+------+---------------------------------------+
编辑:

正如noleto在下面的回答中提到的,自从pyspark 2.1在窗口上运行以来,现在有一个近似计数的不同函数


原始答案

我发现我可以使用collect_set和size函数的组合来模拟窗口上countDistinct的功能:

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

#function to calculate number of seconds from number of days
days = lambda i: i * 86400

#create some test data
df = spark.createDataFrame([(17, "2017-03-10T15:27:18+00:00", "orange"),
                    (13, "2017-03-15T12:27:18+00:00", "red"),
                    (25, "2017-03-18T11:27:18+00:00", "red")],
                    ["dollars", "timestampGMT", "color"])

#convert string timestamp to timestamp type             
df = df.withColumn('timestampGMT', df.timestampGMT.cast('timestamp'))

#create window by casting timestamp to long (number of seconds)
w = (Window.orderBy(F.col("timestampGMT").cast('long')).rangeBetween(-days(7), 0))

#use collect_set and size functions to perform countDistinct over a window
df = df.withColumn('distinct_color_count_over_the_last_week', F.size(F.collect_set("color").over(w)))

df.show()
这将导致前一周记录的颜色计数不同:

+-------+--------------------+------+---------------------------------------+
|dollars|        timestampGMT| color|distinct_color_count_over_the_last_week|
+-------+--------------------+------+---------------------------------------+
|     17|2017-03-10 15:27:...|orange|                                      1|
|     13|2017-03-15 12:27:...|   red|                                      2|
|     25|2017-03-18 11:27:...|   red|                                      1|
+-------+--------------------+------+---------------------------------------+

@鲍勃·斯温的回答很好,很有效!从那时起,Spark提供了一个相当于
countDistinct
的函数,
approx\u count\u distinct
,该函数使用效率更高,最重要的是,它支持在窗口上计算distinct

下面是“替换”下拉列表的代码:

#approx_count_distinct supports a window
df = df.withColumn('distinct_color_count_over_the_last_week', F.approx_count_distinct("color").over(w))

对于基数较小的列,结果应与“countDistinct”相同。当数据集增长很大时,你应该考虑调整参数<代码> RSD允许的最大估计误差,这允许你调整权衡精度/性能。

如果你的代码>席明区别< /代码>在多个列之间?code>collect\u set只能使用一个列名。这是一项有点难度的工作,但我做的一件事是创建一个新列,它是两列的串联。例如,如果有一个firstname列和一个lastname列,则添加第三列,这两列相加在一起。然后您可以使用这一个新列来执行collect_set.interest。我一直在使用的解决方法是在聚合中使用
countDistinct
执行
groupBy
,然后将
连接回分组的原始数据帧。我想知道哪种方法对大型集群更有效?我认为添加一个新列将使用更多的RAM,特别是如果您要处理大量列,或者如果列很大,但不会增加太多计算复杂性。我注意到使用orderBy时的性能问题,它将所有结果带回驱动程序。结果应该与“countDistinct”相同-对此有任何保证吗?如果我使用默认的rsd=0.05,这是否意味着对于基数<20,它将在100%的时间内返回正确的结果?