6

我需要找到多个双数据类型列的中位数。请求建议以找到正确的方法。

下面是我的一列示例数据集。我期望样本的中值返回为 1。

  scala> sqlContext.sql("select num from test").show();
+---+
|num|
+---+
|0.0|
|0.0|
|1.0|
|1.0|
|1.0|
|1.0|
+---+

我尝试了以下选项

1) Hive UDAF 百分位数,它仅适用于 BigInt。

2) Hive UDAT percentile_approx,但它没有按预期工作(返回 0.25 vs 1)。

sqlContext.sql("select percentile_approx(num,0.5) from test").show();

+----+
| _c0|
+----+
|0.25|
+----+

3)Spark窗口函数percent_rank-找到中位数我看到的方法是寻找所有高于0.5的percent_rank并选择最大percent_rank对应的num值。但它并非在所有情况下都有效,尤其是当我有记录计数时,在这种情况下,中位数是排序分布中中间值的平均值。

同样在 percent_rank 中,因为我必须找到多列的中位数,所以我必须在不同的数据帧中计算它,这对我来说是一个不太复杂的方法。如果我的理解不正确,请纠正我。

+---+-------------+
|num|percent_rank |
+---+-------------+
|0.0|0.0|
|0.0|0.0|
|1.0|0.4|
|1.0|0.4|
|1.0|0.4|
|1.0|0.4|
+---+---+
4

1 回答 1

9

出于好奇,您使用的是哪个版本的 Apache Spark?Apache Spark 2.0+ 中有一些修复,其中包括对approxQuantile.

如果我要运行下面的 pySpark 代码片段:

rdd = sc.parallelize([[1, 0.0], [1, 0.0], [1, 1.0], [1, 1.0], [1, 1.0], [1, 1.0]])
df = rdd.toDF(['id', 'num'])
df.createOrReplaceTempView("df")

使用 as进行median计算approxQuantile

df.approxQuantile("num", [0.5], 0.25)

或者

spark.sql("select percentile_approx(num, 0.5) from df").show()

结果是:

  • 火花 2.0.0:0.25
  • 火花 2.0.1 : 1.0
  • 火花 2.1.0 : 1.0

请注意,因为这些是近似数字(通过approxQuantile),但通常这应该很好用。如果您需要确切的中位数,一种方法是使用numpy.median. 下面的代码片段是根据 gench 对How to find the median in Apache Spark with Python Dataframe API?df的 SO 响应为此示例更新的。:

from pyspark.sql.types import *
import pyspark.sql.functions as F
import numpy as np

def find_median(values):
    try:
        median = np.median(values) #get the median of values in a list in each row
        return round(float(median),2)
    except Exception:
        return None #if there is anything wrong with the given values

median_finder = F.udf(find_median,FloatType())

df2 = df.groupBy("id").agg(F.collect_list("num").alias("nums"))
df2 = df2.withColumn("median", median_finder("nums"))

# print out
df2.show()

输出:

+---+--------------------+------+
| id|                nums|median|
+---+--------------------+------+
|  1|[0.0, 0.0, 1.0, 1...|   1.0|
+---+--------------------+------+

更新:使用 RDD 的 Spark 1.6 Scala 版本

如果您使用的是 Spark 1.6,您可以median通过 Eugene Zhuleev 的回复How can I calculate the exact median with Apache Spark计算使用 Scala 代码。以下是适用于我们示例的修改后的代码。

import org.apache.spark.SparkContext._

  val rdd: RDD[Double] = sc.parallelize(Seq((0.0), (0.0), (1.0), (1.0), (1.0), (1.0)))

  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

输出:

// output
import org.apache.spark.SparkContext._
rdd: org.apache.spark.rdd.RDD[Double] = ParallelCollectionRDD[227] at parallelize at <console>:34
sorted: org.apache.spark.rdd.RDD[(Long, Double)] = MapPartitionsRDD[234] at map at <console>:36
count: Long = 6
median: Double = 1.0

请注意,这是使用计算精确中位数RDDs- 即您需要将 DataFrame 列转换为 RDD 才能执行此计算。

于 2016-12-31T05:30:51.350 回答