Skip to content

Instantly share code, notes, and snippets.

Created April 11, 2016 04:16
Show Gist options
  • Save anonymous/a20f8e7947f49dab5e41d79c3293e11c to your computer and use it in GitHub Desktop.
Save anonymous/a20f8e7947f49dab5e41d79c3293e11c to your computer and use it in GitHub Desktop.
ES Function Score Visualization

ES Function Score Visualization

Shows the score curves when given different settings for the three types of scoring functions available in Elasticsearch.

A Pen by Xiao Yu on CodePen.

License.

<form id="chart_control" style="display:none">
<div>
<label for="offset">Offset (days):</label>
<input id="offset" type="number" min="0" max="365" step="1" />
</div>
<div>
<label for="scale">Scale (days):</label>
<input id="scale" type="number" min="0" max="365" step="1" />
</div>
<div class="break">
<label for="decay">Decay:</label>
<input id="decay" type="number" min="0.000001" max="0.999999" step="0.000001" />
</div>
<div>
<input type="submit" value="Update Graph" />
</div>
</form>
<div class="break">
<canvas id="chart" width="600" height="220"></canvas>
</div>
( function( w ) {
var day_ms = 86400000;
var config = {
origin : 365 * day_ms, // 1971-01-01
offset : 2419200000, // 28 days
scale : 4838400000, // 56 days
decay : 0.5
};
var scorer = {
_distance: function( val ) {
return Math.max( 0, Math.abs( val - config.origin ) - config.offset );
},
gauss: function( val ) {
var segma_sq = -1 * Math.pow( config.scale, 2 ) / ( 2 * Math.log( config.decay ) );
return Math.exp( -1 * ( Math.pow( scorer._distance( val ), 2 ) / ( 2 * segma_sq ) ) )
},
exp: function( val ) {
var lambda = Math.log( config.decay ) / config.scale;
return Math.exp( lambda * scorer._distance( val ) );
},
linear: function( val ) {
var s = config.scale / ( 1 - config.decay );
return Math.max( ( ( s - scorer._distance( val ) ) / s ), 0 );
}
}
w.day_scorer = {
set_configs: function( offset_days, scale_days, decay ) {
config.offset = day_ms * offset_days;
config.scale = day_ms * scale_days;
config.decay = decay;
},
get_configs: function() {
return {
offset : config.offset / day_ms,
scale : config.scale / day_ms,
decay : config.decay,
}
},
// Calc scores for range of days defaults to a year
calc_days: function( first, last ) {
// Origin is always last day of range
config.origin = last * day_ms;
var s = {
gauss : [],
exp : [],
linear : []
};
for ( var d = first; d <= last; d++ ) {
s.gauss.push( scorer.gauss( d * day_ms ) );
s.exp.push( scorer.exp( d * day_ms ) );
s.linear.push( scorer.linear( d * day_ms ) );
}
return s;
}
};
} )( window );
( function( w, d ) {
// Set default chart configs
Chart.defaults.global.responsive = true;
Chart.defaults.global.scaleOverride = true;
Chart.defaults.global.scaleSteps = 4;
Chart.defaults.global.scaleStepWidth = 0.25;
Chart.defaults.global.scaleStartValue = 0;
Chart.defaults.global.tooltipXOffset = 0;
Chart.defaults.global.multiTooltipTemplate = "<%= value.toFixed(3) %><%if (datasetLabel){%> - <%=datasetLabel%><%}%>";
var first = 0,
last = 365;
var get_labels = function( all ) {
var months = [ 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec' ];
var labels = [];
for ( var day = first; day <= last; day++ ) {
var date = new Date( 86400000 * day );
if ( all || date.getUTCDate() == 1 ) {
labels.push( months[ date.getUTCMonth() ] + ' ' + date.getUTCDate() );
} else {
labels.push( '' );
}
}
return labels;
};
var get_filler = function() {
var filler = new Array( 1 + last - first );
filler.fill( 0 );
return filler;
};
var update_chart = function() {
var scores = w.day_scorer.calc_days( first, last );
var labels = get_labels( true );
[ 'gauss', 'exp', 'linear' ].forEach( function( type, i ) {
scores[ type ].forEach( function( s, j ) {
graph.datasets[ i ].points[ j ].value = s;
graph.datasets[ i ].points[ j ].label = labels[ j ];
} );
} );
graph.update()
}
var graph = null,
ctx = null,
data = {
labels: get_labels( false ),
datasets: [
{
label: "gauss",
fillColor: "rgba( 0, 135, 190, 0.05 )",
strokeColor: "rgba( 0, 135, 190, 1 )",
pointColor: "rgba( 0, 135, 190, 1)",
data: get_filler()
},
{
label: "exp",
fillColor: "rgba( 213, 78, 33, 0.05 )",
strokeColor: "rgba( 213, 78, 33, 1 )",
pointColor: "rgba( 213, 78, 33, 1 )",
data: get_filler()
},
{
label: "linear",
fillColor: "rgba( 168, 190, 206, 0.05 )",
strokeColor: "rgba( 168, 190, 206, 1 )",
pointColor: "rgba( 168, 190, 206, 1 )",
data: get_filler()
}
]
},
options = {
scaleShowVerticalLines: false,
pointDot: false,
pointHitDetectionRadius: 1
},
ids = {
chart : '',
offset : '',
scale : '',
decay : ''
};
w.decay_chart = {
init: function( chart_id, offset_id, scale_id, decay_id ) {
// Store IDs
ids = {
chart : chart_id,
offset : offset_id,
scale : scale_id,
decay : decay_id
}
// Set UI to default values
var configs = w.day_scorer.get_configs();
d.getElementById( ids.offset ).value = configs.offset;
d.getElementById( ids.scale ).value = configs.scale;
d.getElementById( ids.decay ).value = configs.decay;
ctx = d.getElementById( ids.chart ).getContext( "2d" );
graph = new Chart( ctx ).Line( data, options );
update_chart();
},
update: function() {
// Check new values from UI
var valid = true;
[ ids.offset, ids.scale, ids.decay ].forEach( function( id ) {
var elem = d.getElementById( id );
if ( false == elem.validity.valid ) {
valid = false;
if ( 'function' == typeof elem.reportValidity ) {
elem.reportValidity();
}
elem.className = "invalid";
} else {
elem.className = "";
}
} );
if ( !valid ) {
return;
}
w.day_scorer.set_configs(
parseInt( d.getElementById( ids.offset ).value ),
parseInt( d.getElementById( ids.scale ).value ),
parseFloat( d.getElementById( ids.decay ).value )
);
update_chart();
}
}
} )( window, document );
window.onload = function() {
var elem = document.getElementById( 'chart_control' );
elem.addEventListener( 'submit', function( event ) {
event.preventDefault();
window.decay_chart.update();
} );
elem.style.display = '';
window.decay_chart.init( 'chart', 'offset', 'scale', 'decay' );
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/1.1.1/Chart.js"></script>
body {
font-family: 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;
font-size: 13px;
}
label, input {
margin-bottom: 0.25em;
width: 7em;
display: block;
float: left;
}
input[type="number"] {
text-align: right;
}
input[type="submit"] {
width: 10em;
}
form div {
width: 14em;
float: left;
}
div.break {
clear: left;
}
.invalid {
color: #d94f4f;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment