如何使用Apache Spark计算确切的中位数?

16

这个 页面 包含了一些统计函数(如平均值、标准差、方差等),但没有中位数。我该怎么计算精确的中位数?


percentile_approx函数在具有偶数个条目的组中无法正常工作。为了使其在这种情况下正常工作,一个可能的解决方法是取第50百分位数和下一个值的平均值。在pyspark中,可以使用以下代码实现:((F.percentile_approx('val', 0.5) + F.percentile_approx('val', 0.500000000001)) * 0.5).alias('med_val2') - prashanth
2个回答

19

您需要对RDD进行排序,并取中间元素或两个元素的平均值。以下是带有RDD[Int]的示例:

  import org.apache.spark.SparkContext._

  val rdd: RDD[Int] = ???

  val sorted = rdd.sortBy(identity).zipWithIndex().map {
    case (v, idx) => (idx, v)
  }

  val count = sorted.count()

  val median: Double = if (count % 2 == 0) {
    val l = count / 2 - 1
    val r = l + 1
    (sorted.lookup(l).head + sorted.lookup(r).head).toDouble / 2
  } else sorted.lookup(count / 2).head.toDouble

2
我认为有更快的算法可以找到中位数,而不需要完全排序。(http://en.wikipedia.org/wiki/Selection_algorithm) - Eran Medan
很遗憾,它们不适用于分布式RDD。 - Eugene Zhulenev
1
DataFrame API可以替代RDD API使用吗? - Geoffrey Anderson
是的,请参见https://dev59.com/yF0Z5IYBdhLWcg3wdQEy - FrankGT
最好将排序后的RDD持久化,这样在进行查找时就不会重新计算DAG。 - kjsr7
显示剩余2条评论

7

使用Spark 2.0+和DataFrame API,您可以使用approxQuantile方法:


(注:该方法可用于计算DataFrame列的近似分位数。)
def approxQuantile(col: String, probabilities: Array[Double], relativeError: Double)

自Spark 2.2版本以来,它也可以同时处理多列。通过将 probabilites 设置为 Array(0.5) 并将 relativeError 设置为 0,它将计算精确的中位数。从文档中可以看到:

实现相对目标精度(大于等于0)。如果设置为零,则计算精确分位数可能非常昂贵。

尽管如此,当将relativeError设置为0时,似乎存在一些精度问题,请查看这里的问题here。在某些情况下,接近0的低误差会表现得更好(取决于Spark版本)。
以下是一个简单的工作示例,它计算1至99(包括边界)之间数字的中位数并使用较低的 relativeError:
val df = (1 to 99).toDF("num")
val median = df.stat.approxQuantile("num", Array(0.5), 0.001)(0)
println(median)

返回结果:

中位数为50.0。


莫妮卡,你知道为什么我运行你的代码时会出现NameError: name 'Array' is not defined吗?这似乎不是我需要导入的包。 - bernando_vialli
@mathlover:你在使用Scala吗?也许你在某个地方用变量覆盖了名称? - Shaido
我正在使用 PySpark。 - bernando_vialli
@mathlover:那么你不能直接使用Scala版本也就不足为奇了。你需要稍作调整。 - Shaido

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接