diff --git a/draftlogs/6223_add.md b/draftlogs/6223_add.md index c4853f7f97c..d90cd7d103a 100644 --- a/draftlogs/6223_add.md +++ b/draftlogs/6223_add.md @@ -1,2 +1,2 @@ - - Add geomean functionality and 'geomean ascending' + 'geomean descending' to `category_order` on cartesian axes [[#6223](https://github.com/plotly/plotly.js/pull/6223)] + - Add geometric mean functionality and 'geometric mean ascending' + 'geometric mean descending' to `category_order` on cartesian axes [[#6223](https://github.com/plotly/plotly.js/pull/6223)] with thanks to @acxz and @prabhathc for the contribution! diff --git a/src/lib/index.js b/src/lib/index.js index 413859b70fd..af997e0236f 100644 --- a/src/lib/index.js +++ b/src/lib/index.js @@ -114,7 +114,7 @@ var statsModule = require('./stats'); lib.aggNums = statsModule.aggNums; lib.len = statsModule.len; lib.mean = statsModule.mean; -lib.geoMean = statsModule.geoMean; +lib.geometricMean = statsModule.geometricMean; lib.median = statsModule.median; lib.midRange = statsModule.midRange; lib.variance = statsModule.variance; diff --git a/src/lib/stats.js b/src/lib/stats.js index 463f3960855..0ffbf4eb0e9 100644 --- a/src/lib/stats.js +++ b/src/lib/stats.js @@ -47,10 +47,10 @@ exports.mean = function(data, len) { return exports.aggNums(function(a, b) { return a + b; }, 0, data) / len; }; -exports.geoMean = function(data, len) { +exports.geometricMean = function(data, len) { if(!len) len = exports.len(data); - return exports.aggNums(function(a, b) { return a * b; }, 0, data) ** (1 / len); -} + return Math.pow(exports.aggNums(function(a, b) { return a * b; }, 1, data), 1 / len); +}; exports.midRange = function(numArr) { if(numArr === undefined || numArr.length === 0) return undefined; diff --git a/src/plots/cartesian/layout_attributes.js b/src/plots/cartesian/layout_attributes.js index cfc48b79028..1c1be7bd347 100644 --- a/src/plots/cartesian/layout_attributes.js +++ b/src/plots/cartesian/layout_attributes.js @@ -1012,7 +1012,7 @@ module.exports = { 'max ascending', 'max descending', 'sum ascending', 'sum descending', 'mean ascending', 'mean descending', - 'geomean ascending', 'geomean descending', + 'geometric mean ascending', 'geometric mean descending', 'median ascending', 'median descending' ], dflt: 'trace', @@ -1027,7 +1027,7 @@ module.exports = { 'the *trace* mode. The unspecified categories will follow the categories in `categoryarray`.', 'Set `categoryorder` to *total ascending* or *total descending* if order should be determined by the', 'numerical order of the values.', - 'Similarly, the order can be determined by the min, max, sum, mean, geomean or median of all the values.' + 'Similarly, the order can be determined by the min, max, sum, mean, geometric mean or median of all the values.' ].join(' ') }, categoryarray: { diff --git a/src/plots/plots.js b/src/plots/plots.js index 84b3e3ab7f4..ace7589c2a0 100644 --- a/src/plots/plots.js +++ b/src/plots/plots.js @@ -3092,7 +3092,7 @@ plots.doCalcdata = function(gd, traces) { Registry.getComponentMethod('errorbars', 'calc')(gd); }; -var sortAxisCategoriesByValueRegex = /(total|sum|min|max|mean|median) (ascending|descending)/; +var sortAxisCategoriesByValueRegex = /(total|sum|min|max|mean|geometric mean|median) (ascending|descending)/; function sortAxisCategoriesByValue(axList, gd) { var affectedTraces = []; @@ -3127,7 +3127,7 @@ function sortAxisCategoriesByValue(axList, gd) { 'sum': function(values) {return Lib.aggNums(function(a, b) { return a + b;}, null, values);}, 'total': function(values) {return Lib.aggNums(function(a, b) { return a + b;}, null, values);}, 'mean': function(values) {return Lib.mean(values);}, - 'geomean': function(values) {return Lib.geoMean(values);}, + 'geometric mean': function(values) {return Lib.geometricMean(values);}, 'median': function(values) {return Lib.median(values);} }; diff --git a/test/jasmine/tests/calcdata_test.js b/test/jasmine/tests/calcdata_test.js index 2bb185fca05..517a583f576 100644 --- a/test/jasmine/tests/calcdata_test.js +++ b/test/jasmine/tests/calcdata_test.js @@ -1154,19 +1154,19 @@ describe('calculated data and points', function() { checkAggregatedValue(baseMock, expectedAgg, false, done); }); - it('takes the geomean of all values per category across traces of type ' + trace.type, function(done) { + it('takes the geometric mean of all values per category across traces of type ' + trace.type, function(done) { var type = trace.type; var data = [7, 2, 3]; var data2 = [5, 4, 2]; var baseMock = { data: [makeData(type, axName, cat, data), makeData(type, axName, cat, data2)], layout: {}}; - baseMock.layout[axName] = { type: 'category', categoryorder: 'geomean ascending'}; + baseMock.layout[axName] = { type: 'category', categoryorder: 'geometric mean ascending'}; var expectedAgg = [['a', Math.sqrt(data[0] * data2[0])], ['b', Math.sqrt(data[1] * data2[1])], ['c', Math.sqrt(data[2] * data2[2])]]; // TODO: how to actually calc these? what do these even mean? if(type === 'histogram') expectedAgg = [['a', 2], ['b', 1], ['c', 1]]; - if(type === 'histogram2d') expectedAgg = [['a', 2 / 3], ['b', 1 / 3], ['c', 1 / 3]]; - if(type === 'contour' || type === 'heatmap') expectedAgg = [['a', expectedAgg[0][1] / 3], ['b', expectedAgg[1][1] / 3], ['c', expectedAgg[2][1] / 3]]; - if(type === 'histogram2dcontour') expectedAgg = [['a', 2 / 4], ['b', 1 / 4], ['c', 1 / 4]]; + if(type === 'histogram2d') expectedAgg = [['a', 0], ['b', 0], ['c', 0]]; + if(type === 'contour' || type === 'heatmap') expectedAgg = [['a', 0], ['b', 0], ['c', 0]]; + if(type === 'histogram2dcontour') expectedAgg = [['a', 0], ['b', 0], ['c', 0]]; checkAggregatedValue(baseMock, expectedAgg, false, done); }); diff --git a/test/jasmine/tests/lib_test.js b/test/jasmine/tests/lib_test.js index bae5f206192..cc09b4c49c9 100644 --- a/test/jasmine/tests/lib_test.js +++ b/test/jasmine/tests/lib_test.js @@ -126,20 +126,20 @@ describe('Test lib.js:', function() { }); }); - describe('geomean() should', function() { + describe('geometricMean() should', function() { it('toss out non-numerics (strings)', function() { var input = [1, 2, 'apple', 'orange']; - var res = Lib.geomean(input); + var res = Lib.geometricMean(input); expect(res).toBeCloseTo(1.414, 3); }); it('toss out non-numerics (NaN)', function() { var input = [1, 2, NaN]; - var res = Lib.geomean(input); + var res = Lib.geometricMean(input); expect(res).toBeCloseTo(1.414, 3); }); it('evaluate numbers which are passed around as text strings:', function() { var input = ['1', '2']; - var res = Lib.geomean(input); + var res = Lib.geometricMean(input); expect(res).toBeCloseTo(1.414, 3); }); });