How do you determine the bucket to which a number belongs to? For ex, lets say we have buckets 0 - 20, 21 - 50, 51 - 80, 81 - 100 or the equivalent grades as 'Poor', 'Average', 'Good', 'Great'. Is there an efficient way using jquery/lodash/d3/underscore to find out that '45' belongs to the '21 - 50' bucket or is 'Average'?
Edit: Is this the best way to do it? In terms of speed, minimal code.
Here's what I have with a lot of help;
// Set up your data
var range = [[0, 20], [21, 50], [51, 80], [81, 100]]
var number = 45
range.find(function(val) { return val[1] >= number })
// Returns [21, 50]
range.findIndex(function(val) { return val[1] >= number })
// Returns 1
This should work...
var range = [[0, 20], [21, 50], [51, 80], [81, 100]]
var number = 45
var bucket = range.filter(function(a) {
if (number >= a[0] && number <= a[1]) return a
})
console.log(bucket[0])
You can use an array of objects with properties set to from "Poor" to "Great" corresponding to the range of numbers set as value of the property, Array.prototype.filter()
var range = [{
Poor: [0, 20]
}, {
Average: [21, 50]
}, {
Good: [51, 80]
}, {
Great: [81, 100]
}];
var number = 45;
var res = range.filter(function(el) {
var key = el[Object.keys(el)];
return number > key[0] && number < key[1]
});
console.log(Object.keys(res[0])[0])
The D3 way using scales:
var scale = d3.scaleQuantize()
.domain([0,100])
.range(["very bad","bad","average","good","very good"]);
console.log(scale(34));
console.log(scale(55));
console.log(scale(91));
<script src="https://d3js.org/d3.v4.min.js"></script>
The nice thing of D3 is that the scale automatically divides the domain based on the number of values of the range. For instance, the snippet above has 5 values ("very bad","bad","average","good","very good"), and so 34 is "bad". In the snippet below, using only 3 values, 34 is "average":
var scale = d3.scaleQuantize()
.domain([0,100])
.range(["bad","average","good"]);
console.log(scale(34));
console.log(scale(55));
console.log(scale(91));
<script src="https://d3js.org/d3.v4.min.js"></script>
var ranges = [
{
name: 'Poor',
range: [0, 20]
},
{
name: 'Average',
range: [21, 50]
},
{
name: 'Good',
range: [51, 80]
},
{
name: 'Great',
range: [81, 100]
},
]
var number = 45;
var range = _.find(ranges, function (r) {
return _.inRange(number, r.range[0], r.range[1] + 1);
}).name;
Related
I am looking for a simple solution to the following example:
let rangeOfInterest = [25 , 44];
let input = [10, 20, 30, 40, 50, 60];
I'm interested in all values that are greater than 25 and less than 44 (inclusive). The rangeOfInterest may be completely inside or completely outside the input values, other examples include [85, 95] or [0, 100].
output1 = [30, 40];
If a value exists either side of this output then take that sample as well;
finalOutput = [20, 30, 40, 50];
I can achieve the above by applying a filter to the array then finding the index of the first and last elements of the result and extracting the additional samples based on that (if they exist). Is there a cleaner way of achieving this that doesn't involve ~20 lines of code?
Note: samples will be floats, used ints for a simpler example.
You can find the index of the first item that is larger than the minimum range, and the index of the first item that is larger than the maximum range, and slice according to the indexes (minIndex - 1, maxIndex + 1).
const fn = ([min, max], arr) => {
const startIndex = arr.findIndex(n => n > min); // find the index of the first item that is larger than min
const endIndex = arr.findIndex(n => n > max); // find the index of the first item that is smaller than max
return arr.slice(
startIndex > 1 ? startIndex - 1 : 0,
endIndex === -1 ? arr.length : endIndex + 1
);
};
const input = [10, 20, 30, 40, 50, 60];
console.log(fn([25 , 44], input));
console.log(fn([25 , 65], input));
console.log(fn([-25 , 65], input));
Try this:
let input = [10, 20, 30, 40, 50, 60];
let rangeOfInterest = [25 , 44];
let newOutput = input.filter(function(number) {
return Math.min(...rangeOfInterest) <= number && Math.max(...rangeOfInterest) >= number;
});
newOutput.push( Math.floor(Math.min(...rangeOfInterest) / 10) * 10 ); //adding 20
newOutput.push( Math.floor(Math.max(...rangeOfInterest) / 10) * 10 ); // adding 40
console.log(newOutput)
let roi = [25 , 44]; //simplifying range of interest variable name
let arr = [10, 20, 30, 40, 50, 60];
function inRange(val1,val2){//assuming val1<val2 always
return (num)=>{
return num>=val1 && num<=val2;
}
}
let firstInd=0;
let lastInd=0;
var resultArr = arr.filter((val,ind) => {
let match = inRange(roi[0],roi[1])(val);
if(!firstInd){
firstInd=match?ind:0;
}
lastInd = match&&(ind>lastInd)?ind:lastInd;
return match;
});
lastInd<arr.length?resultArr.push(arr[lastInd+1]):'';
firstInd>0?resultArr.splice(0,0,arr[firstInd-1]):'';
console.log(resultArr);
I'm starting to learn about deep-learning and found synaptic.js.
I would like to create a prediction system where I have a input of numbers and would like the AI to understand the pattern.
My training data would be a array of 2 numbers, and the output I want to validate is [x, y, z] where x and z are kind of booleans for even/odd, and y is the sum of both numbers in the imput.
So:
var trainingSet = [{
'input': [20, 34],
'output': [1, 54, 0]
}, {
'input': [22, 33],
'output': [1, 55, 1]
},{
'input': [24, 35],
'output': [1, 59, 1]
},{
'input': [23, 36],
'output': [0, 59, 0]
}];
and I would like the AI to know the answer if I input [20, 31].
How would I set up such logic?
I started a jsFiddle based on a YouTube talk but don't understand what the code does actually...
Made a loop to generate trainig data in this jsFiddle that basically is:
// training data generator:
var trainingSet = [];
for (var i = 0; i < 500; i++) {
var obj = {};
obj.input = [
Math.random() * 10,
Math.random() * 10
].map(Math.round);
obj.output = [
Number(obj.input[0] % 2 == 0),
obj.input[0] + obj.input[1],
Number(obj.input[1] % 2 == 1)
]
trainingSet.push(obj);
}
document.body.innerHTML = JSON.stringify(trainingSet);
Unless the generator you build is simply to explain the problem to us, there's no way the problem can be solved. More formally, no function exists such that you can recover the input from the output. The generator produces random numbers and what is preserved is whether they were odd / even and the sum. There exists an infinite set of numbers that fulfills these criteria. From your example: 54 = 20 + 34 = 18 + 36 = 16 + 38 ... If there was a process driving this, it can be done. But it's random. Your neural network can never learn a pattern because there is no pattern.
Here is my code;
var data = [[40, 20, 60], [20, 30, 10], [50, 75, 40]];
var averageData = [];
data.forEach(function(entries) {
entries.reduce(function(a, b) {
return a + b[1];
}, 0);
console.log(entries);
});
I would like to be able to add the numbers from each array together.
But I'm not sure how I can get each array of numbers added together from the forEach loop?
From this data I would like to output instead [120, 60, 165].
It is important that the data is within a nested array, the aim is to try get it out of the nested array onto a single line with the above output.
Hope someone can offer some advice!
Thanks
Use Array#map instead
Note that b[1] holds nothing(undefined) and entries.reduce returns a reduced value, either return it of keep it in variable
var data = [
[40, 20, 60],
[20, 30, 10],
[50, 75, 40]
];
var averageData = data.map(function(entries) {
return entries.reduce(function(a, b) {
return a + b;
}, 0);
});
console.log(averageData)
Edit-
As suggested in comments by #Tushar, ES6 version
var data = [
[40, 20, 60],
[20, 30, 10],
[50, 75, 40]
];
console.log(data.map(arr => arr.reduce((a, b) => a + b, 0)));
reduce() will return the sum as you want at last. See as below.
var data = [[40, 20, 60], [20, 30, 10], [50, 75, 40]];
var averageData = []
data.forEach(function(entries) {
var sum = entries.reduce(function(a, b) {
return a + b;
}, 0)
console.log(sum)
})
If your values are safe (meaning you know where there come from) you could use eval to quickly sum the values.
var data = [[40, 20, 60], [20, 30, 10], [50, 75, 40]];
var totals = [];
data.forEach(function(a, i) {
totals.push(eval(a.join('+')));
});
console.log(totals);
Not the most efficient way but it works
I have a chart in highcharts and i'm experiencing trouble passing through data. Right now the problem is the graph is completely vertical, and it is receiving the wrong points for the graph. Its saying its totals are 80, and 2011. The totals are supposed to be 50, and 80.
var myarrays = [2011, 1, 12, 50, 2011, 2, 13, 80];
data:
(function () {
var data = [];
for (var i = 0; i < myarrays.length; i++)
{
data.push(myarrays[i], myarrays[i + 1], myarrays[i+2], myarrays[i+3]);
i+2;
}
return data;
}())
The out put is supposed to return, two sets because the series takes in 4 parameters,
[2011, 1, 12, 50]
[2011, 2, 13, 80]
Also when i manually pass in variables, the format that seems to work is
[[Date.UTC(2011, 7, 11), 101]
So is there any way to turn the return to the above format?
var myarrays = [2011, 1, 12, 50, 2011, 2, 13, 80];
$(function() {
$('#container').highcharts({
chart: 'spline',
},
series: [{
name: 'MySeries',
data:
(function() {
var data = [];
for (var i = 0; i < myarrays.length; i++) {
data.push(myarrays[i], myarrays[i + 1], myarrays[i + 2], myarrays[i + 3]);
i +=3;
}
return data;
}]
});
)]);
<script src="https://code.highcharts.com/highcharts.js"></script>
<div id="container" style="height: 400px"></div>
A data can be provided to the spline series in the following forms...
data = [y1, y2, ..., yn];
data = [[x1, y1], [x2, y2], ..., [xn, yn];
data = [[name1, y1], [name2, y2], ..., [namen, yn]];
data = [{x: x1, y: y1}, {x: x1, y: y2}, ... {x: xn, y: y2}];
where x and y values are numbers and name values are strings. If x values are not included then the x values are automately calculated starting at 0 and incrementing by 1.
In your example, the myarray has the form [year1, month1, day1, total1, year2, month2, day2, total2, ...] where every four values represent a single data point in the chart. You need to combine the year, month, and day values into a single number or string value which will then be used as the x or name value for the data point. The total value will be used as the y value for the data point. The code might then look something like...
var myarrays = [2011, 1, 12, 50, 2011, 2, 13, 80];
$(function() {
$('#container').highcharts({
chart: {
type: 'spline'
},
series: [{
name: 'MySeries',
data: (function() {
var data = [];
for (var i = 0; i + 3 < myarrays.length; i += 4) {
var name = myarrays[i] + "/" + myarrays[i + 1] + "/" + myarrays[i + 2];
var y = myarrays[i+3];
data.push([name, y]);
}
return data;
})()
}]
});
});
I have some data, which is the time in seconds (from people completing a workout). I want to graph these times in HighCharts, but I feel like I'm going about this all wrong. Right now I'm having to fill in the spaces between values with 0's. Also, note that some people finished in the exact same times
//Time in seconds, already ordered
var rawData = [183, 189, 195, 237, 256, 298, 306, 314, 328, 330, 330, 330, 338, 364, 370, 411, 411, 458, 474, 513, 513, 566, 572, 574, 600];
//Get the smallest & largest values.
//This is simple since it's already ordered
var min = rawData[0];
var max = rawData[rawData.length - 1];
//Now fill in the missing places in the data.
//Count how many items have the exact same value
var graphData = [];
for (var i = min; i <= max; i++) {
graphData.push(_.filter(rawData,function(m){
return m === i;
}).length);
}
I know this can't be the best way to do what I want, and I know it has some bugs with it. Can anyone suggest a better way of doing this?
Demo: http://codepen.io/chrismbarr/pen/epGGYX?editors=001
You don't need to do that. You only need to create a 2D array with the x and y values, eg.
var graphData = [];
for (var i in rawData) {
graphData.push([rawData[i], _.filter(rawData,function(m){
return m === rawData[i];
}).length]);
}
which creates an array like
[
[183, 1],
[189, 1],
[195, 1],
[237, 1],
[256, 1],
[298, 1],
// so on
[330, 3],
[330, 3],
[330, 3],
// ...
]
Update
another option is
var graphData = [];
for (var i = min; i <= max; i++) {
var ocurrences = _.filter(rawData, function(m){
return m === i;
}).length;
if (ocurrences > 0) {
graphData.push([i,ocurrences]);
}
}
which creates
[
[183, 1],
[189, 1],
[195, 1],
[237, 1],
[256, 1],
[298, 1],
// so on
[330, 3],
// ...
]
Both options work, you don't have to worry about the repeated items in the first resulting array since Highcharts draws the bars exactly in the same position, so you won't notice the difference.
Demo http://codepen.io/anon/pen/RWLjed?editors=001