我试图获取单列数据框的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>
答案 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_percentile 和 percentile 是 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