计算中位数 - javascript
Posted
技术标签:
【中文标题】计算中位数 - javascript【英文标题】:Calculating median - javascript 【发布时间】:2017-12-31 17:49:48 【问题描述】:我一直在尝试计算 中位数,但我仍然遇到了一些数学问题,因为我无法获得正确的中位数并且无法弄清楚原因。这是代码;
class StatsCollector
constructor()
this.inputNumber = 0;
this.average = 0;
this.timeout = 19000;
this.frequencies = new Map();
for (let i of Array(this.timeout).keys())
this.frequencies.set(i, 0);
pushValue(responseTimeMs)
let req = responseTimeMs;
if (req > this.timeout)
req = this.timeout;
this.average = (this.average * this.inputNumber + req) / (this.inputNumber + 1);
console.log(responseTimeMs / 1000)
let groupIndex = Math.floor(responseTimeMs / 1000);
this.frequencies.set(groupIndex, this.frequencies.get(groupIndex) + 1);
this.inputNumber += 1;
getMedian()
let medianElement = 0;
if (this.inputNumber <= 0)
return 0;
if (this.inputNumber == 1)
return this.average
if (this.inputNumber == 2)
return this.average
if (this.inputNumber > 2)
medianElement = this.inputNumber / 2;
let minCumulativeFreq = 0;
let maxCumulativeFreq = 0;
let cumulativeFreq = 0;
let freqGroup = 0;
for (let i of Array(20).keys())
if (medianElement <= cumulativeFreq + this.frequencies.get(i))
minCumulativeFreq = cumulativeFreq;
maxCumulativeFreq = cumulativeFreq + this.frequencies.get(i);
freqGroup = i;
break;
cumulativeFreq += this.frequencies.get(i);
return (((medianElement - minCumulativeFreq) / (maxCumulativeFreq - minCumulativeFreq)) + (freqGroup)) * 1000;
getAverage()
return this.average;
这是我输入值时的结果快照
342,654,987,1093,2234,6243,7087,20123
正确的结果应该是;
中位数:1663.5
【问题讨论】:
也许看here 要计算中位数,您必须对值进行排序并选择中间的值。 这不是中位数。中位数应该在集合中。 如果有奇数个值,则中位数是排序列表的中间数,如果有偶数个值,则中位数是中间两个值的中点或平均值。 find median values from array in javascript (8 values or 9 values)的可能重复 【参考方案1】:将您的中位数方法更改为:
function median(values)
if(values.length ===0) throw new Error("No inputs");
values.sort(function(a,b)
return a-b;
);
var half = Math.floor(values.length / 2);
if (values.length % 2)
return values[half];
return (values[half - 1] + values[half]) / 2.0;
fiddle
【讨论】:
values.length ===0
时会发生什么?我建议在第一行进行安全检查if(values.length ===0) return 0
你不需要else
注意这个方法修改了给定的数组。
我认为空数组的中位数不是0,而是未定义。
要保留给定的数组,请在函数开始处使用 values = [...values];
之类的东西。【参考方案2】:
这是另一个解决方案:
function median(numbers)
const sorted = numbers.slice().sort((a, b) => a - b);
const middle = Math.floor(sorted.length / 2);
if (sorted.length % 2 === 0)
return (sorted[middle - 1] + sorted[middle]) / 2;
return sorted[middle];
console.log(median([4, 5, 7, 1, 33]));
【讨论】:
好,这使原始数组保持不变。 我知道我迟到了 2 年,但这个答案比顶部的其他答案要好。【参考方案3】:上面的解决方案 - 排序然后查找中间 - 很好,但在大型数据集上速度很慢。首先对数据进行排序的复杂度为 n x log(n)。
有一种更快的中值算法,它包括根据轴将数组分成两部分,然后在更大的集合中寻找中值。这是一些javascript代码,但here is a more detailed explanation
// Trying some array
alert(quickselect_median([7,3,5])); // 2300,5,4,0,123,2,76,768,28]));
function quickselect_median(arr)
const L = arr.length, halfL = L/2;
if (L % 2 == 1)
return quickselect(arr, halfL);
else
return 0.5 * (quickselect(arr, halfL - 1) + quickselect(arr, halfL));
function quickselect(arr, k)
// Select the kth element in arr
// arr: List of numerics
// k: Index
// return: The kth element (in numerical order) of arr
if (arr.length == 1)
return arr[0];
else
const pivot = arr[0];
const lows = arr.filter((e)=>(e<pivot));
const highs = arr.filter((e)=>(e>pivot));
const pivots = arr.filter((e)=>(e==pivot));
if (k < lows.length) // the pivot is too high
return quickselect(lows, k);
else if (k < lows.length + pivots.length)// We got lucky and guessed the median
return pivot;
else // the pivot is too low
return quickselect(highs, k - lows.length - pivots.length);
精明的读者会注意到一些事情:
-
我只是将 Russel Cohen 的 Python 解决方案音译成 JS,
所以对他的所有荣誉。
有几个小的优化值得
做,但有值得做的并行化,代码原样
更容易在更快的单线程或更快的
多线程,版本。
这是平均线性时间
算法,有一个更有效的确定性线性时间版本,有关详细信息,包括性能数据,请参阅Russel's
post。
2019 年 9 月 19 日补充:
有一条评论询问这是否值得在 javascript 中执行。我在JSPerf 中运行了代码,结果很有趣。
如果数组有奇数个元素(找一个数字),排序比这个“快速中位数”命题慢 20%。
1234563可以编写不这样做的快速中位数版本。测试使用了相当小的数组(jsperf 测试中有 29 个元素)。随着阵列变大,效果似乎更加明显。一个更普遍的观点是:它表明这些优化在 Javascript 中是值得做的。大量的计算是在 JS 中完成的,包括大量数据(想想仪表板、电子表格、数据可视化),以及资源有限的系统(想想移动和嵌入式计算)。
【讨论】:
我试图了解它是否对 javascript 有意义。这个快速选择算法似乎试图手动实现快速排序算法。在 javascript 中,排序算法的类型也取决于数组的大小和浏览器。当您使用 Array.sort() 时,会在后台选择优化的排序算法。我可能是误会了,当然,你怎么看? 我的答案可能是我作为一个计算教育者的产物,而不是从业者 - 我教的东西,所以这是一个很棒的课程在这。上面的算法是否是一个好主意取决于你为什么这样做,一如既往。多大,阵列?很多吗?您是否需要在某个时候对数据进行排序,或者需要其他统计数据,例如四分位数等?您是否使用其他更改工具选择的库?时间表现对你来说是一个重要因素吗?资源对您来说是一个重要因素吗? @ThiagoC.SVentura 您的评论提示您测试差异是否在 JSPerf 中可见。我将结果添加到答案中。【参考方案4】:var arr =
max: function(array)
return Math.max.apply(null, array);
,
min: function(array)
return Math.min.apply(null, array);
,
range: function(array)
return arr.max(array) - arr.min(array);
,
midrange: function(array)
return arr.range(array) / 2;
,
sum: function(array)
var num = 0;
for (var i = 0, l = array.length; i < l; i++) num += array[i];
return num;
,
mean: function(array)
return arr.sum(array) / array.length;
,
median: function(array)
array.sort(function(a, b)
return a - b;
);
var mid = array.length / 2;
return mid % 1 ? array[mid - 0.5] : (array[mid - 1] + array[mid]) / 2;
,
modes: function(array)
if (!array.length) return [];
var modeMap = ,
maxCount = 1,
modes = [array[0]];
array.forEach(function(val)
if (!modeMap[val]) modeMap[val] = 1;
else modeMap[val]++;
if (modeMap[val] > maxCount)
modes = [val];
maxCount = modeMap[val];
else if (modeMap[val] === maxCount)
modes.push(val);
maxCount = modeMap[val];
);
return modes;
,
variance: function(array)
var mean = arr.mean(array);
return arr.mean(array.map(function(num)
return Math.pow(num - mean, 2);
));
,
standardDeviation: function(array)
return Math.sqrt(arr.variance(array));
,
meanAbsoluteDeviation: function(array)
var mean = arr.mean(array);
return arr.mean(array.map(function(num)
return Math.abs(num - mean);
));
,
zScores: function(array)
var mean = arr.mean(array);
var standardDeviation = arr.standardDeviation(array);
return array.map(function(num)
return (num - mean) / standardDeviation;
);
;
【讨论】:
感谢您复制粘贴整个库,但这是从哪里来的? 这非常有用,因为它有很多有用的功能。 被低估的回答这个 同样,这个中值函数会在对输入数组进行排序时对其进行修改。只是需要注意的事情。【参考方案5】:又短又甜。
Array.prototype.median = function ()
return this.slice().sort((a, b) => a - b)[Math.floor(this.length / 2)];
;
用法
[4, 5, 7, 1, 33].median()
也适用于字符串
["a","a","b","b","c","d","e"].median()
【讨论】:
[0, 5].median()
结果为 5
不要改变原型。【参考方案6】:
TypeScript 答案 2020:
// Calculate Median
const calculateMedian = (array: Array<number>) =>
// Check If Data Exists
if (array.length >= 1)
// Sort Array
array = array.sort((a: number, b: number) =>
return a - b;
);
// Array Length: Even
if (array.length % 2 === 0)
// Average Of Two Middle Numbers
return (array[(array.length / 2) - 1] + array[array.length / 2]) / 2;
// Array Length: Odd
else
// Middle Number
return array[(array.length - 1) / 2];
else
// Error
console.error('Error: Empty Array (calculateMedian)');
;
【讨论】:
【参考方案7】:为了在时间复杂度方面获得更好的性能,请使用 MaxHeap - MinHeap 来查找数组流的中位数。
【讨论】:
【参考方案8】:更简单、更高效
const median = dataSet =>
if (dataSet.length === 1) return dataSet[0]
const sorted = ([ ...dataSet ]).sort()
const ceil = Math.ceil(sorted.length / 2)
const floor = Math.floor(sorted.length / 2)
if (ceil === floor) return sorted[floor]
return ((sorted[ceil] + sorted[floor]) / 2)
【讨论】:
【参考方案9】:简单的解决方案:
function calcMedian(array)
const
length
= array;
if (length < 1)
return 0;
//sort array asc
array.sort((a, b) => a - b);
if (length % 2)
//length of array is odd
return array[(length + 1) / 2 - 1];
else
//length of array is even
return 0.5 * [(array[length / 2 - 1] + array[length / 2])];
console.log(2, calcMedian([1, 2, 2, 5, 6]));
console.log(3.5, calcMedian([1, 2, 2, 5, 6, 7]));
console.log(9, calcMedian([13, 9, 8, 15, 7]));
console.log(3.5, calcMedian([1, 4, 6, 3]));
console.log(5, calcMedian([5, 1, 11, 2, 8]));
【讨论】:
【参考方案10】:更简单、更高效、更易阅读
-
克隆数据以避免更改原始数据。
对值列表进行排序。
得到中间点。
从列表中获取中位数。
返回中位数。
function getMedian(data)
const values = [...data];
const v = values.sort( (a, b) => a - b);
const mid = Math.floor( v.length / 2);
const median = (v.length % 2 !== 0) ? v[mid] : (v[mid - 1] + v[mid]) / 2;
return median;
【讨论】:
虽然您的回答可能会解决问题,但 including an explanation 关于如何以及为什么解决问题将真正有助于提高您的帖子质量,并可能导致更多的赞成票。请记住,您正在为将来的读者回答问题,而不仅仅是现在提问的人。您可以编辑您的答案以添加解释并指出适用的限制和假设。 - From Review 你写的是“更简单、更高效”,但最好知道你的比较,因为很多人已经用类似的代码回答了。本质上,sort
操作决定了执行时间,大多数答案都使用了它。
不起作用 console.log(getMedian([-0.51182868190794402, 0.33955843791573237, 1.073205764212215]));【参考方案11】:
const medianArr = (x) =>
let sortedx = x.sort((a,b)=> a-b);
let halfIndex = Math.floor(sortedx.length/2);
return (sortedx.length%2) ? (sortedx[Math.floor(sortedx.length/2)]) : ((sortedx[halfIndex-1]+sortedx[halfIndex])/2)
console.log(medianArr([1,2,3,4,5]));
console.log(medianArr([1,2,3,4,5,6]));
【讨论】:
以上是关于计算中位数 - javascript的主要内容,如果未能解决你的问题,请参考以下文章