Line 26: | Line 26: | ||
var decimal_seperator = params.decimal_seperator.value; | var decimal_seperator = params.decimal_seperator.value; | ||
+ | if (value_seperator === 't') value_seperator = '\t'; | ||
Message("Validating format..."); | Message("Validating format..."); | ||
+ | console.log("value seperator" + value_seperator + "."); | ||
// validation and manipulation | // validation and manipulation | ||
Line 36: | Line 38: | ||
Warning("Your thousands seperator is the same as the value seperator."); | Warning("Your thousands seperator is the same as the value seperator."); | ||
} | } | ||
− | data = | + | data = replaceAll(data, thousands_seperator, ''); |
} | } | ||
if (value_seperator == decimal_seperator) { | if (value_seperator == decimal_seperator) { | ||
Line 43: | Line 45: | ||
// turn value seperator into ',' and decimal seperator into '.', and preventing those to be mixed up. | // turn value seperator into ',' and decimal seperator into '.', and preventing those to be mixed up. | ||
if (data.indexOf('¬') === -1) { | if (data.indexOf('¬') === -1) { | ||
− | + | dataWithStrange = replaceAll(data, value_seperator, '¬'); | |
+ | dataDecimalToPoint = replaceAll(dataWithStrange, decimal_seperator, '.'); | ||
+ | data = replaceAll(dataDecimalToPoint, '¬', ','); | ||
} else { | } else { | ||
return Error("¬ cannot be used as delimiter."); | return Error("¬ cannot be used as delimiter."); | ||
} | } | ||
− | data = | + | data = replaceAll(data, "\"", ""); |
Message("Parsing..."); | Message("Parsing..."); | ||
Line 54: | Line 58: | ||
Message("Validating data...") | Message("Validating data...") | ||
− | aa[0] = aa[0].filter( | + | aa[0] = aa[0].filter(filterExceptZero); |
− | aa[1] = aa[1].filter( | + | aa[1] = aa[1].filter(filterExceptZero); |
− | + | ||
if (aa.length > 2) { | if (aa.length > 2) { | ||
aa = numeric.transpose(aa); | aa = numeric.transpose(aa); | ||
Line 62: | Line 65: | ||
if (aa.length !== 2) { | if (aa.length !== 2) { | ||
− | return Error("Enter data with 2 | + | return Error("Enter data with 2 columns or rows."); |
} | } | ||
if (aa[0].length !== aa[1].length) { | if (aa[0].length !== aa[1].length) { | ||
− | Warning("Your two | + | console.log(aa[0]); |
− | var min = Math.min(aa[0].length | + | console.log(aa[1]); |
+ | Warning("Your two columns or rows are of different length. The longest one is truncated to match the other."); | ||
+ | var min = Math.min(aa[0].length, aa[1].length); | ||
+ | console.log(min); | ||
if (aa[0].length == min) { | if (aa[0].length == min) { | ||
aa[1] = aa[1].slice(0, min); | aa[1] = aa[1].slice(0, min); | ||
Line 74: | Line 80: | ||
} | } | ||
} | } | ||
− | + | aa = [aa[0], aa[1]]; | |
+ | console.log(aa[0]); | ||
+ | console.log(aa[1]) | ||
+ | console.log(aa); | ||
aa = numeric.transpose(aa); | aa = numeric.transpose(aa); | ||
DATA = aa; | DATA = aa; | ||
Line 99: | Line 108: | ||
var initial = [N[0], N[N.length - 1], 0.005]; | var initial = [N[0], N[N.length - 1], 0.005]; | ||
var objective = makeObjective(logistic, t, N); | var objective = makeObjective(logistic, t, N); | ||
− | |||
− | |||
try { | try { | ||
var minimiser = numeric.uncmin(objective, initial); | var minimiser = numeric.uncmin(objective, initial); | ||
− | } | + | } catch (err) { |
− | + | ||
return Error(err) | return Error(err) | ||
} | } | ||
Line 198: | Line 204: | ||
total += delta * delta; | total += delta * delta; | ||
} | } | ||
− | |||
return total; | return total; | ||
} | } | ||
Line 251: | Line 256: | ||
text: e | text: e | ||
})); | })); | ||
+ | } | ||
+ | // This functions takes a string and adds \ before any character that needs to be escaped in a regex. | ||
+ | // This is necesarry when putting user input into a regex. | ||
+ | function escapeRegExp(string) { | ||
+ | return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string | ||
+ | } | ||
+ | // This functions replaces all matched characters and also does this safe for user input | ||
+ | function replaceAll(str, find, replace) { | ||
+ | return str.replace(new RegExp(escapeRegExp(find), 'g'), replace); | ||
+ | } | ||
+ | // This function returns false when NaN, false, and undefined, but returns true for 0 | ||
+ | function filterExceptZero(el) { | ||
+ | if (el === 0) return true; | ||
+ | else return Boolean(el); | ||
} | } | ||
} | } |
Revision as of 16:56, 18 August 2017
function plot(params) {
$("main.container").addClass("progress"); progress(params, plot); //Globals: var DATA;
// ################ // The rest defines the functions: // ################
// Gets data from form and validates. function progress(params, plot) { // Print an error
// Empty alert box and results box
$('#alert').html(""); $('#results').html(""); Message("Processing...");
// Getting all the data from the form. var data = params.data.value.trim(); var thousands_seperator = params.thousands_seperator.value; var thousands_checked = params.thousands_usage.checked; var value_seperator = params.value_seperator.value; var decimal_seperator = params.decimal_seperator.value;
if (value_seperator === 't') value_seperator = '\t'; Message("Validating format..."); console.log("value seperator" + value_seperator + ".");
// validation and manipulation if (thousands_checked) { if (thousands_seperator == value_seperator) { Warning("Your thousands seperator is the same as the value seperator."); } if (thousands_seperator == decimal_seperator) { Warning("Your thousands seperator is the same as the value seperator."); } data = replaceAll(data, thousands_seperator, ); } if (value_seperator == decimal_seperator) { return Error("Your value seperator is the same as the decimal seperator."); } // turn value seperator into ',' and decimal seperator into '.', and preventing those to be mixed up. if (data.indexOf('¬') === -1) { dataWithStrange = replaceAll(data, value_seperator, '¬'); dataDecimalToPoint = replaceAll(dataWithStrange, decimal_seperator, '.'); data = replaceAll(dataDecimalToPoint, '¬', ','); } else { return Error("¬ cannot be used as delimiter."); }
data = replaceAll(data, "\"", "");
Message("Parsing..."); aa = numeric.parseCSV(data);
Message("Validating data...") aa[0] = aa[0].filter(filterExceptZero); aa[1] = aa[1].filter(filterExceptZero); if (aa.length > 2) { aa = numeric.transpose(aa); }
if (aa.length !== 2) { return Error("Enter data with 2 columns or rows."); }
if (aa[0].length !== aa[1].length) { console.log(aa[0]); console.log(aa[1]); Warning("Your two columns or rows are of different length. The longest one is truncated to match the other."); var min = Math.min(aa[0].length, aa[1].length); console.log(min); if (aa[0].length == min) { aa[1] = aa[1].slice(0, min); } else { aa[0] = aa[0].slice(0, min); } } aa = [aa[0], aa[1]]; console.log(aa[0]); console.log(aa[1]) console.log(aa); aa = numeric.transpose(aa); DATA = aa;
Message("Plotting..."); plot(aa, fit);
}
// Plots the data function plot(data, fit) { result("Your data has size " + data.length + "."); $.plot($("#placeholder"), [{ color: 'yellow', data: data }]); fit(data, plotfit); }
function fit(data, plotfit) { data = numeric.transpose(data); var t = data[0]; var N = data[1]; var initial = [N[0], N[N.length - 1], 0.005]; var objective = makeObjective(logistic, t, N); try { var minimiser = numeric.uncmin(objective, initial); } catch (err) { return Error(err) } result("Solution found after " + minimiser.iterations + " iterations."); result(minimiser.message); draw(minimiser.solution); var t = numeric.linspace(t[0], t[t.length - 1], t.length); var N = vectorize( setParamInFunc(minimiser.solution, logistic), t ); // var N = (func, x) - > { // var res = Array(x.length); // for (var i = 0; i < x.length; i++) { // res[i] = func(x[i]); // } // return res; // }((func, params) - > { // return (x) - > // return func(params, x); // }, t); var datafit = [t, N]; datafit = numeric.transpose(datafit); data = numeric.transpose(data); plotfit(datafit, data, callback); }
function plotfit(datafit, data, callback) { $.plot($("#placeholder"), [{ label: "fitted data", data: datafit, color: 'green' }, { label: "input data", data: data, color: 'yellow' }], { legend: { position: "nw" } }); callback(); }
// Tells user we are done. function callback() { $("main.container").removeClass("progress"); }
// ################ // The following functions are out of the callback work loop // ################
// Decorator for functions to perform vectorized function vectorize(func, x) { var res = new Array(x.length); testtest = func; for (var i = 0; i < x.length; i++) { res[i] = func(x[i]); } return res; }
// Decorator for functions to set params fixed function setParamInFunc(params, func) { function resFunc(x) { return func(params, x); } return resFunc; }
// Function to fit // N = logistic(params,t); function logistic(params, t) { var N0, K, r; [N0, K, r] = params; var ans = (K * N0 * Math.exp(r * t)) / (K + N0 * (Math.exp(r * t) - 1)); var below = (K + N0 * (Math.exp(r * t) - 1)); if (below === Infinity) { ans = 0; } return ans; }
// Least square objective // Build as factory with a decorator function makeObjective(targetFunc, x, y) { function objective(params) { var total = 0.0 for (var i = 0; i < y.length; i++) { var result = targetFunc(params, x[i]) var delta = result - y[i]; total += delta * delta; } return total; } return objective; }
function draw(params) { param_names = ["N0", "K", "r"]; for (var i = 0; i < param_names.length; i++) { result(param_names[i] + " is " + params[i]); } var latex = katex.renderToString("N(t) = \\frac{K \\cdot N_0 \\cdot e^{rt}}{K + N_0 \\cdot (e^{rt}-1)}"); result("For the function:") resultHTML(latex); }
function result(e) { $("#results").append($("<p />", { class: "result", text: e })); }
function resultHTML(e) { $("#results").append($("<p />", { class: "result", html: e })); }
// Print an error (This function is usually returned.) function Error(e) { $("#alert").prepend($('<p />', { class: 'error', text: e })); $("main.container").removeClass("progress"); }
// Print a warming function Warning(e) { $("#alert").append($('<p />', { class: 'warning', text: e })); }
// Print a message function Message(e) { $("#alert").append($('<p />', { class: 'message', text: e })); } // This functions takes a string and adds \ before any character that needs to be escaped in a regex. // This is necesarry when putting user input into a regex. function escapeRegExp(string) { return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string } // This functions replaces all matched characters and also does this safe for user input function replaceAll(str, find, replace) { return str.replace(new RegExp(escapeRegExp(find), 'g'), replace); } // This function returns false when NaN, false, and undefined, but returns true for 0 function filterExceptZero(el) { if (el === 0) return true; else return Boolean(el); }
}