javascriptarraysweighting

Bucketing numbers in an array


I have a list of numbers e.g 1-to 60, but not necessarily in increments of 1. E.g 1-40 in increments of 1, and 40-60 in increments of two.

I have another set of defined numbers (buckets) –e.g 2, 3, 5 , 10, 30, 50

I need to produce a two dimensional array with percentages of where each number from above (1 to 60) fits into which bucket.

to make this simpler: let’s say we have numbers 1 to 10, and buckets, 2, 3, 5, 10

I want my two dimensional array to look like this:

enter image description here

I can do this with a bunch of conditionals, but I think there’s a solution out there which I have not thought of and would be great if someone could shed some light! I need to do this in JavaScript, but any language would help me to try and understand any solution more optimal than lots of if’s deciding where each number fits and then doing (6-5/10-5)=0.2 for each cell.

I’m trying to avoid hardcoding buckets, 2, 3, 5, 10 so that any set of buckets or numbers can do the job.

EDIT: First of all, I'm sorry for the incomplete description - I was on my phone at the time and couldn't post on stackoverflow via a computer.

Both 1-10 and 2,3,5,10 represent years. Effectively, I'm trying to bucket each year from 1 to 10.

I hope this makes it clearer.


Solution

  • You could do something like this. It puts it in exactly the format you asked for in the post:

    function bucketize(numberList, buckets) {
        // buckets must contain values sorted from smallest to largest
        var bucketized = [];
        var i, j, lowBucket, difference, bucketSpan, ratio;
    
        for (i=0; i<numberList.length; i++) {
            bucketized[i]=new Array(buckets.length + 1);
            bucketized[i][0]=numberList[i];
            lowBucketIndex=null;
    
            for (j=0; j<buckets.length; j++) {
                if (lowBucketIndex === null && numberList[i] < buckets[j]) {
                    lowBucketIndex=j-1;
                    if (lowBucketIndex < 0) {
                        // this bucket gets it all
                        bucketized[i][j+1]=1;
                    } else {
                        //divide this value between buckets
                        difference = numberList[i] - buckets[lowBucketIndex];
                        bucketSpan = buckets[j] - buckets[lowBucketIndex];
                        ratio=difference/bucketSpan;
                        bucketized[i][lowBucketIndex+1] = 1-ratio;
                        bucketized[i][j+1] = ratio;
                    }
                } else {
                    bucketized[i][j+1]=0;
                }
            }
            if (lowBucketIndex === null) {
                bucketized[i][buckets.length] = 1;
            }
        }
    
        return bucketized;
    }
    
    var buckets = [2,3,5,10];
    var numberList=[1,2,3,4,5,6,7,8,9,10];
    var bucketized = bucketize(numberList, buckets);
    var i;
    
    for (i=0; i<bucketized.length; i++) {
        console.log(bucketized[i].join(','));
    }
    

    Here's a fiddle.