如何在JavaScript(或PHP)中获得数组的中位数和四分位数/百分位数?

时间:2018-02-10 10:44:30

标签: javascript php statistics median percentile

  

这个问题变成了Q& A,因为我很难找到答案,并认为它对其他人有用

我有一个JavaScript 值数组,需要在JavaScript中计算其Q2(第50个百分点又名 MEDIAN ),Q1( 25th百分位数 )和Q3(第75百分位数)值。

enter image description here

3 个答案:

答案 0 :(得分:8)

在搜索了很长时间后,找到了不同版本的不同版本,我在Bastian Pöttner's web blog找到了这个不错的代码段,但 PHP 。对于相同的价格,我们得到数据的平均值标准差(对于正态分布)...

PHP版

//from https://blog.poettner.de/2011/06/09/simple-statistics-with-php/

function Median($Array) {
  return Quartile_50($Array);
}

function Quartile_25($Array) {
  return Quartile($Array, 0.25);
}

function Quartile_50($Array) {
  return Quartile($Array, 0.5);
}

function Quartile_75($Array) {
  return Quartile($Array, 0.75);
}

function Quartile($Array, $Quartile) {
  sort($Array);
  $pos = (count($Array) - 1) * $Quartile;

  $base = floor($pos);
  $rest = $pos - $base;

  if( isset($Array[$base+1]) ) {
    return $Array[$base] + $rest * ($Array[$base+1] - $Array[$base]);
  } else {
    return $Array[$base];
  }
}

function Average($Array) {
  return array_sum($Array) / count($Array);
}

function StdDev($Array) {
  if( count($Array) < 2 ) {
    return;
  }

  $avg = Average($Array);

  $sum = 0;
  foreach($Array as $value) {
    $sum += pow($value - $avg, 2);
  }

  return sqrt((1 / (count($Array) - 1)) * $sum);
}

根据作者的评论,我只是编写了一个非常有用的 JavaScript 翻译,因为令人惊讶的是,几乎不可能在网络上找到等价的JavaScript,否则需要额外的库,如Math.js

JavaScript版

//adapted from https://blog.poettner.de/2011/06/09/simple-statistics-with-php/
function Median(data) {
  return Quartile_50(data);
}

function Quartile_25(data) {
  return Quartile(data, 0.25);
}

function Quartile_50(data) {
  return Quartile(data, 0.5);
}

function Quartile_75(data) {
  return Quartile(data, 0.75);
}

function Quartile(data, q) {
  data=Array_Sort_Numbers(data);
  var pos = ((data.length) - 1) * q;
  var base = Math.floor(pos);
  var rest = pos - base;
  if( (data[base+1]!==undefined) ) {
    return data[base] + rest * (data[base+1] - data[base]);
  } else {
    return data[base];
  }
}

function Array_Sort_Numbers(inputarray){
  return inputarray.sort(function(a, b) {
    return a - b;
  });
}

function Array_Sum(t){
   return t.reduce(function(a, b) { return a + b; }, 0); 
}

function Array_Average(data) {
  return Array_Sum(data) / data.length;
}

function Array_Stdev(tab){
   var i,j,total = 0, mean = 0, diffSqredArr = [];
   for(i=0;i<tab.length;i+=1){
       total+=tab[i];
   }
   mean = total/tab.length;
   for(j=0;j<tab.length;j+=1){
       diffSqredArr.push(Math.pow((tab[j]-mean),2));
   }
   return (Math.sqrt(diffSqredArr.reduce(function(firstEl, nextEl){
            return firstEl + nextEl;
          })/tab.length));  
}

答案 1 :(得分:3)

我从第一个答案中更新了JavaScript翻译,以使用箭头功能和更简洁的符号。

除了std以外,其他功能基本相同,arr.length - 1现在可以计算样本标准差(除以arr.length而不是// sort array ascending const asc = arr => arr.sort((a, b) => a - b); const sum = arr => arr.reduce((a, b) => a + b, 0); const mean = arr => sum(arr) / arr.length; // sample standard deviation const std = (arr) => { const mu = mean(arr); const diffArr = arr.map(a => (a - mu) ** 2); return Math.sqrt(sum(diffArr) / (arr.length - 1)); }; const quantile = (arr, q) => { const sorted = asc(arr); const pos = ((sorted.length) - 1) * q; const base = Math.floor(pos); const rest = pos - base; if ((sorted[base + 1] !== undefined)) { return sorted[base] + rest * (sorted[base + 1] - sorted[base]); } else { return sorted[base]; } }; const q25 = arr => quantile(arr, .25); const q50 = arr => quantile(arr, .50); const q75 = arr => quantile(arr, .75); const median = arr => q50(arr);
sphinx-apidoc

答案 2 :(得分:0)

TL; DR

其他答案似乎是计算分位数的“ R-7”版本的可靠实现。下面是一些上下文以及使用相同的R-7方法从D3借用的另一种JavaScript实现,其好处是可以涵盖更多边缘情况。


背景

在仔细研究了一些数学和统计信息StackExchange网站(12)之后,我发现计算每个分位数有“常识”方法,但是通常这些方法并没有与九种公认的计算方法的结果相吻合。

second link from stats.stackexchange的答案说的是……的常识性方法。

您的教科书很困惑。很少有人或软件以这种方式定义四分位数。 (这会使第一个四分位数太小,而第三个四分位数又太大。)

quantile中的R函数实现了九种计算分位数的方法!

我认为最后一点很有趣,这就是我从这九种方法中挖掘出来的...

这告诉我,我可能不应该基于对四分位数代表的理解来尝试编写某些东西,而应该借鉴其他人的解决方案。


D3中的现有解决方案

一个例子来自D3。其d3.array程序包具有一个quantile function,实际上是BSD licensed

https://github.com/d3/d3-array/blob/master/src/quantile.js

我很快就创建了d3版本的一个相当直观的版本,该版本需要将已经排序的元素数组放入原始JavaScript中。这里是。我已经对d3的结果本身进行了一些测试,足以感觉到它是一个有效的端口,但是您的体验可能有所不同(不过,请在注释中告诉我!):

  //Credit D3: https://github.com/d3/d3-array/blob/master/LICENSE
  function quantileSorted(values, p, fnValueFrom) {
    var n = values.length;
    if (!n) {
      return;
    }

    fnValueFrom =
      Object.prototype.toString.call(fnValueFrom) == "[object Function]"
        ? fnValueFrom
        : function (x) {
            return x;
          };

    p = +p;

    if (p <= 0 || n < 2) {
      return +fnValueFrom(values[0], 0, values);
    }

    if (p >= 1) {
      return +fnValueFrom(values[n - 1], n - 1, values);
    }

    var i = (n - 1) * p,
      i0 = Math.floor(i),
      value0 = +fnValueFrom(values[i0], i0, values),
      value1 = +fnValueFrom(values[i0 + 1], i0 + 1, values);

    return value0 + (value1 - value0) * (i - i0);
  }

请注意,fnValueFrom是一种将复杂对象处理为值的方法。您可以in a list of d3 usage examples here看到其工作原理-向下搜索.quantile的使用位置。

快速版本是values是乌龟,而您在每种情况下都在对tortoise.age进行排序时,您的fnValueFrom可能是x => x.age。更为复杂的版本(包括可能需要在值计算期间访问索引(参数2)和整个集合(参数3)的版本)留给读者。

我在这里添加了一个快速检查,以便如果没有为fnValueFrom提供任何内容,或者给定的不是函数,则逻辑假定values中的元素本身就是实际的排序值。


与现有答案的逻辑比较

我可以肯定地说,这在其他两个答案中会减少到相同的版本(请参见下文),但是如果您需要向产品经理说明为什么要使用它,或者以上所述可能会有所帮助。

快速比较:

function Quartile(data, q) {
  data=Array_Sort_Numbers(data);        // we're assuming it's already sorted, above, vs. the function use here. same difference.
  var pos = ((data.length) - 1) * q;    // i = (n - 1) * p
  var base = Math.floor(pos);           // i0 = Math.floor(i)
  var rest = pos - base;                // (i - i0);
  if( (data[base+1]!==undefined) ) {
    //      value0    + (i - i0)   * (value1 which is values[i0+1] - value0 which is values[i0])
    return data[base] + rest       * (data[base+1]                 - data[base]);
  } else {
    // I think this is covered by if (p <= 0 || n < 2)
    return data[base];
  }
}

因此逻辑上接近/看起来完全一样。我认为我移植的d3版本涵盖了更多边缘/无效条件,这可能很有用。


顺便说一下,根据d3-array's readme,此处的答案都使用“ R-7方法”:

此特定实现使用R-7 method,这是R编程语言和Excel的默认设置。

为进一步了解,d3很好地证明了{3} d3使用R-7来确定分位数与常见的感官方法之间的区别,并在一篇与philippe原始来源相关的文章中进行了描述。 php版本in this question(德语)。这是Google翻译的一些内容:

在我们的示例中,该值为(n +1)/ 4位= 5.25,即介于第5个值(= 5)和第6个值(= 7)之间。分数(0.25)表示除了5的值外,还添加了5到6之间距离的1/4。因此,Q1为5 + 0.25 * 2 = 5.5。