Trend computation test

author(s): Øystein Moseng

by oysteinmoseng

HTML

<script src="https://code.highcharts.com/highcharts.js"></script>
<script src="https://code.highcharts.com/modules/exporting.js"></script>
<script src="https://code.highcharts.com/modules/export-data.js"></script>
<script src="https://code.highcharts.com/modules/accessibility.js"></script>

<fieldset>
    <legend>
        Chart data
    </legend>
    <label class="datasel">
       Dataset 1
       <input type="radio" name="dataset" value="1" checked>
    </label>
    <label class="datasel">
       Dataset 2
       <input type="radio" name="dataset" value="2">
    </label>
    <label class="datasel">
       Dataset 3
       <input type="radio" name="dataset" value="3">
    </label>
    <label class="datasel">
       Dataset 4
       <input type="radio" name="dataset" value="4">
    </label>
    <label class="datasel">
       Dataset 5
       <input type="radio" name="dataset" value="5">
    </label>
    <label style="display: none">
       Random
       <input id="random" type="radio" name="dataset" value="random">
    </label>
    <button id="randomize">
        Randomize
    </button>
</fieldset>
<figure class="highcharts-figure">
    <div id="container"></div>
    <p class="highcharts-description" id="trend">Placeholder for trend</p>
</figure>
<div>
    <p>
        Fluctuation: <span id="fluctuation"></span>
    </p>
    <p>
        Trend coherence: <span id="coherence"></span>
    </p>
</div>

CSS

body {
    font-family: Verdana, sans-serif;    
}

.datasel {
    margin-right: 20px;    
}

.highcharts-figure, .highcharts-data-table table {
    min-width: 360px; 
    max-width: 800px;
    margin: 1em auto;
}

.highcharts-data-table table {
	border-collapse: collapse;
	border: 1px solid #EBEBEB;
	margin: 10px auto;
	text-align: center;
	width: 100%;
	max-width: 500px;
}
.highcharts-data-table caption {
    padding: 1em 0;
    font-size: 1.2em;
    color: #555;
}
.highcharts-data-table th {
	font-weight: 600;
    padding: 0.5em;
}
.highcharts-data-table td, .highcharts-data-table th, .highcharts-data-table caption {
    padding: 0.5em;
}
.highcharts-data-table thead tr, .highcharts-data-table tr:nth-child(even) {
    background: #f8f8f8;
}
.highcharts-data-table tr:hover {
    background: #f1f7ff;
}

JavaScript

function computeTrend(chart) {
	let pos = 0;
    let neg = 0;
    let posCount = 0;
    let negCount = 0;
    const points = chart.series[0].points;
    const len = points.length;
	
    for (let i = 1; i < len; ++i) {
        const diff = points[i].y - points[i - 1].y;
        if (!diff) {
	        continue;
        }
        if (diff > 0) {
        	pos += diff;
            posCount++;
        } else {
        	neg += -diff;
            negCount++;
        }
    }

    const fluctuation = pos > neg ? neg / pos : pos / neg;
    const trendCoherence = pos > neg ? posCount / negCount : negCount / posCount;

    return {
		// 1 for positive, -1 for negative, 0 for equal
    	trend: pos > neg ? 1 : pos === neg ? 0 : -1,
		// Fluctuation smaller means clearer trend. 0 is a perfect trend.
        fluctuation: fluctuation,
        // Trend coherence of 1 means equal number of points go against trend as with trend. Higher coherence means more outliers against trend. Lower coherence means more outliers towards trend. 
        trendCoherence: trendCoherence
    }
}


function showTrend(chart) {
	const trend = computeTrend(chart);
    const dir = trend.trend > 0 ? 'positive' : 'negative';
    let trendStr;
    if (trend.fluctuation < 0.1) {
    	trendStr = 'There is a very clear ' + dir + ' trend.';
    } else if (trend.fluctuation < 0.3) {
    	trendStr = 'There is a clear ' + dir + ' trend.';
    } else if (trend.fluctuation < 0.5) {
        if (trend.trendCoherence > 2.5) {
        	trendStr = 'There is a clear ' + dir + ' trend.';
        } else {
            trendStr = 'There is a fairly clear ' + dir + ' trend.';
        }
    } else if (trend.fluctuation < 0.7) {
        if (trend.trendCoherence > 2.5) {
        	trendStr = 'There is a fairly clear ' + dir + ' trend.';
        } else {
            trendStr = 'There is a fluctuating ' + dir + ' trend.';
        }
    } else if (trend.fluctuation < 0.8) {
        if (trend.trendCoherence > 2.5) {
        	trendStr =...