如何按组使用approxQuantile?
How to use approxQuantile by group?
Spark 有 SQL 函数 percentile_approx()
,它的 Scala 对应函数是 df.stat.approxQuantile()
。
但是,Scala 对应项不能用于分组数据集,如 df.groupby("foo").stat.approxQuantile()
,如此处回答:。
但是在 SQL 语法中可以进行分组和百分位数。所以我想知道,也许我可以从 SQL percentile_approx
函数定义一个 UDF 并将其用于我的分组数据集?
Spark >= 3.1
Spark 3.1 中添加了相应的 SQL 函数 - 请参阅 SPARK-30569。
Spark < 3.1
虽然您不能在 UDF 中使用 approxQuantile
,并且您没有 percentile_approx
的 Scala 包装器,但您自己实现一个并不难:
import org.apache.spark.sql.functions._
import org.apache.spark.sql.Column
import org.apache.spark.sql.catalyst.expressions.aggregate.ApproximatePercentile
object PercentileApprox {
def percentile_approx(col: Column, percentage: Column, accuracy: Column): Column = {
val expr = new ApproximatePercentile(
col.expr, percentage.expr, accuracy.expr
).toAggregateExpression
new Column(expr)
}
def percentile_approx(col: Column, percentage: Column): Column = percentile_approx(
col, percentage, lit(ApproximatePercentile.DEFAULT_PERCENTILE_ACCURACY)
)
}
用法示例:
import PercentileApprox._
val df = (Seq.fill(100)("a") ++ Seq.fill(100)("b")).toDF("group").withColumn(
"value", when($"group" === "a", randn(1) + 10).otherwise(randn(3))
)
df.groupBy($"group").agg(percentile_approx($"value", lit(0.5))).show
+-----+------------------------------------+
|group|percentile_approx(value, 0.5, 10000)|
+-----+------------------------------------+
| b| -0.06336346702250675|
| a| 9.818985618591595|
+-----+------------------------------------+
df.groupBy($"group").agg(
percentile_approx($"value", typedLit(Seq(0.1, 0.25, 0.75, 0.9)))
).show(false)
+-----+----------------------------------------------------------------------------------+
|group|percentile_approx(value, [0.1,0.25,0.75,0.9], 10000) |
+-----+----------------------------------------------------------------------------------+
|b |[-1.2098351202406483, -0.6640768986666159, 0.6778253126144265, 1.3255676906697658]|
|a |[8.902067202468098, 9.290417382259626, 10.41767257153993, 11.067087075488068] |
+-----+----------------------------------------------------------------------------------+
一旦它位于 JVM 类路径中,您还可以添加 PySpark 包装器,使用类似于内置函数的逻辑。
Spark 有 SQL 函数 percentile_approx()
,它的 Scala 对应函数是 df.stat.approxQuantile()
。
但是,Scala 对应项不能用于分组数据集,如 df.groupby("foo").stat.approxQuantile()
,如此处回答:
但是在 SQL 语法中可以进行分组和百分位数。所以我想知道,也许我可以从 SQL percentile_approx
函数定义一个 UDF 并将其用于我的分组数据集?
Spark >= 3.1
Spark 3.1 中添加了相应的 SQL 函数 - 请参阅 SPARK-30569。
Spark < 3.1
虽然您不能在 UDF 中使用 approxQuantile
,并且您没有 percentile_approx
的 Scala 包装器,但您自己实现一个并不难:
import org.apache.spark.sql.functions._
import org.apache.spark.sql.Column
import org.apache.spark.sql.catalyst.expressions.aggregate.ApproximatePercentile
object PercentileApprox {
def percentile_approx(col: Column, percentage: Column, accuracy: Column): Column = {
val expr = new ApproximatePercentile(
col.expr, percentage.expr, accuracy.expr
).toAggregateExpression
new Column(expr)
}
def percentile_approx(col: Column, percentage: Column): Column = percentile_approx(
col, percentage, lit(ApproximatePercentile.DEFAULT_PERCENTILE_ACCURACY)
)
}
用法示例:
import PercentileApprox._
val df = (Seq.fill(100)("a") ++ Seq.fill(100)("b")).toDF("group").withColumn(
"value", when($"group" === "a", randn(1) + 10).otherwise(randn(3))
)
df.groupBy($"group").agg(percentile_approx($"value", lit(0.5))).show
+-----+------------------------------------+
|group|percentile_approx(value, 0.5, 10000)|
+-----+------------------------------------+
| b| -0.06336346702250675|
| a| 9.818985618591595|
+-----+------------------------------------+
df.groupBy($"group").agg(
percentile_approx($"value", typedLit(Seq(0.1, 0.25, 0.75, 0.9)))
).show(false)
+-----+----------------------------------------------------------------------------------+
|group|percentile_approx(value, [0.1,0.25,0.75,0.9], 10000) |
+-----+----------------------------------------------------------------------------------+
|b |[-1.2098351202406483, -0.6640768986666159, 0.6778253126144265, 1.3255676906697658]|
|a |[8.902067202468098, 9.290417382259626, 10.41767257153993, 11.067087075488068] |
+-----+----------------------------------------------------------------------------------+
一旦它位于 JVM 类路径中,您还可以添加 PySpark 包装器,使用类似于内置函数的逻辑。