Transformar función "natural breaks" de Jenks desde Java a Javascript

Según la wikipedia, la función de quiebres naturales (o natural breaks) de Jenks es un método de clasificación de datos diseñado para determinar la mejor disposición de valores en diferentes clases.

En este apunte tomaré un código Java que sirve para calcular dicha función y lo transformaré a su versión en Javascript.

El método en Java recibe dos parámetros:

1.- Arreglo con los valores cuya mejor disposición queremos determinar (ArrayList list)
2.- y la cantidad de clases (int numclass)

import java.util.ArrayList;
import java.util.Comparator;

public class NaturalBreakJenks {

/**
     * @return int[]
     * @param list com.sun.java.util.collections.ArrayList
     * @param numclass int
     */
    public int[] getJenksBreaks(ArrayList list, int numclass) {

        //int numclass;
        int numdata = list.size();
        
        double[][] mat1 = new double[numdata + 1][numclass + 1];
        double[][] mat2 = new double[numdata + 1][numclass + 1];
        double[] st = new double[numdata];
        
        for (int i = 1; i <= numclass; i++) {
            mat1[1][i] = 1;
            mat2[1][i] = 0;
            for (int j = 2; j <= numdata; j++)
                mat2[j][i] = Double.MAX_VALUE;
        }
        double v = 0;
        for (int l = 2; l <= numdata; l++) {
            double s1 = 0;
            double s2 = 0;
            double w = 0;
            for (int m = 1; m <= l; m++) {
                int i3 = l - m + 1;

                double val = ((Double)list.get(i3 -1)).doubleValue();
         
                s2 += val * val;
                s1 += val;
                
                w++;
                v = s2 - (s1 * s1) / w;
                int i4 = i3 - 1;
                if (i4 != 0) {
                    for (int j = 2; j <= numclass; j++) {
                        if (mat2[l][j] >= (v + mat2[i4][j - 1])) {
                            mat1[l][j] = i3;
                            mat2[l][j] = v + mat2[i4][j - 1];

                        };
                    };
                };
            };
            mat1[l][1] = 1;
            mat2[l][1] = v;
        };
        int k = numdata;
                
        int[] kclass = new int[numclass];
        
        kclass[numclass - 1] = list.size() - 1;
        
        for (int j = numclass; j >= 2; j--) {
            System.out.println("rank = " + mat1[k][j]);
            int id =  (int) (mat1[k][j]) - 2;
            System.out.println("val = " + list.get(id));
            //System.out.println(mat2[k][j]);
            
            kclass[j - 2] = id;
            
            k = (int) mat1[k][j] - 1;
            
        };
        return kclass;
    }
}

Si defino el arreglo con los valores [1.1, 1.2, 1.3, 2.5] y le asigno valor 4 a numclass, luego ejecuto el método en Java obtengo los siguientes resultados:

rank = 4.0
val = 1.3
rank = 3.0
val = 1.2
rank = 2.0
val = 1.1

Para transformar el código Java crearé un objeto Javascript demoninado naturalBreakJenks:

naturalBreakJenks = {
		arreglo : [],
		clases: 0,
		resultado :[],
		calcular: function(){
			this.arreglo.sort();
			var numdata = this.arreglo.length;
			
			var matriz1 = new Array(numdata + 1);
			for (var recorreMatriz = 0; recorreMatriz < matriz1.length; recorreMatriz++){
				matriz1[recorreMatriz] = new Array(this.clases + 1);
			}
			
			var matriz2 = new Array(numdata + 1);
			for (var recorreMatriz = 0; recorreMatriz < matriz2.length; recorreMatriz++){
				matriz2[recorreMatriz] = new Array(this.clases + 1);
			}
			var st = [numdata];
			
	        for (var i = 1; i <= this.clases; i++) {
	        	matriz1[1][i] = 1;
	        	matriz2[1][i] = 0;
	            for (var j = 2; j <= numdata; j++)

	                matriz2[j][i] = Number.MAX_VALUE;
	        }
	        
	        var v = 0;
	        for (var l = 2; l <= numdata; l++) {
	            var s1 = 0;
	            var s2 = 0;
	            var w = 0;
	            for (var m = 1; m <= l; m++) {
	                var i3 = l - m + 1;

	                var val = this.arreglo[i3 -1];
	         
	                s2 += val * val;
	                s1 += val;
	                
	                w++;
	                v = s2 - (s1 * s1) / w;
	                var i4 = i3 - 1;
	                if (i4 != 0) {
	                    for (var j = 2; j <= this.clases; j++) {
	                        if (matriz2[l][j] >= (v + matriz2[i4][j - 1])) {
	                            matriz1[l][j] = i3;
	                            matriz2[l][j] = v + matriz2[i4][j - 1];

	                        };
	                    };
	                };
	            };
	            matriz1[l][1] = 1;
	            matriz2[l][1] = v;
	        };
	        
	        var k = numdata;
               
	        var kclass = new Array(this.clases);
	        
	        kclass[this.clases - 1] = numdata - 1;
        	var contador = 0;
        	
	        for (var j = this.clases; j >= 2; j--) {
	            var id =  parseInt((matriz1[k][j]) - 2);
	        	this.resultado[contador++] = {
	        			"rank": matriz1[k][j],
	        			"val": this.arreglo[id]
	        	};

	            kclass[j - 2] = id;
	            
	            k = parseInt(matriz1[k][j] - 1);
	            
	        };
		}
}

El nuevo objeto Javascript contiene los siguientes elementos:

  • arreglo: Arreglo con los valores cuya mejor disposición queremos determinar.
  • clases: cantidad de clases.
  • resultado: arreglo asociativo que contendrá el resultado
  • calcular: función (o método) utilizado para calcular.

Para probar el objeto naturalBreakJenks de Javascript utilizaré los mismo valores que utilicé con Java:

naturalBreakJenks.arreglo = [1.1, 1.2, 1.3, 2.5]; //Asigno valores al arreglo
naturalBreakJenks.clases = 4; // Asigno valor a "clases"
naturalBreakJenks.calcular(); // Ejecuto el método "calcular"
// Recorro y muestro el arreglo con los resultados
for (var res in naturalBreakJenks.resultado){
	alert(naturalBreakJenks.resultado[res]["rank"] + " => " + naturalBreakJenks.resultado[res]["val"])
}

4 => 1.3
3 => 1.2
2 => 1.1

Espero que este ejemplo sea útil sobre todo para mis amigos famosos como "Chayanne"!

Pueden descargar el ejemplo Javascript aquí

Herramientas tecnológicas: