Javascript number data grouping and outlier removal - javascript

I have an array as follows:
var myArray = [3, 6, 8, 9, 16, 17, 19, 37]
I am needing to remove outliers as well as group the remaining data into any distinctive groups that appear. In this case 37 would be removed as an outlier and [3, 6, 8, 9] would be returned as the first group and [16, 17, 19] would be returned as the second.
Here is a second example
var mySecondArray = [80, 90, 100, 200, 280, 281, 287, 500, 510, 520, 800]
200 and 800 would be removed as an outlier, [80, 90, 100] would be the first group, [280, 281, 287] would be the second and [500, 510, 520] as the third.
I already have written code that works to remove outliers on the outside which is simple enough using the first and third quartile. In other words it would have no problem removing 800 from the mySecondArray as an outlier. But it would not remove 280 as an outlier.
I suppose that an outlier could then be defined as a group with less than n members so the real issue is what is an efficient method to divide up this data into an appropriate number of groups?
Any help is much appreciated!

jsFiddle Demo
This is just a simple implementation, it may not be the perfect solution to this set of problems but it should suffice for your example - it may work beyond that as well.
By looking at the average distance between your numbers, and comparing that distance to the distance on either side of each number, it should be possible to remove outliers. Thus following, the same metric can be used for grouping.
function Sum(arr){
return arr.filter(i => !isNaN(i)).reduce((p,c) => p+c,0);
};
function Avg(arr){
return Sum(arr) / arr.length;
}
function groupby(arr,dist){
var groups = [];
var group = [];
for(var i = 0; i < arr.length; i++){
group.push(arr[i]);
if(arr[i+1] == undefined)continue;
if(arr[i+1] - arr[i] > dist){
groups.push(group);
group = [];
}
}
groups.push(group);
return groups;
}
function groupOutlier(arr){
var distbefore = arr.map((c,i,a) => i == 0 ? undefined : c - a[i-1]);
var distafter = arr.map((c,i,a) => i == a.length-1 ? undefined : a[i+1] - c);
var avgdist = Avg(distafter);
var result = arr.filter((c,i,a) => !(distbefore[i] == undefined ? distafter[i] > avgdist : (distafter[i] == undefined ? distbefore[i] > avgdist : distbefore[i] > avgdist && distafter[i] > avgdist)));
return groupby(result,avgdist);
}
var myArray = [3, 6, 8, 9, 16, 17, 19, 37];
console.log(groupOutlier(myArray));
var mySecondArray = [80, 90, 100, 200, 280, 281, 287, 500, 510, 520, 800]
console.log(groupOutlier(mySecondArray));

Related

Filter numbers from an array which share the same digit without knowing the digit beforehand

I'm looking for an easy way to do the following:
I have an array of random numbers between 0-99. I need two arrays, first which have values which share the same first digit, second for values that share the same second digit. It doesn't matter what that digit is, as long as it occurs more than once. 0-9 is to be treated as if the first digit was 0.
To illustrate the problem:
[0, 10, 20, 11, 19, 12, 54, 64, 23, 24] would result in [10, 11, 12, 19, 23, 24] and [0, 10, 20, 24, 54, 64]. Some values can go in both arrays if they meet the criteria.
I've found solutions that are useful if you know the digit you're comparing. However, not in this scenario.
I know the way to begin is to convert numbers inside the array into strings: array.map(String), so that the second and first digit can be accessed by first[0], second[1]. I don't know how to proceed from there, though. Any ideas are greatly appreciated.
You can group your array based on first digit and last digit in an object accumulator. Then, filter the grouped array based on length.
const input = [0, 10, 20, 11, 19, 12, 54, 64, 23, 24],
groupLastDigits = input.reduce((r, n) => {
const last = n % 10;
r[last] ??= [];
r[last].push(n);
return r;
}, {}),
groupFirstDigits = input.reduce((r, n) => {
let first = Math.floor(n / 10);
if(first === n) first = 0;
r[first] ??= [];
r[first].push(n);
return r;
}, {}),
getNumbers = o => Object.values(o).filter(arr => arr.length > 1).flat()
commonLastDigits = getNumbers(groupLastDigits),
commonFirstDigits = getNumbers(groupFirstDigits);
console.log(commonLastDigits);
console.log(commonFirstDigits);
I'd like to offer a generic solution for n digits!
let matchingDigitGroups = function*(arr) {
// Note that very large numbers will convert to string
// in scientific notation, breaking this approach
let maxDigits = Math.max(...arr.map(num => `${num}`.length));
let strs = arr.map(num => {
let str = `${num}`;
while (str.length < maxDigits) str = `0${str}`;
return str;
});
for (let i = 0; i < maxDigits; i++) {
let result = [];
for (let n = 0; n <= 9; n++) {
let matchDigits = strs.filter(str => str[i] === `${n}`);
if (matchDigits.length > 1) result.push(...matchDigits);
}
yield result;
}
};
let tests = [
[ 0, 10, 20, 11, 19, 12, 54, 64, 23, 24 ],
[ 100, 200, 300 ],
[ 111, 222, 333, 771, 828, 399 ],
[],
[ 1 ],
[ 92929, 91919 ]
];
for (let arr of tests) {
console.log(`Groups for: [ ${arr.join(', ')} ]`);
for (let group of matchingDigitGroups(arr)) console.log(` [ ${group.join(', ')} ]`);
}

Vlookup like function in javascript

Actually I have made this on excel using Vlookup but now I am making this on webpage.
I have a input box where user will enter the value
<input class="text" type="text" name="rawScore" onchange="calcpercentile()">
and I have a span of where user can get the result
<span id="percentile"></span>
I have two arrays
var percentile = [10, 20, 30, 40, 50, 60, 70, 80, 90];
var rawScores = [1, 3, 5, 7, 10, 12, 18, 25, 27];
what code should I write that if I write so I get the
input value
(rawScores) (percentile)
1 10
2 20
3 30
4 40
Your example seems wrong. I expect score 1 to map to the 10th percentile, 2 & 3 to the 20th percentile, and 4 to the 30th percentile.
In essence, I think what you're trying to do is: find the array index of the first raw score that is greater than the input, and return the corresponding value from the percentiles array.
The Javascript could look something like this:
var percentiles = [10, 20, 30, 40, 50, 60, 70, 80, 90];
var rawScores = [1, 3, 5, 7, 10, 12, 18, 25, 27];
function map(input) {
let index = rawScores.findIndex(rawScore => rawScore >= input);
return percentiles[index];
}
console.log(map(1));
console.log(map(2));
console.log(map(3));
console.log(map(4));
Note that browser support for Array#findIndex() is limited. If you need wide browser support, a simple loop-based approach might be better:
var percentiles = [10, 20, 30, 40, 50, 60, 70, 80, 90];
var rawScores = [1, 3, 5, 7, 10, 12, 18, 25, 27];
function map(input) {
for (var i = 0; i < rawScores.length; i++) {
if (rawScores[i] >= input) {
return percentiles[i];
}
}
}
console.log(map(1));
console.log(map(2));
console.log(map(3));
console.log(map(4));
you can input text : 1
span display "10"
window.onload = function(){
var percentile = [0,10, 20, 30, 40, 50, 60, 70, 80, 90];
document.getElementById("rawScore").onchange = function () {
var index = document.getElementById("rawScore").value;
document.getElementById("percentile").innerHTML = percentile[index];
}
}
<input class="text" type="text" id="rawScore">
<span id="percentile"></span>
First you sort your dataset of course
const arr = [0,2,5,2,7,3];
const data = arr.sort();
What may help next, is this function to find the index of the closest number.
console.log(findClosestIndex([0, 1, 2, 3.5, 4.5, 5], 4));
// output: 3
console.log(findClosestIndex([0, 1, 2, 3.49, 4.5, 5], 4));
// output: 4
console.log(findClosestIndex([0, 1, 2, 3.49, 4.5, 5], 90));
// output: 5
console.log(findClosestIndex([0, 1, 2, 3.49, 4.5, 5], -1));
// output: 0
function findClosestIndex(arr, element) {
let from = 0, until = arr.length - 1
while (true) {
const cursor = Math.floor((from + until) / 2);
if (cursor === from) {
const diff1 = element - arr[from];
const diff2 = arr[until] - element;
return diff1 <= diff2 ? from : until;
}
const found = arr[cursor];
if (found === element) return cursor;
if (found > element) {
until = cursor;
} else if (found < element) {
from = cursor;
}
}
}
So, now you know your index and the length of your array. And you have to get a percentile from that. Let's first calculate an exact percentage.
const index = findClosestIndex(data, input);
const pct = index / arr.length;
Turning this percentage into a percentile is a matter of rounding it.
const percentile = (Math.floor(pct/10)+1) * 10;
(PS: I use this function for buying/selling stocks when their current price is in a certain percentile of the daily transaction price rates.)

interchange array using shift method

I am trying to interchange array and print it using shift method but not sure whether I can use it or not.
Code Snippet below.
var points = [40, 100, 1, 5, 25, 10];
//trying to achieve like anotherPoints array
//var anotherPoints = [1, 5, 100, 40, 25, 10];
for (index = 0; index < points.length; index++) {
points.shift();
console.log(points);
}
Some logic to get the desired result:
var points = [40, 100, 1, 5, 25, 10],
temp1 = [], temp2 = [], anotherArray;
points.forEach(function(val){
if(val < 10 ) {
temp1.push(val)
} else {
temp2.push(val);
}
});
anotherArray = temp1.sort().concat(temp2.sort(function(a,b){return b- a}));
alert(anotherArray);
It's not possible via shift or splice. Unless manually creating the array.
The shift() method doesn't shift or interchange the elements of an Array. It is similar to pop(), but it pops out the first element from the Array.
For example,
var points = [40, 100, 1, 5, 25, 10];
console.log(points.shift()); // 40
console.log(points); // [100, 1, 5, 25, 10]
As to your requirement of rearranging the elements of an Array, you will have to use Array.splice() method. Check out this question Reordering arrays.

Find all unique values that are sums of sub-arrays of an array of numbers... simple?

Sup fellow geeks!
I'm trying to make an array that lists all the possible values of the sums of the elements of an array. I'm sure this must be quite easy but I'm up to 2 or 3 hours now and I'm getting frustrated, I think I'm almost there...
var frootVals = [0,1,2,3,4,5]
var frootInc = frootVals
var fruitEnd = frootInc[frootInc.length-1]//begins at 5
var fruitAll = 15 // The total of all the numbers in the array. (this is actually
// calculated in another function, but lets just say I declared it as 15)
for (e = frootVals.length-2 ;fruitEnd !== fruitAll;e--){ //so I want it to
//finish when the final array entry is 15.
for (p = 1;p < e; p++){
var incEnd = frootInc[frootInc.length-p]
frootInc.push(incEnd + frootVals[p]) //1st time round (5 + 1 = 6, 5 + 2 = 7,
//5 + 3 =8, 5 + 4 = 9) THEN start again with 9 now being incEnd so pushes
//9+1 = 10 etc etc until the last digit is 15 and the whole loop stops...
}
}
EDIT - Basically the final result I'm after is frootInc to be be an array of the integers [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15] - I'm sure I'll kick myself for giving up but I've only been learning a few weeks so this is all quite brain taxing.
After thinking about your question a bit, I think the easiest solution would be with recursion when the condition that the final value added to the array is less than the sum of values.
Here's a JS Fiddle for demo: http://jsfiddle.net/ukgzwpky/
To break it down a bit (and so that you may confirm I have the question right :D), say we have the following array:
[0, 1, 2, 3, 10, 15, 30]
The sum of the values are: 61. So the expected output array would be:
[0, 1, 2, 3, 10, 15, 30, 31, 32, 33, 40, 45, 46, 47, 48, 55, 60, 61]
To further break it down, the looping logic would do something like this:
// Get final value to add to previous values
var val = [0, 1, 2, 3, 10, 15, 30].pop();
// Add the final value - 30 - to all previous values (ignoring the zero)
.. loop here and push the values to our array ...
// For the first iteration, the updated array looks like:
[0, 1, 2, 3, 10, 15, 30, 31, 32, 33, 40, 45]
// New values calculated from: 30 + 1, 30 + 2, 30 + 3, 30 + 10, 30 + 15
At this point, our max value of 61 is less than the final value of 45 So, we do it again!
var val = [0, 1, 2, 3, 10, 15, 30, 31, 32, 33, 40, 45].pop();
.. loop here and push the values to our array ...
// Second iteration, the updated array looks like:
[0, 1, 2, 3, 10, 15, 30, 31, 32, 33, 40, 45, 46, 47, 48, 55, 60, 61]
// New values are: 45 + 1, 45 + 2, 45 + 3, 45 + 10, 45 + 15
// Note that 45 + 30 would be greater than our sum of 61, so we break
If that's correct, here's a script that I wrote that populates such an array:
function getPopulatedArray(arr) {
var max = arguments[1] || getSum(arr),
current = arr.pop(),
temp = [],
i = 1,
len = arr.length;
// Populate temp array with values
for (; i < len; i++) {
if ((arr[i] + current) < max) {
temp.push(arr[i] + current);
} else {
temp.push(max);
break;
}
}
arr.push(current);
arr = arr.concat(temp);
// Done? Or should we continue?
if (arr[arr.length - 1] < max) {
return getPopulatedArray(arr, max);
} else {
return arr;
}
}
Again, the JS fiddle for demonstration: http://jsfiddle.net/ukgzwpky/
Hopefully this helps!
A very simple solution would be to do something like this:
var frootVals = [0,1,2,3,4,5]
var result = [];
for (var i = 0; i < frootVals.length; i++){ // Iterate over the array twice
for (var j = 0; j < frootVals.length; j++){ // To calculate all combinations
result.push(frootVals[i] + frootVals[j]);
}
}
Now, if you don't want duplicates, try this:
var frootVals = [0,1,2,3,4,5]
var result = [];
for (var i = 0; i < frootVals.length; i++){
for (var j = 0; j < frootVals.length; j++){
var value = frootVals[i] + frootVals[j];
if(result.indexOf(value) === -1){
result.push(value);
}
}
}
You could then use result = result.sort() if you want to output a sorted result.

Common values in multiple variable lenght javascript arrays

I have 7 arrays in javascript and I need to find values that are present in all of them.
I don't think I'm the first one to ask this but I can't find a solution for this. I read many answers but they all compare only 2 arrays and that logic don't work for multiple arrays.
I tried functions proposed in Simplest code for array intersection in javascript but they don't fit the kind of arrays I have.
The arrays I have can have different lengths in elements and the element's lengtt can vary too. I also may have zero item arrays in which they should not be compared against.
The main problem is with different number lengths. All functions I tried require sorting but this causes a problem.
Given this arrays:
xnombre = [1,2,3,4,5,24,44,124,125,165];
xacomp = [1,5,44,55,124];
xeje = [];
xanio = [1,5,44,55,124];
xini = [1,5,44,55,124];
xaporte = [1,5,44,55,122,123,124,144,155,166,245];
xpcia = [2,1,3,4,6,5,7,9,12,12,14,15,44,16,17,19,124];
The first to arrays are sorted to:
[1, 124, 125, 165, 2, 24, 3, 4, 44, 5]
[1, 124, 44, 5, 55]
Which when I "intersect" I only get [1,124] but 44 and 5 are missed.
Any help would be appreciated.
Thanks
The function from the other question works, but you have to sort your array numerically, not lexicographically, since you are working with numbers, not strings.
function sortNumber(a,b) {
return a - b;
}
var xnombre = [1,2,3,4,5,24,44,124,125,165];
var xacomp = [1,5,44,55,124];
xnombre.sort(sortNumber);
xacomp.sort(sortNumber);
DEMO
To apply this to multiple arrays, you could apply this function consecutively:
// var result = intersect(a, b, c, ...);
function intersect(var_args) {
// sort arrays here or beforehand
var target = arguments[0];
for (var i = 1; i < arguments.length; i++) {
if (arguments[i].length > 0) {
target = intersection_safe(target, arguments[i]);
}
}
return target;
}
This requires some of the new array methods, but it produces your desired output.
function intersection() {
var arrs = Array.prototype.filter.call(arguments, function (a) {
return a.length > 0;
}).sort(function (a, b) { // sort the arrays, so that we test the shortest.
return a.length - b.length;
});
var rest = arrs.slice(1),
test = arrs[0];
return test.filter(function (x) { return rest.every(function (a) { return a.indexOf(x) !== -1; }); });
}
var xnombre = [1, 2, 3, 4, 5, 24, 44, 124, 125, 165],
xacomp = [1, 5, 44, 55, 124],
xeje = [],
xanio = [1, 5, 44, 55, 124],
xini = [1, 5, 44, 55, 124],
xaporte = [1, 5, 44, 55, 122, 123, 124, 144, 155, 166, 245],
xpcia = [2, 1, 3, 4, 6, 5, 7, 9, 12, 12, 14, 15, 44, 16, 17, 19, 124];
intersection(xnombre, xacomp, xeje, xanio, xini, xaporte, xpcia)
// => [1, 5, 44, 124]
I tried your problem with underscore.
var _ = require('underscore');
xnombre = [1,2,3,4,5,24,44,124,125,165];
xacomp = [1,5,44,55,124];
xeje = [];
xanio = [1,5,44,55,124];
xini = [1,5,44,55,124];
xaporte = [1,5,44,55,122,123,124,144,155,166,245];
xpcia = [2,1,3,4,6,5,7,9,12,12,14,15,44,16,17,19,124];
var result = _.intersection(xnombre,xacomp,xanio,xini,xaporte,xpcia);
console.log(result);
But as you see that I haven't given the empty array, so somehow you have to ignore empty array.
Fiddle

Categories