Spark-如何计算Spark中的百分位数?

时间:2018-06-19 16:12:11

标签: scala apache-spark

我试图获取单列数据框的0.8%。我以这种方式尝试过:

val limit80 = 0.8
val dfSize = df.count()
val perfentileIndex = dfSize*limit80 

dfSorted = df.sort()
val percentile80 = dfSorted .take(perfentileIndex).last()

但是我认为这对于大数据帧将失败,因为它们可能分布在不同的节点上。

有没有更好的方法来计算百分位数?或者我怎么能在同一台机器上拥有数据帧的所有行(即使那是非常反模式的),所以df.take(index)实际上会考虑整个数据集,而不仅仅是节点中的分区。 / p>

4 个答案:

答案 0 :(得分:7)

对于Spark 2.x,您可以使用approxQuantile,如以下示例所示:

val df = Seq(
  10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
  20, 21, 22, 23, 24, 25, 26, 27, 28, 29
).toDF("num")

df.stat.approxQuantile("num", Array(0.8), 0.1)
// res4: Array[Double] = Array(26.0)

请注意,第三个参数relativeError越小,计算成本就越高。这是API文档中的相关说明:

  

relativeError :要达到的相对目标精度(大于   或等于0)。如果设置为零,则将计算精确的分位数,   这可能非常昂贵。

答案 1 :(得分:2)

approx_percentilepercentile 是 SQL API 的一部分。

假设您有以下 DataFrame:

+--------+
|some_int|
+--------+
|       0|
|      10|
+--------+

以下是使用 expr hack 计算第 50 个百分位数的方法:

df.agg(expr("percentile(some_int, 0.5)").as("50_percentile"))
+-------------+
|50_percentile|
+-------------+
|          5.0|
+-------------+

我创建了一个名为 bebe 的库,它也通过 Scala API 公开这些方法(因此您无需编写调用 Scala 代码中的函数的字符串)。

df.agg(bebe_percentile(col("some_int"), lit(0.5)).as("50_percentile"))
+-------------+
|50_percentile|
+-------------+
|          5.0|
+-------------+

有关如何使用 bebe_approx_percentile 的说明,请参阅 bebe README。

答案 2 :(得分:1)

对于大型数据集,您可能应该采用近似方法

import org.apache.spark.sql.functions.{callUDF, lit}

df.agg(callUDF("percentile_approx", $"someColumn", lit(0.8)).as("percentile80"))

答案 3 :(得分:1)

您可以使用Spark SQL函数approx_percentile(col, percentage)

val df = Seq(0.5, 0.4, 0.1).toDF
df.agg(expr("approx_percentile(value, array(0.5))").as("percentile")).show
// +----------+
// |percentile|
// +----------+
// |     [0.4]|
// +----------+

https://spark.apache.org/docs/latest/api/sql/index.html#approx_percentile