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

phi*_*ppe 9 javascript php statistics median percentile

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

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

在此处输入图片说明

phi*_*ppe 12

经过很长时间的搜索,找到了不同的版本并给出了不同的结果,我在BastianPöttner的Web博客上找到了这个漂亮的片段,但是对于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);
}
Run Code Online (Sandbox Code Playgroud)

根据作者的评论,我只是写了一个肯定会有用的JavaScript翻译,因为令人惊讶的是,几乎不可能在Web上找到等效的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));  
}
Run Code Online (Sandbox Code Playgroud)


ruf*_*fin 9

长话短说

\n

其他答案似乎对计算分位数的“R-7”版本有可靠的实现。下面是一些上下文和另一个使用相同 R-7 方法从D3借用的 JavaScript 实现,好处是该解决方案符合 es5 标准(不需要 JavaScript 转译),并且可能涵盖更多边缘情况。

\n
\n

D3 的现有解决方案(移植到 es5/“vanilla JS”)

\n

下面的“一些背景”部分应该会说服您使用现有的实现,而不是编写自己的实现。

\n

D3d3.array包是一个很好的候选者。它有一个基本上是BSD 许可的分位数函数

\n

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

\n

我很快就创建了一个从 es6 到 d3quantileSorted函数(该文件中定义的第二个函数)的普通 JavaScript 的相当直接的端口,该函数要求元素数组已经排序。这里是。我已经根据 d3 自己的结果对其进行了测试,足以感觉到它是一个有效的端口,但您的体验可能会有所不同(如果您发现差异,请在评论中告诉我!):

\n

再次记住,排序必须在调用此函数之前quantileSorted进行,就像在 D3 中一样。

\n
  //Credit D3: https://github.com/d3/d3-array/blob/master/LICENSE\n  function quantileSorted(values, p, fnValueFrom) {\n    var n = values.length;\n    if (!n) {\n      return;\n    }\n\n    fnValueFrom =\n      Object.prototype.toString.call(fnValueFrom) == "[object Function]"\n        ? fnValueFrom\n        : function (x) {\n            return x;\n          };\n\n    p = +p;\n\n    if (p <= 0 || n < 2) {\n      return +fnValueFrom(values[0], 0, values);\n    }\n\n    if (p >= 1) {\n      return +fnValueFrom(values[n - 1], n - 1, values);\n    }\n\n    var i = (n - 1) * p,\n      i0 = Math.floor(i),\n      value0 = +fnValueFrom(values[i0], i0, values),\n      value1 = +fnValueFrom(values[i0 + 1], i0 + 1, values);\n\n    return value0 + (value1 - value0) * (i - i0);\n  }\n
Run Code Online (Sandbox Code Playgroud)\n

请注意,这fnValueFrom是一种将复杂对象处理为值的方法。您可以在此处的 d3 使用示例列表中了解它的工作原理——搜索.quantile使用位置。

\n

快速版本是,如果它们values是乌龟,并且您在每种情况下都进行排序tortoise.age,那么您的fnValueFrom可能是x => x.age。更复杂的版本,包括在值计算期间可能需要访问索引(参数 2)和整个集合(参数 3)的版本,由读者自行决定。

\n

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

\n
\n

与现有答案的逻辑比较

\n

我有理由确信这在其他两个答案中会简化为相同的版本(请参阅下面的“R-7 方法”),但是如果您需要向产品经理或其他人证明为什么要使用它以下内容会有所帮助。

\n

快速比较:

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

所以这在逻辑上很接近/看起来完全相同。我认为我移植的 d3 版本涵盖了更多边缘/无效条件并包含集成fnValueFrom,这两者都可能有用。

\n
\n

R-7 方法与“常识”

\n

正如TL;DR中提到的,根据d3.array的readme,这里的答案都使用“R-7方法”。

\n
\n

这个特定的实现[来自 d3] 使用R-7 方法,这是 R 编程语言和 Excel 的默认方法。

\n
\n

由于 d3.array 代码与此处的其他答案相匹配,因此我们可以有把握地说它们都在使用 R-7。

\n
\n

背景

\n

在对一些数学和统计 StackExchange 网站(1 , 2这些方法通常与公认的九个分位数的结果不相符计算它们的方法。

\n

来自 stats.stackexchange 的第二个链接的答案 说的是常识性方法......

\n
\n

你的教科书很混乱。很少有人或软件以这种方式定义四分位数。(它往往会使第一个四分位数太小而第三个四分位数太大。)

\n

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

\n
\n

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

\n\n

d3 使用“方法 7”(R-7) 来确定分位数与常见的合理方法之间的差异在 SO 问题“d3.quantile 似乎错误地计算 q1”中得到了很好的证明,并且描述了原因这篇文章中有详细的说明,可以在 philippe 的 php 版本的原始源中找到。

\n

以下是谷歌翻译的一段内容(原文是德语):

\n
\n

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

\n
\n

总而言之,这告诉我,我可能不应该尝试根据我对四分位数代表什么的理解来编写代码,而应该借用其他人的解决方案。

\n


bub*_*boh 8

我从第一个答案中更新了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);
Run Code Online (Sandbox Code Playgroud)

  • 计算中位数和分位数不需要它 - 它只是作为奖励包含在内;) (2认同)