Flatten curve of array of numbers - javascript

I'm trying to find the technique of Normalising (not sure if that is the right word) a range of numbers.
Say I have an array:
[0, 1, 2, 3, 4, 70, 80, 900]
I want to flatten or average out the range curve, so it's more like:
[10, 11, 12, 13, 14, 50, 100, 300]. // not a real calculation
So increasing the smaller numbers in relation to reducing the larger numbers.
What is this technique called? Normalised scale? I wish to implement this in some Javascript.
UPDATE: Here is hopefully a better description of what I'm trying to do:
I have an original array of numbers:
[0, 10, 15, 50, 70, 100]
When processed through a function averageOutAllNumbers(array, slider) will produce and array that when slider is set to 100% looks like:
[0, 20, 40, 60, 80, 100] // the curve has been flattened
when slider is set to 0%, it will return the original array. If slider is set to 50%, the returned array will look something like:
[0, 12, 19, 52, 88, 100] // the curve is less steep [do not take these number literally, I'm guess what the output would be based on the slider].
the array.max() will alway be 100.

Thanks for the comments so far, they did point me closer to the solution.
No thanks to the trolls who down-voted; if you can't fix it no one can—right!
When I updated my question I realised that "increasing the smaller numbers in relation to reducing the larger numbers" would eventually lead to an evenly distributed set of numbers, e.g. [20, 20, 20, 20, 20]. However I did actually want something like I stated in the question: [0, 20, 40, 60, 80, 100] // the curve has been flattened. I did some more searching for things like:
Evenly space an array of oddly spaced numbers
Making a list of evenly spaced numbers in a certain range
Return evenly spaced numbers over a specified interval
Find what percent X is between two numbers
Amongst the list of search result I saw the answer to my original question: "What is this technique called?" Linear Interpolation
Based on that I was able to create the following:
var orig = [3, 11, 54, 72, 100];
function lerp(n, x0, x1) {
// returns a position: x that is n percent between y0 and y1
// As numbers in array are x only, y values are fixed to 0(start) - 1(end)
const y0 = 0;
const y1 = 1;
const x = ((y1 - n)*x0 + (n - y0)*x1) / (y1 - y0);
return x;
}
var flattenedEven = orig.map((value, index, arr) => {
return lerp(1, value, (Math.max(...arr)/arr.length) * (index + 1));
});
//=> [20, 40, 60, 80, 100]
var flattenedCurve = orig.map((value, index, arr) => {
return lerp(.7, value, (Math.max(...arr)/arr.length) * (index + 1));
});
//=> [14.9, 31.3, 58.2, 77.6, 100]

Related

How to get coordinates in 2D starting from (0,0) with length and angle

I have the following coordinates
[-20, -20], [-20, 20], [-40, 0], [-40, 20], [-40, 40], [-20, 80], [20, 80], [40, 40], [80, 20], [80, -20].
My task is to get coordinates starting point from [0, 0].I can get the angles and the length from the above given coordinates. The angles should be between 0 to 360 positive.
Suppose I don't have the original coordinates now. I need to calculate the coordinates starting from 0,0 with angles and length.I need the current coordinate, previous and next coordinates.
Ex: if the current coordinates segment has a length of 5 then coordinates are (0,0) (0,5).The end point of the Predecessor is (0,0)and the start point of the Successor is (0,5).
What could be the formula,
I need to calculate keeping in mind the quadrants and rotate it finally ??
To find the next and previous coordinates I use as in quadrant 1.
x1=x+l * cos(angle), y1=y+l * sin(angle).
Do the above formulas change in my case w.r.t quadrants as below or they stand the same.
x1=x-l*cos(angle), y1=y-l*sin(angle) etc (change w.r.t quadrants).
Help me find out.
Actual Figure:
Red is current, blue is predecessor, black is successor.
Not java, but python, since it was tagged 'computational geometry'. You can translate if needed
Not totally sure this is what you need, but you have a center and a series of points. The angle (ie azimuth since it is from north) and the distance to those points can be calculated.
If it isn't what is needed, then you can pull out what you need from it.
a = np.array([[-20, -20], [-20, 20], [-40, 0], [-40, 20], [-40, 40], [-20, 80], [20, 80], [40, 40], [80, 20], [80, -20]])
pnt = np.array([0,0]
import numpy as np
def e_dist(a, b, metric='euclidean'):
"""Distance calculation for 1D, 2D and 3D points using einsum
`a`, `b` : array like
Inputs, list, tuple, array in 1, 2 or 3D form
`metric` : string
euclidean ('e', 'eu'...), sqeuclidean ('s', 'sq'...),
-----------------------------------------------------------------------
"""
a = np.asarray(a)
b = np.atleast_2d(b)
a_dim = a.ndim
b_dim = b.ndim
if a_dim == 1:
a = a.reshape(1, 1, a.shape[0])
if a_dim >= 2:
a = a.reshape(np.prod(a.shape[:-1]), 1, a.shape[-1])
if b_dim > 2:
b = b.reshape(np.prod(b.shape[:-1]), b.shape[-1])
diff = a - b
dist_arr = np.einsum('ijk,ijk->ij', diff, diff)
if metric[:1] == 'e':
dist_arr = np.sqrt(dist_arr)
dist_arr = np.squeeze(dist_arr)
return dist_arr
def radial_sort(pnts, cent=None, as_azimuth=False):
"""Sort about the point cloud center or from a given point
`pnts` : points
An array of points (x,y) as array or list
`cent` : coordinate
list, tuple, array of the center's x,y coordinates
>>> cent = [0, 0] or np.array([0, 0])
Returns:
-------
The angles in the range -180, 180 x-axis oriented
"""
pnts = np.asarray(pnts, dtype=np.float64)
if cent is None:
cent = _center(pnts, remove_dup=False)
ba = pnts - cent
ang_ab = np.arctan2(ba[:, 1], ba[:, 0])
ang_ab = np.degrees(ang_ab)
sort_order = np.argsort(ang_ab)
if as_azimuth:
ang_ab = np.where(ang_ab > 90, 450.0 - ang_ab, 90.0 - ang_ab)
return ang_ab, sort_order
Now if you use 'a' and 'pnt' above, you get the following results.
dist = e_dist(a, pnt)
angles = radial_sort(a, pnt, True)[0]
dist
Out[36]:
array([28.284, 28.284, 40. , 44.721, 56.569, 82.462, 82.462, 56.569, 82.462,
82.462])
angles
Out[37]:
array([225. , 315. , 270. , 296.565, 315. , 345.964, 14.036, 45. ,
75.964, 104.036])

set minimum and maximum values for axis' data in highchart

I want to set minimum and maximum values in an axis in highcharts. I have tried min, max, ceiling and floor attributes. They change the range of axis.
Link to the JS Fiddle
yAxis: {
floor: 0,
ceiling: 40,
title: {
text: 'Percentage'
}
},
series: [{
data: [0, 1, -5, 2, 3, 5, 8, 5, 50, 14, 25, 54]
}]
I want the data to be modified automatically based on the minimum/floor and maximum/ceiling values declared in the axis declaration.
For example, if in the above mentioned case, the last and last 4th elements of series should be automatically modified to 40 and the 3rd element to 0.
Is there any attribute of highchart using which I can achieve this without manually checking all the series elements to fall between the min and max values?
No I don't think there is no such a function. Highcharts does not alter your data, it just changes what is displayed. But checking for values beneath or above certain thresholds is really simple:
var data = [0, 1, -5, 2, 3, 5, 8, 5, 50, 14, 25, 54];
var max = 40;
var min = 0;
// clamping "by hand"
console.log( data.map(function(d) { return Math.max(min, Math.min(max, d)); }) );
If you use a library like lodash: these often provide a clamp function so you could write
console.log( _.map(data, function(d) { return _.clamp(d, min, max) }

Resize array to specified length and evenly distribute value of indexes

I am very bad at math, so forgive me if this is super obvious, I did do research and couldn't find anything matching what I need.
I'm trying to make my audio analyser responsive in width. This has proven to be very difficult, as the frequency array has a fixed size.
By calculating the width of each bar, and against the width of the canvas, I can easily find out how many bars could fit inside it.
So let's say the canvas is 20 pixels wide, and each bar is 4 pixels wide (including space between). That would mean 5 bars could fit inside the canvas. So using this information, we could create a function that resizes the frequency array length to 5, and evenly distributes frequencies into those 5 indexes.
This way no frequency range is left out, and the analyser can be resized to any width, and still fill the space.
function resize(array, integer) {
...
return result;
}
array = [1, 2, 3, 4, 5... 100] // Array that goes from 1 to 100 (100 indexes)
resize(array, 10) // [1, 10, 20, 30, 40, 50, 60, 70, 80, 90]
Here's the original array displayed on the canvas (the dotted bars are the ones that are outside of the canvas)
Now I want to resize the array so that the bars fit in the canvas like this (20 bars in this example)
I really hope I explained myself well here, this was very difficult to explain
This solution works. Now. With returning the nth value factor of the original array.
function arrayResize(array, integer) {
var factor = array.length / integer;
return Array.apply(null, { length: integer }).map(function (_, i) {
return array[Math.floor(i * factor)];
});
}
var array = Array.apply(null, { length: 100 }).map(function (_, i) { return Math.floor(i / 10); });
document.write('<pre>' + JSON.stringify(array, 0, 4) + '</pre>');
document.write('<pre>' + JSON.stringify(arrayResize(array, 5), 0, 4) + '</pre>');
document.write('<pre>' + JSON.stringify(arrayResize(array, 10), 0, 4) + '</pre>');
document.write('<pre>' + JSON.stringify(arrayResize(array, 20), 0, 4) + '</pre>');

Mapping array of numbers into percentages with the total value equaling 100%

Hi I'm not sure how to title this but my problem is that I'm trying to find a way to map an array of numbers into an array of percentages constrained by a min and max percentage. In addition to that I would also like the total percentages to add up to 100%. Specifically this relates to NBA lottery odds. I would like to take
nbaLotteryTeamsWinAmount2013 = [20, 21, 24, 25, 27, 28, 29, 29, 31, 33, 34, 34, 41, 43];
and convert it into percentages ranging from .5% to 25%
nbaLotteryOdds2013 = [.25, ..., 0.5];
I have tried some ways of mapping it over but I get more of a linear transformation but I think I need a logrithmic scale or something else. Heres what I tried.
function offset(array, selectedMin, selectedMax) {
var minValue = array.reduce(min);
var maxValue = array.reduce(max);
var selectedSpread = selectedMax - selectedMin;
array = array.map(function(item) {
return (item - minValue) / (maxValue - minValue) * selectedSpread + selectedMin;
});
}
offset(nbaLotteryTeamsWinAmount2013, 0, 25);
The result of this was
[0, 1.0869565217391304, 4.3478260869565215, 5.434782608695652, 7.608695652173914, 8.695652173913043, 9.782608695652174, 9.782608695652174, 11.956521739130435, 14.130434782608695, 15.217391304347828, 15.217391304347828, 22.82608695652174, 25]
This resulted in a linear scale where the numbers looked to be on the same scale but didnt add up to the right amount.
I'd like to do this in javascript (just because I know it better) but any language or hints would be appreciated.

Optimal algorithm for segmenting set of integers into labels for a chart axis?

Say you get values anywhere from 0 to 1,000,000,000, and you want to plot 30 days. So one particular chart may have a set like:
[ 1, 465, 123, 9, ... ]
While another chart can have a set with much larger numbers:
[ 761010, 418781, ... ]
Is there an "optimal algorithm" that can take those values and segment them into "clean" numbers? Sorry for the wording, don't know the right terminology, I will try to explain.
By "optimal algorithm", I mean both in terms of minimum number of computational steps, given that it creates labels (say for the y-axis) that are simplest from a human perspective.
For example, say you always want to divide the y-axis into 5 labels. You could do this:
var max = Math.max.apply(Math, values); // 465 (from the first set of values)
var interval = max / 5;
var labels = [ interval * 0, interval * 1, interval * 2, ... ];
But that creates labels like:
[ 0, 93, 186, ... ]
And that would be complex for humans to understand. What would be better (but still not ideal) is to create labels like:
[ 0, 125, 250, 375, 500 ]
But that's still to specific. Somehow it should figure out that a better segmentation is:
[ 0, 200, 400, 600, 800 ]
That way, it's divided into more intuitive chunks.
Is there a standard way to solve this problem? What algorithm works best?
Some maths
var getLabelWidth = function(sep, max_value){
var l = (""+max_value).length;
var av = max_value/sep/Math.pow(10,l-2); // get the length max 2 digit
/// 15.22
var width = (Math.ceil(av)*Math.pow(10,l-2)); // do a ceil on the value retrieved
// and apply it to the width of max_value.
// 16 * 10 000
return width;
}
console.log(getLabelWidth(2,59)); // 30 : [0, 30, 60]
console.log(getLabelWidth(2,100)); // 50 : [0, 50, 100]
console.log(getLabelWidth(2,968)); // 490 : [0, 490, 980]
console.log(getLabelWidth(3,368)); // 130 : [0, 130, 260, 390]
console.log(getLabelWidth(3,859)); // 290 : [0, 290, 580, 870]
console.log(getLabelWidth(3,175)); // 60 : [0, 60, 120, 180]
console.log(getLabelWidth(3,580)); // 200 : [0, 200, 400, 600]
console.log(getLabelWidth(3,74)); // 25 : [0, 25, 50, 75]
console.log(getLabelWidth(4,1111)); // 300 :[0, 300, 600, 900, 1200]
console.log(getLabelWidth(4,761010)); // 200 000: [0, 200000, 400000, 600000, 800000]
It could be improved a little bit i guess,
sorry for my bad english .
For reference, here's what I ended up doing.
function computeLabels(count, max) {
var magnitude = orderOfMagnitude(max);
var multiplier = magnitude * count;
// 1
if (multiplier >= max) return buildLabels(count, multiplier);
// 2
multiplier *= 2;
if (multiplier >= max) return buildLabels(count, multiplier);
// 5
multiplier *= 5;
if (multiplier >= max) return buildLabels(count, multiplier);
// 10, don't think it will ever get here but just in case.
multiplier *= 10;
if (multiplier >= max) return buildLabels(count, multiplier);
}
function buildLabels(count, multiplier) {
var labels = new Array(count);
while (count--) labels[count] = formatLabel(count * multiplier);
return labels;
}
function formatLabel(value) {
if (value > 10e5) return (value / 10e5) + 'M'; // millions
if (value > 10e2) return (value / 10e2) + 'K'; // thousands
return value; // <= hundreds
}
function orderOfMagnitude(val) {
var order = Math.floor(log10(val) + 0.000000001);
return Math.pow(10, order);
}
After drawing it out on paper, the "desirable" labels seemed to follow a simple pattern:
Find the max value in the set.
Get the order of magnitude for it.
Multiply the order of magnitude by the number of ticks.
Iterate: If that previous calculation is greater than the max value, then use it. Otherwise, multiply the value times 2 and check. If not, try times 5. So the pattern is, 1, 2, 5.
This gives you labels that are like:
10, 20 (2 ticks)
20, 40
50, 100
100, 200
200, 400
500, 1000
...
10, 20, 30 (3 ticks)
20, 40, 60
50, 100, 150 (don't like this one too much but oh well)
100, 200, 300
10, 20, 30, 40 (4 ticks)
...
It seems like it can be improved, both in producing better quality "human readable" labels, and in using more optimized functionality, but don't quite see it yet. This works for now.
Would love to know if you find a better way!

Categories

Resources