Skip to content

Commit

Permalink
Merge pull request #23 from ORNL-AMO/model-validation
Browse files Browse the repository at this point in the history
Added model validation test
  • Loading branch information
brappop authored Aug 9, 2018
2 parents 5e7d358 + 1858103 commit 120ddf6
Show file tree
Hide file tree
Showing 4 changed files with 187 additions and 20 deletions.
182 changes: 163 additions & 19 deletions app/enpi_tool.js
Original file line number Diff line number Diff line change
Expand Up @@ -612,7 +612,7 @@ function displayGraphs(){
for(var k = 0; k < displayJsons[i].length; k++) {
dataJsons[k]= [{}];
for (var j = 0; j < displayJsons[i][k].length; j++) {
dataJsons[k][j] = {modelCombo: k, modelYear: j, dependentNumber: i, rSquare: displayJsons[i][k][j].rSquare, savingsPercent: savingsLines[i][k][j], fittedModel: displayJsons[i][k][j].fittedModel};
dataJsons[k][j] = {modelCombo: k, modelYear: j, dependentNumber: i, rSquare: displayJsons[i][k][j].rSquare, savingsPercent: savingsLines[i][k][j], fittedModel: displayJsons[i][k][j].fittedModel, valid: tables[i]["results"][k]["model"+k][j]};
}
}

Expand Down Expand Up @@ -674,10 +674,14 @@ function makeGraphElements(number){
" <div id='savings-graph-container" + number + "' class='savings-graph-container'></div>\n" +
" </div>\n" +
" <div id='legendCol" + number + "' class='col-2' style='padding-left: 0px; overflow: auto;'>" +
" <div id='legend" + number + "' ></div>" +
" <div id='legend" + number + "' >" +
" <input type=\"radio\" onclick=\"changeView('line')\" checked=\"checked\" name=\"changeView\"> Line Graph<br>" +
" <input type=\"radio\" onclick=\"changeView('all')\" name=\"changeView\"> All Models<br>" +
" <input type=\"radio\" onclick=\"changeView('valid')\" name=\"changeView\"> Valid Only<br>" +
" </div>" +
" </div>\n" +
" </div>\n" +
" <div id='graph-row" + number + "' class=\"row graph-row\">\n" +
" <div id='graph-row" + number + "' class=\"row graph-row\">\n" +
" <div class=\"col-2\" style='padding: 0px'></div>\n" +
" <div id='graph-col' class=\"col-8\">\n" +
" <div id='range-display-row" + number + "' class='row range-display-row'>" +
Expand Down Expand Up @@ -862,18 +866,52 @@ function makeGraph(displayJson, number, combinations, dataJsons) {

activeModels[number] = [];

/*
function filterValidData(data) {
var validData = [];
for (var i = 0; i < data.length; i++) {
validData[i] =[{}];
for (var j = 0; j < data[i].length; j++) {
if (data[i][j]["valid"] === "Pass") {
validData[i][j] = data[i][j];
} else {
validData[i][j] = null;
}
}
}
return validData;
} */

for(var i = 0; i < dataJsons.length; i++) {

// clickableBoxData[count] = {data: dataJsons, dependentNumber: number, number: i};
clickableBoxData[count] = {data: dataJsons, dependentNumber: number, number: i};


//build line
svg.append("path")
.data([dataJsons[i]])
.attr("id", "line"+count)
.attr("class", "line")
.style("stroke", lineColors[i])
.style("stroke-width", "3px")
.style("fill", "none")
.attr("d", valueline);
// .data([dataJsons[i]])
.data([dataJsons[i]])
.attr("id", "line"+count)
.attr("class", "line-graph line")
.style("stroke", lineColors[i])
.style("stroke-width", "3px")
.style("fill", "none")
.attr("d", valueline);

var symbol = d3.symbol();

//build scatterplot, invisible by default
for (var j = 0; j < dataJsons[i].length; j++) {
var validity = (dataJsons[i][j]["valid"] === "Pass" ? " valid" : " invalid");
svg.append("path")
.data([dataJsons[i][j]])
.attr("d", symbol.type(function(d) { return d["valid"] === "Pass" ? d3.symbolCircle : d3.symbolCross;}))
.attr("transform", function(d) { return "translate(" + x(dataJsons[i][j].modelYear) + "," + y(dataJsons[i][j].rSquare) + ") rotate(45)" })
.attr("class", "scatterplot line line"+count + validity)
.style("fill", lineColors[i])
.style("visibility", "hidden");
}

legendSvg.append("rect")
.attr("id", "clickableTile"+count)
Expand Down Expand Up @@ -994,6 +1032,25 @@ function makeGraph(displayJson, number, combinations, dataJsons) {
newCol.style.fontSize = "20px";
}

function changeView(format) {
switch (format[0]) {
case 'l':
d3.selectAll(".scatterplot").style("visibility", "hidden");
d3.selectAll(".line-graph").style("visibility", "visible");
break;
case 'a':
d3.selectAll(".line-graph").style("visibility", "hidden");
d3.selectAll(".scatterplot").style("visibility", "visible");
break;
case 'v':
d3.selectAll(".line-graph").style("visibility", "hidden");
d3.selectAll(".invalid").style("visibility", "hidden");
d3.selectAll(".valid").style("visibility", "visible");
break;
default:
console.log("Failure in changeView()");
}
}
function remakeModelInfoTable(number, combinations){
//make model-info-table
var modelInfoTable = document.getElementById("model-info-table" + number);
Expand Down Expand Up @@ -1539,21 +1596,89 @@ function loadGraphListeners(combinations, displayJsons){
return y(d.rSquare);
});

var showHideElements = function(i){
if((document.getElementById("line"+i) && document.getElementById("line"+i).style.strokeWidth !== "0px") ||
(document.getElementsByClassName("line"+i) && document.getElementsByClassName("line"+i)[0].style.opacity !== "0")) {
d3.select("#line" + i)
.style("stroke-width", "0px");

d3.selectAll(".line" + i)
.style("opacity", "0");

d3.select("#savingsLine" + i)
.style("stroke-width", "0px");

d3.select("#clickableTile"+i)
.style("fill", "gray");


activeModels[Math.floor(i / combinations.length)][combinations[i % combinations.length]] = false;
changeModelInfoTableModels(Math.floor(i / combinations.length), combinations);

for(var z = 0; z < tables.length; z++){

var dataJsons = [];
for(var k = 0; k < displayJsons[z].length; k++) {
dataJsons[k]= [{}];
for (var j = 0; j < displayJsons[z][k].length; j++) {
dataJsons[k][j] = {modelCombo: k, modelYear: j, dependentNumber: z, rSquare: displayJsons[z][k][j].rSquare, savingsPercent: savingsLines[z][k][j], fittedModel: displayJsons[z][k][j].fittedModel};
}
}
makeSavingsGraph(displayJsons[z], z, combinations, dataJsons);
makeGuideLine(displayJsons[z], z, combinations, dataJsons);
}
}
else {
d3.select("#line" + i)
.style("stroke-width", "3px");

d3.selectAll(".line" + i)
.style("opacity", "1");

d3.select("#savingsLine" + i)
.style("stroke-width", "3px");

d3.select("#clickableTile"+i)
.style("fill", lineColors[clickableBoxData[i].number]);

activeModels[Math.floor(i / combinations.length)][combinations[i % combinations.length]] = true;
changeModelInfoTableModels(Math.floor(i / combinations.length), combinations);

for(var z = 0; z < tables.length; z++){

var dataJsons = [];
for(var k = 0; k < displayJsons[z].length; k++) {
dataJsons[k]= [{}];
for (var j = 0; j < displayJsons[z][k].length; j++) {
dataJsons[k][j] = {modelCombo: k, modelYear: j, dependentNumber: z, rSquare: displayJsons[z][k][j].rSquare, savingsPercent: savingsLines[z][k][j], fittedModel: displayJsons[z][k][j].fittedModel};
}
}
makeSavingsGraph(displayJsons[z], z, combinations, dataJsons);
makeGuideLine(displayJsons[z], z, combinations, dataJsons);
}
}
}

d3.selectAll(".clickableBox")
.each( function(d, i){
var i = i;
d3.select(this)
.on("click", () => {
if(document.getElementById("line"+i).style.strokeWidth !== "0px") {
if((document.getElementById("line"+i) && document.getElementById("line"+i).style.strokeWidth !== "0px") ||
(document.getElementsByClassName("line"+i) && document.getElementsByClassName("line"+i)[0].style.opacity !== "0")) {
d3.select("#line" + i)
.style("stroke-width", "0px");

d3.selectAll(".line" + i)
.style("opacity", "0");

d3.select("#savingsLine" + i)
.style("stroke-width", "0px");

d3.select("#clickableTile"+i)
.style("fill", "gray");


activeModels[Math.floor(i / combinations.length)][combinations[i % combinations.length]] = false;
changeModelInfoTableModels(Math.floor(i / combinations.length), combinations);

Expand All @@ -1570,10 +1695,13 @@ function loadGraphListeners(combinations, displayJsons){
makeGuideLine(displayJsons[z], z, combinations, dataJsons);
}
}
else{
else {
d3.select("#line" + i)
.style("stroke-width", "3px");

d3.selectAll(".line" + i)
.style("opacity", "1");

d3.select("#savingsLine" + i)
.style("stroke-width", "3px");

Expand Down Expand Up @@ -1604,16 +1732,21 @@ function loadGraphListeners(combinations, displayJsons){
var i = i;
d3.select(this)
.on("click", () => {
if(document.getElementById("line"+i).style.strokeWidth !== "0px") {
if((document.getElementById("line"+i) && document.getElementById("line"+i).style.strokeWidth !== "0px") ||
(document.getElementsByClassName("line"+i) && document.getElementsByClassName("line"+i)[0].style.opacity !== "0")) {
d3.select("#line" + i)
.style("stroke-width", "0px");

d3.selectAll(".line" + i)
.style("opacity", "0");

d3.select("#savingsLine" + i)
.style("stroke-width", "0px");

d3.select("#clickableTile"+i)
.style("fill", "gray");


activeModels[Math.floor(i / combinations.length)][combinations[i % combinations.length]] = false;
changeModelInfoTableModels(Math.floor(i / combinations.length), combinations);

Expand All @@ -1630,10 +1763,13 @@ function loadGraphListeners(combinations, displayJsons){
makeGuideLine(displayJsons[z], z, combinations, dataJsons);
}
}
else{
else {
d3.select("#line" + i)
.style("stroke-width", "3px");

d3.selectAll(".line" + i)
.style("opacity", "1");

d3.select("#savingsLine" + i)
.style("stroke-width", "3px");

Expand Down Expand Up @@ -1664,16 +1800,21 @@ function loadGraphListeners(combinations, displayJsons){
var i = i;
d3.select(this)
.on("click", () => {
if(document.getElementById("line"+i).style.strokeWidth !== "0px") {
if((document.getElementById("line"+i) && document.getElementById("line"+i).style.strokeWidth !== "0px") ||
(document.getElementsByClassName("line"+i) && document.getElementsByClassName("line"+i)[0].style.opacity !== "0")) {
d3.select("#line" + i)
.style("stroke-width", "0px");

d3.selectAll(".line" + i)
.style("opacity", "0");

d3.select("#savingsLine" + i)
.style("stroke-width", "0px");

d3.select(this)
d3.select("#clickableTile"+i)
.style("fill", "gray");


activeModels[Math.floor(i / combinations.length)][combinations[i % combinations.length]] = false;
changeModelInfoTableModels(Math.floor(i / combinations.length), combinations);

Expand All @@ -1690,14 +1831,17 @@ function loadGraphListeners(combinations, displayJsons){
makeGuideLine(displayJsons[z], z, combinations, dataJsons);
}
}
else{
else {
d3.select("#line" + i)
.style("stroke-width", "3px");

d3.selectAll(".line" + i)
.style("opacity", "1");

d3.select("#savingsLine" + i)
.style("stroke-width", "3px");

d3.select(this)
d3.select("#clickableTile"+i)
.style("fill", lineColors[clickableBoxData[i].number]);

activeModels[Math.floor(i / combinations.length)][combinations[i % combinations.length]] = true;
Expand Down Expand Up @@ -1986,7 +2130,7 @@ function exportData(){

var blob = new Blob([csv], {type: "text/plain;charset=utf-8"});

saveAs(blob, "export.xlsx");
saveAs(blob, "export.csv");

}

Expand Down
21 changes: 21 additions & 0 deletions app/maths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,23 @@ function getCombinations(array) {
return results.slice(1);
}

function modelCheck(independentCombinations, model, i, j, len) {
//1. The p-value for the overall model fit must be less than 0.10
if (model[i + "pValue"][j] < .1) {
var test = false;
//2. All independent variables included in the model must have a p-value of less than 0.20
for (var k = 0; k < len; k++) {
if (model[i + independentCombinations[i][k] + "pvalue"][j] >= .2) return false;
if (!test && model[i + independentCombinations[i][k] + "pvalue"][j] < .1) test = true;
}
//3. At least one of the independent variables in the model must have a p-value of less than 0.10
if (test) {
// 4. The adjusted R2 for the regression must be at least 0.50
return model[i + "adjustedRSquare"][j] >= .5;
}
}
return false;
}

//TODO Needs work
function findResults(json, dependentNumber){
Expand Down Expand Up @@ -55,10 +72,12 @@ function findResults(json, dependentNumber){
};
results["rSquare"] = [];
results[i + "rSquare"] = [];
results[i + "adjustedRSquare"] = [];
results["comboNumber"] = [];
results[i + "Intercept"] = [];
results[i + "fittedModel"] = [];
results[i + "pValue"] = [];
results["model" + i] = [];

for(var k = 0; k < independentCombinations[i].length; k++){
results[i + independentCombinations[i][k] + "Coeff"] = [];
Expand All @@ -80,6 +99,7 @@ function findResults(json, dependentNumber){

results["rSquare"][j] = model.rSquare;
results[i + "rSquare"][j] = model.rSquare;
results[i + "adjustedRSquare"][j] = model.adjRSquare;
results["comboNumber"][j] = i;
results[i + "Intercept"][j] = model.intercept;
results[i + "pValue"][j] = model.pValue;
Expand All @@ -90,6 +110,7 @@ function findResults(json, dependentNumber){
}

results[i + "fittedModel"][j] = model.fittedModel;
results["model" + i][j] = (modelCheck(independentCombinations, results, i, j, independentCombinations[i].length) ? "Pass" : "Fail");
}

totalResult[i] = results;
Expand Down
2 changes: 2 additions & 0 deletions app/regression_calc.js
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,7 @@ function slidingRegression() {
params: [],
yValues: "",
rSquare: "",
adjRSquare: "",
fStatistic: "",
pValue: "",
coeffPVals: [],
Expand Down Expand Up @@ -351,6 +352,7 @@ function slidingRegression() {
model.pValue = pValue;
//console.log("R-Square: " + RRSQ);
model.rSquare = RRSQ;
model.adjRSquare = 1 - ((1 - RRSQ) * (this.n - 1) / (this.n - this.m - 1));

//start analysis************************************************************

Expand Down
2 changes: 1 addition & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@
<div class="row"style="padding-top: 10px;">
<div class="col-12" style="text-align: center;">Project Manager/Engineer: Tom Wenning</div>
<div class="col-12" style="text-align: center;">Engineer: Kiran Thirumaran</div>
<div class="col-12" style="text-align: center;">Programmer: Michael Whitmer</div>
<div class="col-12" style="text-align: center;">Programmers: Michael Whitmer, Benjamin Rappoport</div>
</div>
</div>
</div>
Expand Down

0 comments on commit 120ddf6

Please sign in to comment.