cosine similarity evaluation for whyd

JavaScript

var tags = ["world", "World"];

var maxProportion = 0, minProportion = 1;
var avgProportion = 1 / tags.length; // ideal proportions for 2 tags: {tag1:0.5, tag2:0.5}
var avgProportionSimilDenom = Math.sqrt(Math.pow(avgProportion, 2) * tags.length);

function sum (a, b){
    return a + b;
}

function evalProportion(userTagSet) { // cosine similarity with ideal proportion vector
    var denomSum = 0;
    var numer = tags.map(function(tag){ // sum of (Ai * Bi)
        var taggedTracks = (userTagSet[tag] || {}).c || 0;
        var tagProportion = taggedTracks / userTagSet._t;
        denomSum += Math.pow(tagProportion, 2);
        //console.log("tag proportion", tag, tagProportion, " -> denom: ", denomSum);
        return tagProportion * avgProportion;
    }).reduce(sum);
    //console.log("=> user proportion", {numer: numer, 'Math.sqrt(denomSum)':Math.sqrt(denomSum)}, numer / (Math.sqrt(denomSum) * avgProportionSimilDenom));
    return !denomSum ? 0 : numer / (Math.sqrt(denomSum) * avgProportionSimilDenom);
}

function evalProportion(userTagSet) {
    var error = 0;
    var sumWeights = 0;
    for (var tag in userTagSet)
        if (tag != "_t")
            sumWeights += (userTagSet[tag] || {c:0}).c;
    return tags.map(function(tag){
        var p = (userTagSet[tag] || {c:0}).c / sumWeights;
        error += Math.abs(p - avgProportion)
        return p;
    }).reduce(sum) - error / tags.length;
}

userTagSets = [
    { "_t": 40 },
    { "_t": 40, "Jazz":{c:20} },
    { "_t": 40, "World":{c:10} },
    { "_t": 40, "Jazz":{c:10}, "World":{c:20} },
    { "_t": 40, "Jazz":{c:10}, "World":{c:10} },
    { "_t": 40, "Jazz":{c:20}, "World":{c:20} }
];

userTagSets.map(function(userTagSet){
    var score = evalProportion(userTagSet);
    delete userTagSet._t;
    console.log(JSON.stringify(userTagSet), "=>", score);
});