如何在spark中的DataFrame中计算列的百分位数?

时间:2016-06-06 23:30:33

标签: scala apache-spark apache-spark-sql spark-dataframe

我正在尝试计算DataFrame中列的百分位数?我无法在Spark聚合函数中找到任何percentile_approx函数。

例如在Hive中我们有percentile_approx,我们可以通过以下方式使用它

hiveContext.sql("select percentile_approx("Open_Rate",0.10) from myTable); 

但出于性能原因,我想使用Spark DataFrame。

样本数据集

|User ID|Open_Rate|
------------------- 
|A1     |10.3     |
|B1     |4.04     |
|C1     |21.7     |
|D1     |18.6     |

我想知道有多少用户分为10个百分位或20个百分位,依此类推。我想做这样的事情

df.select($"id",Percentile($"Open_Rate",0.1)).show

3 个答案:

答案 0 :(得分:7)

从Spark2.0开始,事情变得越来越容易,只需在DataFrameStatFunctions中使用此函数,如:

df.stat.approxQuantile("Open_Rate",Array(0.25,0.50,0.75),0.0)

DataFrameStatFunctions中的DataFrame也有一些有用的统计函数。

答案 1 :(得分:4)

SparkSQL和Scala数据框/数据集API由同一引擎执行。等效操作将生成等效的执行计划。您可以使用explain查看执行计划。

sql(...).explain
df.explain

当涉及到您的特定问题时,混合SparkSQL和Scala DSL语法是一种常见模式,因为正如您所发现的,它们的功能尚不相同。 (另一个例子是SQL explode()和DSL' explode()之间的区别,后者更强大,但由于编组而效率更低。)

这样做的简单方法如下:

df.registerTempTable("tmp_tbl")
val newDF = sql(/* do something with tmp_tbl */)
// Continue using newDF with Scala DSL

如果你采用简单的方法,你需要记住的是临时表名是集群全局的(最高1.6.x)。因此,如果代码可能在同一个集群上同时运行多次,则应使用随机表名。

在我的团队中,模式很常见,我们在.sql()中添加了一个隐含的DataFrame,它会自动注册然后取消注册SQL语句范围的临时表。

答案 2 :(得分:0)

我创建了一个 bebe 库,可以轻松计算列的百分位数。

让我们从创建您的 DataFrame 开始。

val df = spark
  .createDF(
    List(
      ("A1", 10.3),
      ("B1", 4.04),
      ("C1", 21.7),
      ("D1", 18.6)
    ),
    List(
      ("User ID", StringType, true),
      ("Open_Rate", DoubleType, true)
    )
  )
df.show()
+-------+---------+
|User ID|Open_Rate|
+-------+---------+
|     A1|     10.3|
|     B1|     4.04|
|     C1|     21.7|
|     D1|     18.6|
+-------+---------+

现在让我们计算第 10 个百分位数:

val resDF = df.agg(bebe_percentile(col("Open_Rate"), lit(0.1)).as("10_percentile"))
resDF.show()
+-----------------+
|    10_percentile|
+-----------------+
|5.918000000000001|
+-----------------+

它使用与 the SQL percentile method 相同的底层代码。