百分位Pandasvs.scala哪里有虫子?

2024-10-02 16:33:25 发布

您现在位置:Python中文网/ 问答频道 /正文

要一个数字列表

val numbers = Seq(0.0817381355303346, 0.08907955219917718, 0.10581384008994665, 0.10970915785902469, 0.1530743353025532, 0.16728932033107657, 0.181932212814931, 0.23200826752868853, 0.2339654613723784, 0.2581657775305527, 0.3481071101229365, 0.5010850992326521, 0.6153244818101578, 0.6233250409474894, 0.6797744231690304, 0.6923891392381571, 0.7440316016776881, 0.7593186414698002, 0.8028091068764153, 0.8780699052482807, 0.8966649331194205)

python/pandas计算以下百分位数:

25%     0.167289
50%     0.348107
75%     0.692389

但是,scala返回:

calcPercentiles(Seq(.25, .5, .75), sortedNumber.toArray)

25% 0.1601818278168149
50% 0.3481071101229365
75% 0.7182103704579226

这些数字几乎是一致的,但不同。我如何才能消除这种差异(并且很可能修复scala代码中的错误)

val sortedNumber = numbers.sorted

import scala.collection.mutable
case class PercentileResult(percentile:Double, value:Double)

// https://github.com/scalanlp/breeze/blob/master/math/src/main/scala/breeze/stats/DescriptiveStats.scala#L537
def calculatePercentile(arr: Array[Double], p: Double)={
    // +1 so that the .5 == mean for even number of elements.
    val f = (arr.length + 1) * p
    val i = f.toInt
    if (i == 0) arr.head
    else if (i >= arr.length) arr.last
    else {
      arr(i - 1) + (f - i) * (arr(i) - arr(i - 1))
    }
  }

 def calcPercentiles(percentiles:Seq[Double], arr: Array[Double]):Array[PercentileResult] = {
    val results = new mutable.ListBuffer[PercentileResult]
    percentiles.foreach(p => {
      val r = PercentileResult(percentile = p, value = calculatePercentile(arr, p))
      results.append(r)
    })
    results.toArray
  }

python:

 import pandas as pd

df = pd.DataFrame({'foo':[0.0817381355303346, 0.08907955219917718, 0.10581384008994665, 0.10970915785902469, 0.1530743353025532, 0.16728932033107657, 0.181932212814931, 0.23200826752868853, 0.2339654613723784, 0.2581657775305527, 0.3481071101229365, 0.5010850992326521, 0.6153244818101578, 0.6233250409474894, 0.6797744231690304, 0.6923891392381571, 0.7440316016776881, 0.7593186414698002, 0.8028091068764153, 0.8780699052482807, 0.8966649331194205]})
display(df.head())
df.describe()

Tags: pandasdf数字valarrayresultsseqdouble
1条回答
网友
1楼 · 发布于 2024-10-02 16:33:25

经过一点尝试和错误后,我编写了这段代码,返回与Panda相同的结果(默认情况下使用线性插值):

def calculatePercentile(numbers: Seq[Double], p: Double): Double = {
  // interpolate only - no special handling of the case when rank is integer
  val rank = (numbers.size - 1) * p
  val i = numbers(math.floor(rank).toInt)
  val j = numbers(math.ceil(rank).toInt)
  val fraction = rank - math.floor(rank)
  i + (j - i) * fraction
}

由此我可以说错误就在这里:

(arr.length + 1) * p

0的百分位数应为0,100%的百分位数应为最大指数

所以对于numbers.size == 21),这将是指数020。然而,对于100%,您将得到22的索引值-比数组的大小大!如果没有这些保护条款:

else if (i >= arr.length) arr.last

你会犯错误,你可能会怀疑出了什么问题。或许代码的作者:

https://github.com/scalanlp/breeze/blob/master/math/src/main/scala/breeze/stats/DescriptiveStats.scala#L537

使用了不同的百分比定义。。。(?)或者他们可能只是有一个bug。我不知道

顺便说一句:这是:

def calcPercentiles(percentiles:Seq[Double], arr: Array[Double]): Array[PercentileResult]

这样写会容易得多:

def calcPercentiles(percentiles:Seq[Double], numbers: Seq[Double]): Seq[PercentileResult] =
  percentiles.map { p =>
    PercentileResult(p, calculatePercentile(numbers, p))
  }

相关问题 更多 >