metrics/confusionMatrix.js

/**
 * Generate a confusion matrix C where rows are actual classes and columns are predicted classes.
 * C = [
 *   [true negative, false positive],
 *   [false negative, true positive]
 * ].
 * 
 * If two classes are passed, class 0 represents the negative case, and class 1 represents the positive case.
 * If more than two classes are passed, an NxN confusion matrix is returned where N is the number of classes.
 * @memberof module:bcijs
 * @function
 * @name confusionMatrix
 * @param {number[]} predictedClasses - An array of predicted classes, with class numbers starting at 0
 * @param {number[]} actualClasses - An array of the actual classes, with class numbers starting at 0
 * @returns {number[][]} The confusion matrix
 */
export function confusionMatrix(predictedClasses, actualClasses){
    if(predictedClasses.length != actualClasses.length){
        throw new Error('predictedClasses length must equal actualClasses length');
    }
    
    let largestClass = Math.max(...predictedClasses.concat(actualClasses));
    // Fill a 2d array of size (largestClass + 1) x (largestClass + 1) with zeros
    let cMatrix = Array(largestClass + 1).fill().map(() => Array(largestClass + 1).fill(0));

    for(let i = 0; i < predictedClasses.length; i++){
        let predicted = predictedClasses[i];
        let actual = actualClasses[i];

        cMatrix[actual][predicted]++;
    }

    return cMatrix;
}