--- title: "Dataset" date: 2019-09-27 16:41 draft: false --- <link rel="stylesheet" type="text/css" href="/css/modal.css" /> <link rel="stylesheet" type="text/css" href="/css/form.css" /> <link rel="stylesheet" type="text/css" href="/css/table.css" /> <link rel="stylesheet" type="text/css" href="/css/emoji.css" /> <script src="/js/data.js" type="text/javascript"></script> <script src="/js/loadAllData.js" type="text/javascript"></script> <script src="/js/getFullDataPath.js" type="text/javascript"></script> <script src="/js/getTextFromFile.js" type="text/javascript"></script> <script src="/js/trueTypeOf.js" type="text/javascript"></script> <script src="/js/uniq.js"></script> <script src="/js/processingIndicator.js"></script> <script src="/js/noNan.js"></script> <script src="/js/websiteFile.js"></script> <script src="/js/getPublis.js"></script> <script src="/js/DOICache.js"></script> <script src="/js/nestedCheckbox.js"></script> <script src="/js/selectSelectAll.js"></script> <script src="/js/numberRangeChange.js"></script> <script src="/js/DebugMode.js"></script> <script src="/js/numberUtils.js"></script> <script src="/js/PubliDB.js"></script> <script> function adjustSticky() { const height = $("nav").height() $("thead.sticky > tr > th ").css("top", height) } window.onload = async () => { window.browser = bowser.getParser(window.navigator.userAgent); $("input[type='checkbox']").trigger("change") $("input[type='number'].range").trigger("change") adjustSticky(); $(window).resize(adjustSticky) var slist = $("#SelectList") getAllSelect().each(function () { $("<li/>").text($('label[for="' + $(this).attr('id') + '"]').text()).appendTo(slist) }) $('[data-needbrowser],[data-neednotbrowser]').each(function () { function test(key, value) { switch (key) { case "Engine": return value.some((v) => browser.isEngine(v)) break; case "Browser": return value.some((v) => browser.isBrowser(v)) case "Platform": return value.some((v) => browser.isPlatform(v)) case "OS": return value.some((v) => browser.isOS(v)) default: return true break; } } const needdata = $(this).data("needbrowser") const neednotdata = $(this).data("neednotbrowser") const need = ((needdata == null) ? true : Object.entries(needdata).every(kv => test(kv[0], kv[1]))) const neednot = ((neednotdata == null) ? true : Object.entries(neednotdata).some(kv => !test(kv[0], kv[1]))) if (need && neednot) { $(this).show() } else { $(this).hide() } }) var cbextl = $("#cb_exTypeList") var extl = $("#exTypeList") var vertkindtl = $("#exVertKindList") for (const [name, value] of VertExcitationKinds.All) { $("<li/>").text(name).appendTo(vertkindtl) } for (const [name, value] of excitationTypes.All) { var txt = value.description.string if (value.description.isLaTeX) { txt = MathJaxUtils.getMathJaxString(txt) } $("<li/>").text(txt).appendTo(extl) var cbli = $("<li/>") if (!DebugMode.Enabled && value == (1 << 31)) { cbli.hide() } $("<input/>", { type: "checkbox", id: "cb_" + name, name: name, value: Number(value) }).change(nestedCheckbox_change).appendTo(cbli); $('<label />', { 'for': 'cb_' + name, text: txt }).appendTo(cbli); cbextl.append(cbli); } await MathJax.typesetPromise(); delete (cbextl) delete (extl) delete (vertkindtl) window.doiCache = new DOICache() await loadFiles() } async function loadFiles() { processingIndicator.isActive = true var chks = [] var data = await loadAllData() window.defaultDats = [] for (const sub of Object.values(data)) { for (const doi of uniq(sub.map(d => (d.DOI == null ? "" : d.DOI.string)))) { const subdoi = sub.filter(d => (d.DOI == null ? "" : d.DOI.string) === doi) for (mol of uniq(subdoi.map(d => d.molecule))) { const submol = subdoi.filter(d => d.molecule === mol) const source = submol.find((d) => { if ((d.DOI == null ? "" : d.DOI.string) === "10.1021/acs.jctc.8b01205") { return d.method.name === "CASPT2" && d.method.basis === "aug-cc-pVDZ" } else { return d.method.isTBE } }) if (source !== undefined) { for (const dat of submol.filter((d) => d !== source)) { dat.CopyExcitationsTypeFrom(source) } } } } window.defaultDats = window.defaultDats.concat(sub) } await doiCache.addRange(Array.from(new Set(window.defaultDats.filter(d => d.DOI !== null).map(d => d.DOI.string)))) window.defaultDats = window.defaultDats.sort((datfa, datfb) => { const DOIa = datfa.DOI const DOIb = datfb.DOI if (DOIa == null && DOIb == null) { return 0 } else if (DOIa == null) { return 1 } else if (DOIb == null) { return -1 } else { const puba = doiCache.get(DOIa.string).format('data', { format: 'object' })[0] const pubb = doiCache.get(DOIb.string).format('data', { format: 'object' })[0] return pubUtils.getIssuedDate(puba) - pubUtils.getIssuedDate(pubb) } }) processingIndicator.isActive = false reloadCustomFiles() } async function reloadCustomFiles() { window.dats = window.defaultDats; const kinds = new Map([["file_abs", VertExcitationKinds.Absorbtion], ["file_fluo", VertExcitationKinds.Fluorescence]]) for (const el of $('#form_dat > fieldset > fieldset > div > input[type="file"')) { if (kinds.has(el.name)) { for (const f of el.files) { const dat = await VertDataFile.loadAsync(f, kinds.get(this.name)) window.dats.push(dat) } } } reloadSelect("DOI") } async function clearSelect(BeforeName = null) { var selects = ((BeforeName == null) ? getAllSelect() : getAllSelectAfter(BeforeName, true)) selects.add($('#sel_ref')) selects.each(function () { $(this).empty() }) } function getAllSelect() { return $('#form_dat > fieldset > fieldset > div > select') } function getAllSelectAfter(Name, include = false) { const selects = getAllSelect() var index = selects.index($(`[name="${Name}"]`)) if (!include) { index++ } return selects.slice(index) } function getAllSelectBefore(Name, include = false) { const selects = getAllSelect() var index = selects.index($(`[name="${Name}"]`)) if (include) { index++ } return selects.slice(0, index) } async function reloadNextSelect(e) { const afters = getAllSelectAfter(e.target.name) const isLast = afters.length == 0 if (!isLast) { await reloadSelect(afters.prop("name")) } const fillArray = ["molecule"] if (fillArray.includes(afters.prop("name"))) { selectSelectAll(afters.first()) } } function getAllVals(select) { var baseval = $(select).val() switch (select.name) { case "DOI": vals=baseval.reduce((accumulator,currentValue)=>accumulator.concat(JSON.parse(currentValue)),[]) return vals break; default: return baseval } } async function reloadSelect(name) { clearSelect(name) var selects = getAllSelectBefore(name, true) var currentselect = selects.last() selects = selects.slice(0, selects.length - 1) var AllValsCache = new Map() selects.each(function() { AllValsCache.set(this.name,getAllVals(this)) }) var AAllValsCache = Array.from(AllValsCache) var vals = uniq(window.dats.filter(d => { return AAllValsCache.every(([k,v]) => { return v.indexOf(getSelectValue(d, k)) !== -1 }) }).map((d) => getSelectValue(d, name))) const publis = await (async () => { if (name === "DOI") { const publis = await publiDB.loadAsync() return publis } else { return undefined } })() if (name === "DOI") { let newvals = [] for (const val of vals) { for (const set of publis.sets.values()) { if (set.includes(val)) { newvals.push(set) } } } vals = newvals } textSelctor = function (value) { switch (name) { case "DOI": setname = publis.findNameFromSet(value, true) return setname === null ? value.toString() : setname break; case "molecule": const mhchemCE = /^\\ce\{(.*)\}$/ const m = value.match(mhchemCE) if (m) { return m[1] } else { return value } break; default: return value.toString() } } valueSelector = function (value) { if (typeof value === 'object') { return JSON.stringify(value) } else { return value } } for (const val of vals) { if (val !== null) { $("<option/>", { value: valueSelector(val) }).text(textSelctor(val)).appendTo(currentselect) } if (name === "molecule") { await MathJax.typesetPromise() } } } function getSelectValue(data, name) { switch (name) { case "method": return data.method.name break; case "basis": return data.method.basis case "DOI": return data.DOI === null ? "" : data.DOI.string default: return data[name] break; } } async function reloadStat() { processingIndicator.isActive = true var stb = $("#stat_table > tbody") $("#graph_div").empty() $(stb).empty() var refstr = $("#sel_ref option:selected").val() var sdatdic = new Map() for (const d of window.filtData) { const key = JSON.stringify(d.method) if (!(sdatdic.has(key))) { sdatdic.set(key, new Map()) } const myT1s = T1ref.get(d.DOI == null ? "" : d.DOI.string).get(d.molecule) for (const exc of d.excitations) { var allowT1 = false const T1Key = JSON.stringify((exc.initial, exc.final)) const T1range = filterParams.T1 if (T1range.min === 0 && T1range.max === 100) { allowT1 = true } else if (myT1s.has(T1Key)) { const T1Val = myT1s.get(T1Key) if (T1range.min <= T1Val && T1Val <= T1range.max) { allowT1 = true } } const key2 = JSON.stringify([d.molecule, exc.initial, exc.final, exc.cVertExcitationKind]) const keydic = sdatdic.get(key) if ((!exc.isUnsafe || window.filterParams.unsafe) && ((exc.type & window.filterParams.exType) !== 0) && ((exc.VertExcitationKind & window.filterParams.exVertKind) !== 0) && allowT1) { if (!(keydic.has(key2))) { keydic.set(key2, exc.value) } } } } var sdic = new Map() for (const [key, sdat] of sdatdic) { for (const [key2, exval] of sdat) { if (!(sdic.has(key))) { sdic.set(key, []) } sdic.get(key).push(exval - ((sdatdic.has(refstr)) ? sdatdic.get(refstr).get(key2) : NaN)) } } sdic.delete(refstr) var graphdat = [] for (const [keystr, vals] of sdic) { row = $("<tr/>") key = JSON.parse(keystr) Reflect.setPrototypeOf(key, method.prototype) th = $("<th/>", { scope: "column" }) const meth = key th.clone().text(meth.toString("\n").split('-').join('\u2011')).appendTo(row) const noNanVals = (vals.every((v) => Number.isNaN(v))) ? vals : (vals.filter((v) => !Number.isNaN(v))) const avals = noNanVals.map(v => Math.abs(v)) th.clone().text(noNanVals.length).appendTo(row) for (const val of [ss.min(noNanVals), ss.max(noNanVals), ss.mean(noNanVals), ss.mean(avals), ss.median(noNanVals), ss.median(avals), ss.rootMeanSquare(noNanVals), ss.variance(noNanVals), ss.standardDeviation(noNanVals)]) { $("<td/>").text(noNanFixed(val, 2)).appendTo(row) } $(stb).append(row) var box = { x: noNanVals, amean: ss.mean(avals).toFixed(3), name: key.toString() + " MAD : " + ss.mean(avals).toPrecision(4), type: 'box', boxmean: 'sd' }; graphdat.push(box) } var layout = { paper_bgcolor: 'rgba(0,0,0,0)', plot_bgcolor: 'rgba(0,0,0,0)', gap: 0, legend: { traceorder: 'reversed', }, xaxis: { title: { text: 'Energy (eV)', } }, bgcolor: '#E2E2E2', bordercolor: '#FFFFFF', borderwidth: 2, // autosize: false, width: 850, height: 500, margin: { l: 0, r: 10, b: 15, t: 20, pad: 0, }, } Plotly.newPlot('graph_div', graphdat, layout); processingIndicator.isActive = false } async function reloadContent() { window.filterParams = { unsafe: $("#cb_unsafe").prop("checked"), exType: Array.from(Array.from($("#cb_exTypeList>li>input[type=checkbox]:checked")).map(el => parseInt(el.value))).reduce((pv, cv) => pv + cv, 0), exVertKind: Array.from(Array.from($("#cb_exVertKindList>li>input[type=checkbox]:checked")).map(el => parseInt(el.value))).reduce((pv, cv) => pv + cv, 0), T1: { min: parseFloat($("#T1min").val()), max: parseFloat($("#T1max").val()) } } doiCache.clear() processingIndicator.isActive = true window.filtData = window.dats getAllSelect().each(function () { const prop = $(this).attr("name") const values = getAllVals(this) window.filtData = window.filtData.filter((d) => { if (typeof values == "undefined" || values == null) { return false } return values.includes(getSelectValue(d, prop)) delete (val) }) }) window.T1ref = new Map() var dois = new Set(window.filtData.map((d) => d.DOI === null ? "" : d.DOI.string)) var mols = new Set(window.filtData.map((d) => d.molecule)) await window.doiCache.addRange(dois) for (const doi of dois) { window.T1ref.set(doi, new Map()) for (const mol of mols) { window.T1ref.get(doi).set(mol, new Map()) var TBESortdat = window.dats.filter(d => (d.DOI === null ? "" : d.DOI.string) === doi && d.molecule === mol).sort((d1, d2) => { if (d1.method.isTBE && !d2.method.isTBE) { return -1 } else if (!d1.method.isTBE && d2.method.isTBE) { return 1 } else if (d1.method.isTBE && d2.method.isTBE) { if (d1.method.name === "TBE(FC)" && d2.method.name === "TBE") { return -1 } else if (d1.method.name === "TBE" && d2.method.name === "TBE(FC)") { return 1 } else { return 0 } } }) if (TBESortdat.length > 0 && TBESortdat[0].method.isTBE) { for (const exc of TBESortdat[0].excitations) { window.T1ref.get(doi).get(mol).set(JSON.stringify((exc.initial, exc.final)), exc.T1) } } } } $(sel_ref).empty() for (const el of uniq(window.filtData.map(d => [d.method, (d.DOI === null ? null : d.DOI.string)]))) { op = $("<option/>", { value: JSON.stringify(el) }).text(el[0]) if (el[0].name.includes("TBE")) { $(sel_ref).prepend(op) } else { $(sel_ref).append(op) } } $(sel_ref).prop("selectedIndex", 0); var data = $("#data") $(data).empty(); if (window.filtData.length > 0) { var publis = await publiDB.loadAsync() var PreviousSetName = undefined var CurrentsetName = undefined for (const doi of doiCache.keys()) { paperdata = window.filtData.filter((d) => { return (d.DOI === null ? "" : d.DOI.string) == doi }) var methods = uniq(paperdata.map(d => d.method)) const sortedMethods = methods.sort((a, b) => { if (a.isTBE && !b.isTBE) { return -1 } else if (a.isTBE && b.isTBE) { if (a.basis !== "CBS" && b.basis === "CBS") { return -1 } else { return 1 } } else if (!a.isTBE && b.isTBE) { return 1 } else { return 0 } }) var div = $('<div/>').appendTo(data) CurrentsetName = publis.findSetNameFromDOI(doi, true) if (PreviousSetName !== CurrentsetName) { $("<h1/>").text(CurrentsetName).appendTo(div) } const doiDat = doiCache.get(doi).format('data', { format: 'object' })[0] $("<h2/>").append($("<a>",{href:doiDat.URL,target:"_blank"}).text(doiDat.title)).appendTo(div) var table = $("<table/>").addClass("datatable").appendTo(div) var head = $("<tr/>") $("<thead/>").addClass("sticky").append(head).appendTo(table) var tbody = $("<tbody/>").appendTo(table) var th = $("<th/>", { scope: "column" }) head.append(["Molecule", "Transition"].map(x => th.clone().text(x))) head.append(sortedMethods.map(x => th.clone().text(x.toString("\n").split('-').join('\u2011')))) adjustSticky(); datadic = new Map() for (const dat of paperdata) { const key1 = dat.molecule; const myT1s = T1ref.get(dat.DOI == null ? "" : dat.DOI.string).get(dat.molecule) if (!datadic.has(key1)) { datadic.set(key1, new Map()) } const key3 = JSON.stringify(dat.method) for (const ex of dat.excitations) { Reflect.setPrototypeOf(ex.type, excitationType.prototype) var allowT1 = false const T1Key = JSON.stringify((ex.initial, ex.final)) const T1range = filterParams.T1 if (T1range.min === 0 && T1range.max === 100) { allowT1 = true } else if (myT1s.has(T1Key)) { const T1Val = myT1s.get(T1Key) if (T1range.min <= T1Val && T1Val <= T1range.max) { allowT1 = true } } if (((window.filterParams.exType & ex.type) !== 0) && ((window.filterParams.exVertKind & ex.VertExcitationKind) !== 0) && allowT1) { const key2 = JSON.stringify([ex.initial, ex.final, ex.type, ex.VertExcitationKind]) if (!datadic.get(key1).has(key2)) { datadic.get(key1).set(key2, new Map()) } datadic.get(key1).get(key2).set(key3, [ex.value, ex.isUnsafe]) } } } for (const [molecule, moldat] of datadic.entries()) { var printmol = true; const mhchemCE = /^\\ce\{.*\}$/ for (const [jsonex, exdat] of moldat.entries()) { const ex = JSON.parse(jsonex) Reflect.setPrototypeOf(ex[0], state.prototype) Reflect.setPrototypeOf(ex[1], state.prototype) Reflect.setPrototypeOf(ex[2], excitationType.prototype) Reflect.setPrototypeOf(ex[3], VertExcitationKind.prototype) var tr = $("<tr/>") if (printmol) { $("<th/>", { scope: "row", rowspan: moldat.size }).text((mhchemCE.test(molecule) ? MathJaxUtils.getMathJaxString(molecule.toString()) : molecule)).appendTo(tr) printmol = false } var Vertkindtext = "" if (ex[3].Value == VertExcitationKinds.Fluorescence.Value) { Vertkindtext = String.raw`[\mathrm{F}]` } desctex = "" if (ex[2].description.string) { desctex = "(" + ex[2].description.string + ")" } $("<th/>", { scope: "rowgroup" }).text(MathJaxUtils.getMathJaxString(String.raw`${ex[0].toLaTeX()} \rightarrow ${ex[1].toLaTeX()} ${Vertkindtext} ${desctex}`)).appendTo(tr) var entries = Array.from(exdat.entries()) for (const method of sortedMethods) { td = $("<td/>").addClass("NumberCell") kv = entries.find(x => { return JSON.stringify(method) == x[0] }) if (kv !== undefined) { const [val, unsafe] = kv[1] if (unsafe) { td.append($("<span/>", { title: "unsafe value", role: "img", "aria-label": "Warning" }).addClass("emoji").text('⚠')) } if (unsafe && !filterParams.unsafe) { td.append($("<s/>").append(val.toString())) } else { td.append(val.toString()) } } tr.append(td) } tbody.append(tr) } } PreviousSetName === CurrentsetName } await MathJax.typesetPromise() } await reloadRef() } async function reloadRef() { $(sel_ref).empty() for (const el of uniq(window.filtData.map(d => d.method))) { op = $("<option/>", { value: JSON.stringify(el) }).text(el) if (el.name.includes("TBE")) { $(sel_ref).prepend(op) } else { $(sel_ref).append(op) } } $(sel_ref).prop("selectedIndex", 0); $(sel_ref).trigger("change") } async function submitdat() { if (!DebugMode.Enabled) { window.onbeforeunload = () => { return ''; } window.onbeforeunload = () => { return ''; } window.onbeforeunload = () => { return ''; } } await reloadContent(); } </script> {{< getDataFilesName >}} <noscript> <p style="background-color: red; color: white; font-size: 20; font-weight: bold;"> <span role="img" aria-label="Warning" class="emoji">⚠</span> <span>This website work only if JavaScript is enable. You must enable JavaScript. <a href="https://www.enable-javascript.com/" target="_blank">How to enable JavaScript ?</a></span> </p> </noscript> <p> On this page you can compare multiple dataset and have some statistics to evaluate method accuracy (All fields marked with a red asterisk (<span style="color: red;">*</span>) are mandatory). </p> <form id="form_dat" action="javascript:submitdat()" method="post"> <fieldset class="main"> <fieldset> <legend>Import custom files</legend> <p> Import custom files to compare it's data to the reference datasets. </p> <div> <label for="absFiles_input">Import custom absorption data files</label> <input type="file" multiple onchange="reloadCustomFiles()" id="absFiles_input" name="file_abs"></input> </div> <div> <label for="fluoFiles_input">import custom fluorescence data files</label> <input type="file" multiple onchange="reloadCustomFiles()" id="fluoFiles_input" name="file_fluo"></input> </div> </fieldset> <fieldset class="table"> <legend>Data selection</legend> <p>Choose each parameter (of course you can select multiple values for each) you can use the <button disabled>Select all</button> button to select all items <span hidden data-needbrowser='{"Engine":["WebKit","Blink"],"Platform":["desktop"]}'>or use the <kbd data-needbrowser='{"OS":["MacOS"]}'>⌘</kbd><kbd data-neednotbrowser='{"OS":["MacOS"]}'>Ctrl</kbd>+<kbd>A</kbd> shortcut key</span> </p> <div style="display: inline-block;"> <label for="DOI_select" class="required">Sets</label> <button class="SelectAll" type="button" onclick="selectSelectAll_click(event)">Select all</button> <select multiple id="DOI_select" required name="DOI" onchange="reloadNextSelect(event)"></select> </div> <div style="display:inline-block"> <label for="mol_select" class="required">Molecules</label> <button class="SelectAll" type="button" onclick="selectSelectAll_click(event)">Select all</button> <select multiple id="mol_select" required name="molecule" onchange="reloadNextSelect(event)"></select> </div> <br /> <div style="display: inline-block;"> <label for="method_select" class="required">Methods</label> <button class="SelectAll" type="button" onclick="selectSelectAll_click(event)">Select all</button> <select multiple id="method_select" required name="method" onchange="reloadNextSelect(event)"></select> </div> <div style="display: inline-block;"> <label for="basis_select" class="required">Basis sets</label> <button class="SelectAll" type="button" onclick="selectSelectAll_click(event)">Select all</button> <select multiple id="basis_select" required name="basis" onchange="reloadNextSelect(event)"></select> </div> </fieldset> <fieldset> <legend class="required">Vertical excitation kind</legend> <p>Choose what kind of vertical excitations you want</p> <ul class="nestedCbList" style="padding-left: 0em;"> <li> <input type="checkbox" data-onerequired="true" checked onchange="nestedCheckbox_change(event)" id="cb_fileType_All"></input> <label for="cb_fileType_All"> All</label> </li> <ul class="nestedCbList" id="cb_exVertKindList"> <li> <input type="checkbox" onchange="nestedCheckbox_change(event)" id="cb_abs" value="1" name="datatype"> <label for="cb_abs">Absorption</label> </li> <li> <input type="checkbox" onchange="nestedCheckbox_change(event)" id="cb_fluo" value="2" name="datatype"> <label for="fluo">Fluorescence</label> </li> </ul> </ul> </fieldset> <fieldset id="excitationFilter"> <legend class="required">Filters</legend> <p>Choose other excitation parameters you want</p> <ul class="nestedCbList" style="padding-left: 0em;"> <li> <input type="checkbox" data-onerequired="true" onchange="nestedCheckbox_change(event)" id="cb_exType_All"></input> <label for="cb_exType_All"> All</label> </li> <ul id="cb_exTypeList" class="nestedCbList"> </ul> <p>Choose if you want to include unsafe values in statistics</p> <li id="li_cb_unsafe"> <input type="checkbox" id="cb_unsafe" name="unsafe"> <label for="cb_unsafe">Unsafe</label> </li> </fieldset> <fieldset> <legend>\(\mathrm{\%T_1}\)</legend> <p>Chose the range of \(\mathrm{\%T_1}\) range using the two number box</p> <input class="range min" airia-label="Minimum value" type="number" id="T1min" onchange="numberRangeChange(event)" min="0" value="0"> <span>\(\leq \mathrm{\%T_1} \leq\)</span> <input class="range max" airia-label="Maximum value" type="number" id="T1max" onchange="numberRangeChange(event)" max="100" value="100"> </fieldset> </fieldset> <input type="submit" value="Load"></input> </form> <form id="form_ref"> <fieldset class="main"> <fieldset> <legend>statistics</legend> <p>Select a reference from <strong>already selected data</strong> (by default first is selected -it's the <abbr title="Theoretical best estimate">TBE</abbr> if present- is already selected)</p> <label for="sel_ref">Reference</label> <select id="sel_ref" onchange="reloadStat()"></select> </fieldset> </fieldset> </form> <p>Now you can see the list of selected data and some statistics about these data</p> <section id="data"> </section> <section> <table id="stat_table" class="datatable"> <thead class="sticky"> <th scope="col">Method</th> <th scope="col">Count</th> <th scope="col">Min</th> <th scope="col">Max</th> <th scope="col"><abbr title="Mean signed error">MSE</abbr></th> <th scope="col"><abbr title="Mean absolute error">MAE</abbr></th> <th scope="col">Median</th> <th scope="col">Absolute Median</th> <th scope="col"><abbr title="Root-mean square error ">RMSE</abbr></th> <th scope="col">Variance</th> <th scope="col"><abbr title="Standard deviation of the errors">SDE</abbr></th> </thead> <tbody> </tbody> <div id="graph_div"></div> </table> </section> {{< waitModal >}}