在Spark Dataframe上移动百分位数

时间:2017-08-30 14:26:13

标签: apache-spark

是否有一种干净的方法来计算Spark Dataframe上的移动百分位数。

我有一个巨大的数据框,我每15分钟聚合一次,我想计算每个部分的百分位数。

df.groupBy(window(col("date").cast("timestamp"), "15 minutes"))
  .agg(sum("session"),mean("session"),percentile_approx("session", 0.5))
  .show()

错误:未找到:value percentile_approx

所以我必须计算基本的东西,如总和和平均值,但我需要计算中位数和其他一些百分位数。

在Spark 2.1中有一种有效的方法吗?

因为在这里,似乎没有在API中实现的中值 percentile_approx Percentile_approx 函数。

我看到这个问题已被提出,但答案并非都是一致的解决方案。这对我来说非常模糊......所以我想知道2017年8月是否有一个好的有效解决方案。

当我浏览15分钟的窗口时,我想知道是否只是硬计算它不会起作用而不是近似值?

非常感谢您的关注,

祝你下午好!

PS:Scala或PySpark我不介意,两者都会更大!

2 个答案:

答案 0 :(得分:1)

如果您不需要滑动(重叠)窗口,可以使用groupBy执行此操作。 AFAIK没有百分位聚合函数,因此您需要实现自己的UDAF或使用以下方法:

val df = (1 to 100).map( i => (
  i/10, scala.util.Random.nextDouble)
  ).toDF("time","session")

val calcStats = udf((data:Seq[Double]) => {
  (data.sum,
   data.sum/data.size,
   data.sorted.apply(data.size/2) // is ~ median, replace with your desired logic
  )
})

df.groupBy($"time")
  .agg(collect_list($"session").as("sessions"))
  .withColumn("stats",calcStats($"sessions").cast("struct<sum:double,mean:double,median:double>"))
  .select($"time",$"stats.*")
  .orderBy($"time")
  .show

+----+------------------+-------------------+-------------------+
|time|               sum|               mean|             median|
+----+------------------+-------------------+-------------------+
|   0|3.5441618790222287| 0.3937957643358032| 0.3968893251191352|
|   1|3.6612518806543757| 0.3661251880654376| 0.4395039388994335|
|   2| 4.040992655970037|0.40409926559700365| 0.3522214051715915|
|   3| 4.583175830988081| 0.4583175830988081| 0.5800394949546751|
|   4| 3.849409207658501| 0.3849409207658501|0.43422232330495936|
|   5| 5.514681139649785| 0.5514681139649784| 0.6703416471647694|
|   6| 4.890227540935781| 0.4890227540935781| 0.5515164635420178|
|   7|4.1148083531280095|0.41148083531280094| 0.4384132796986667|
|   8| 5.723834881155167| 0.5723834881155166| 0.6415902834329499|
|   9| 5.559212938582014| 0.5559212938582014| 0.6816268800227596|
|  10|0.8867335786067405| 0.8867335786067405| 0.8867335786067405|
+----+------------------+-------------------+-------------------+

答案 1 :(得分:1)

好的,我猜我真傻。

我只需将 callUDF 添加到我之前的想法中: percentile_approx 。对不起意见

callUDF("percentile_approx", col("session"), lit(0.5))

因此,例如在我的案例中,我希望每分钟汇总两个月的历史数据集:

df.groupBy(window((col("date")/1000).cast("timestamp"), "1 minutes"))
.agg(sum("session"),mean("session"),callUDF("percentile_approx", col("session"), lit(0.5)))
.show()

(以毫秒为单位的时间戳,因此 / 1000