tag:blogger.com,1999:blog-84413090138581710512024-03-29T04:21:25.437-07:00SciStatCalcAlijah Ahmedhttp://www.blogger.com/profile/01151589526613233918noreply@blogger.comBlogger91125tag:blogger.com,1999:blog-8441309013858171051.post-46090992368775352021-07-21T09:46:00.009-07:002023-12-09T07:24:59.608-08:00Logistic Regression Calculator and ROC Curve Plotter<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-csv/0.8.3/jquery.csv.min.js"></script>
<script>
$(document).ready(function() {
if(isAPIAvailable()) {
$('#files').bind('change', handleFileSelect);
}
});
function isAPIAvailable() {
// Check for the various File API support.
if (window.File && window.FileReader && window.FileList && window.Blob) {
// Great success! All the File APIs are supported.
return true;
} else {
// source: File API availability - http://caniuse.com/#feat=fileapi
// source: <output> availability - http://html5doctor.com/the-output-element/
document.writeln('The HTML5 APIs used in this form are only available in the following browsers:<br />');
// 6.0 File API & 13.0 <output>
document.writeln(' - Google Chrome: 13.0 or later<br />');
// 3.6 File API & 6.0 <output>
document.writeln(' - Mozilla Firefox: 6.0 or later<br />');
// 10.0 File API & 10.0 <output>
document.writeln(' - Internet Explorer: Not supported (partial support expected in 10.0)<br />');
// ? File API & 5.1 <output>
document.writeln(' - Safari: Not supported<br />');
// ? File API & 9.2 <output>
document.writeln(' - Opera: Not supported');
return false;
}
}
function handleFileSelect(evt) {
var files = evt.target.files; // FileList object
var file = files[0];
// read the file metadata
var output = ''
output += '<span style="font-weight:bold;">' + escape(file.name) + '</span><br />\n';
output += ' - FileType: ' + (file.type || 'n/a') + '<br />\n';
output += ' - FileSize: ' + file.size + ' bytes<br />\n';
output += ' - LastModified: ' + (file.lastModifiedDate ? file.lastModifiedDate.toLocaleDateString() : 'n/a') + '<br />\n';
// read the file contents
printTextArea(file);
// post the results
$('#list').append(output);
}
function printTextArea(file) {
var reader = new FileReader();
reader.readAsText(file);
reader.onload = function(event){
var csv = event.target.result;
var data = $.csv.toArrays(csv);
var y_str = ""
var x_str = "";
for(var row in data) {
y_str += data[row][0]
for (ii=1; ii < data[row].length; ii++) {
x_str += data[row][ii].toString()
if (ii < data[row].length-1) {
x_str += ","
}
}
x_str += "\n"
y_str += "\n"
}
x_str = x_str.substring(0, x_str.length - 1)
y_str = y_str.substring(0, y_str.length - 1)
document.getElementById("y_vec").value = y_str;
document.getElementById("x_mat").value = x_str;
};
reader.onerror = function(){ alert('Unable to read ' + file.fileName); };
}
</script>
<!--[if IE ]>
<script type="text/javascript" src="https://firebasestorage.googleapis.com/v0/b/js-static-assets.appspot.com/o/excanvas_min.js?alt=media&token=9dd78ad2-f09b-45dd-8a09-8db2b2546d1d"></script>
<![endif]-->
<script type="text/javascript" src="https://firebasestorage.googleapis.com/v0/b/js-static-assets.appspot.com/o/jquery_flot.js?alt=media&token=bde69a50-3df9-4794-b7d8-dd4e840472cd"></script>
<script type="text/javascript" id="MathJax-script" async
src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js">
</script>
<body>
This blog post implements a Logistic Regression calculator for a binary output.
<br>
<p>Consider a binary outcome response variable \(Y\in\{0,1\}\) and let \(p\) be the probability that \(Y\) is \(1\), i.e. \(p=P(Y=1)\).
<br>
The logistic model is formally given by:-
<br>
\(\log(\frac{p}{1-p})=\beta_0+\beta_1x_1+\beta_2x_2+\cdots+\beta_px_p\)
</p>
The calculator estimates the value of model parameters \(\beta_0, \beta_1,..,\beta_p\) given samples of response variables \(Y^{(n)}\) and the corresponding predictor variables \(x_1^{(n)}, x_2^{(n)},...,x_p^{(n)}\), where \(n\) is the sample index. The Newton-Raphson algorithm is used to maximise the log likelihood function with respect to the model parameters - it is possible to specify the number of iterations to run in the textfield below (a default of 10 is given).
<br>
<br>
In the two texareas below, the first (narrow) one on the left has the response variable \(Y\), while the second one has the corresponding comma separated predictor variable entries \(x_k\). Example values have been entered (2000 samples), which can be altered as appropriate. In addition, it is possible to load a CSV format file by clicking on the "Choose File" button - the first column has to be the response variable (taking either 1 or 0 as a value), while column 2 onwards are the real valued predictor variables.
<br>
<p> To run the algorithm once the values have been entered in the textareas, simply click on the "Estimate model parameters" button. A plot indicating algorithm convergence will be updated (showing the increase of the log likelihood function), and could be useful in specifying the number of iterations needed for the Newton-Raphson algorithm. Finally, the Receiver Operating Characteristic (ROC) plot will be generated. For the ROC, a black line of gradient 1 will be generated as reference.
</body>
<br>
<br>
<div id=inputs class=clearfix>
<input type=file id=files name=files[] single />
</div>
</html>
<br>
<br>
<input name="b1" onclick="clear_y()" style="margin-left: 0px;" type="Button" value="Clear Y" />
<input name="b1" onclick="clear_x()" style="margin-left: 20px;" type="Button" value="Clear x" />
<br>
<textarea rows="20" cols="5" style="font-size:12pt" id ="y_vec">
</textarea>
<textarea rows="20" cols="35" style="font-size:12pt" id ="x_mat">
</textarea>
<form name="frmOne">
<br>
Enter number of iterations:-<br>
<input name="txtIterNumber" size="30" style="font-size:12pt" type="TextArea" value="" />
</form>
<br>
<br>
<input name="b1" onclick="calc()" style="margin-left: 0px;" type="Button" value="Estimate model parameters" />
<br>
<br>
<p id="p1">Results pending...</p>
<br>
<br>
<table style="text-align: center;">
<tbody>
<tr>
<td></td>
<td>Algorithm convergence</td>
<td></td>
</tr>
<tr>
<td></td>
<td><div id='chart1' style='height:150px;width:500px;'>
</div>
</td>
<td></td>
</tr>
<tr>
<td></td>
<td>Iteration number</td>
<td></td>
</tr>
</tbody></table>
<br>
<table style="text-align: center;">
<tbody>
<tr>
<td></td>
<td>Receiver Operating Characteristic</td>
<td></td>
</tr>
<tr>
<td></td>
<td><div id='chart2' style='height:300px;width:500px;'>
</div>
</td>
<td>Sensitivity</td>
</tr>
<tr>
<td></td>
<td>1 - Specificity</td>
<td></td>
</tr>
</tbody></table>
<script language="JavaScript">
var iter_data = new Array();
for (var idx = 0; idx < 10; idx++)
iter_data[idx] = [idx, 0.0];
var ser1 = {
label: "",
data: iter_data,
color: "#ffffff",
points: { show: false },
lines: { show: true },
bars: { show: false},
yaxis: 1
};
var data = [ ser1];
$.plot($("#chart1"),data);
$.plot($("#chart2"),data);
window.onload = set_inputs();
function randn_bm() {
var u = 0, v = 0;
while(u === 0) u = Math.random(); //Converting [0,1) to (0,1)
while(v === 0) v = Math.random();
return Math.sqrt( -2.0 * Math.log( u ) ) * Math.cos( 2.0 * Math.PI * v );
}
function set_inputs() {
var N = 2000
var p = 2
var X_mat = create_const_matrix(N,p,0)
var X_mat_p1 = create_const_matrix(N,p+1,0)
var beta_vec = create_const_matrix(p+1,1,0)
var expXb = create_const_matrix(N,1,0)
for (var aa=0; aa < N; aa++) {
X_mat_p1[aa][0] = 1.0
for (var bb=0; bb < p; bb++) {
X_mat[aa][bb] = randn_bm()
X_mat_p1[aa][bb+1] = X_mat[aa][bb]
}
}
for (var cc=0; cc < p+1; cc++) {
beta_vec[cc][0] = randn_bm()
}
Xb = mult_matrices(X_mat_p1, beta_vec)
console.log("Beta golden ", beta_vec)
for (var dd=0; dd < N; dd++) {
expXb[dd][0] = 1.0/(1.0 + Math.exp(-1.0 * Xb[dd][0]))
}
var Y_vec = create_const_matrix(N,1,0)
for (var nn=0; nn < N; nn++) {
var u = Math.random()
if (u < expXb[nn][0]) {
Y_vec[nn][0] = 1
}
}
var y_vec_new = ""
for (var nn=0; nn < N; nn++) {
y_vec_new += Y_vec[nn][0].toFixed(0)
if (nn < N-1) {
y_vec_new += "\n"
}
}
var x_mat_new = ""
for (var mm=0; mm < N; mm++) {
for (var pp=0; pp < p; pp++) {
x_mat_new += X_mat[mm][pp].toFixed(4)
if (pp < p-1) {
x_mat_new += ","
}
}
if (mm < N-1) {
x_mat_new += "\n"
}
}
var y_vec = 1 + "\n" + 1 + "\n" + 0 + "\n" + 1
var x_mat = "1.3315865,0.7152789" + "\n" + " -1.54540029,-0.00838385" + "\n" + "0.62133597,-0.72008556" + "\n" + " 0.26551159,0.10854853"
// document.getElementById("y_vec").value = y_vec
// document.getElementById("x_mat").value = x_mat
document.getElementById("y_vec").value = y_vec_new
document.getElementById("x_mat").value = x_mat_new
document.frmOne.txtIterNumber.value = "10"
}
function clear_y() {
document.getElementById("y_vec").value = ""
}
function clear_x() {
document.getElementById("x_mat").value = ""
}
function calc() {
var y_str = document.getElementById("y_vec").value
var x_str = document.getElementById("x_mat").value
y_str_arr = y_str.split("\n")
resp_valid = true
var y_vec = new Array()
for (idx = 0; idx < y_str_arr.length; idx++) {
y_vec.push(Number(y_str_arr[idx]))
if (Number(y_str_arr[idx]) != 0 && Number(y_str_arr[idx]) != 1) {
resp_valid = false
}
}
if (resp_valid == false) {
alert("Response variable Y must be either 1 or 0")
}
console.log(y_vec)
var N = y_vec.length
console.log("N:", N)
// Get the X matrix as part of validation
v_m_r = validate_matrix(x_str)
if (v_m_r[0] == 0) {
alert("All rows should have the same number of entries!")
}
x_mat = v_m_r[2]
console.log(x_mat)
var p = x_mat[0].length+1
console.log("p:", p)
X = prepend_with_ones(x_mat)
console.log("X:", X)
Y = create_const_matrix(N,1,0)
for (ii=0;ii < N; ii++) {
Y[ii][0] = y_vec[ii]
}
var Niter = Number(document.frmOne.txtIterNumber.value);
if (Y.length != X.length) {
alert("Y and X must have the same number of rows (check for blank line at the bottom)!")
}
calc_beta_log_reg(Y,X,Niter)
}
function create_const_matrix(m,n,val)
{
var M_res = new Array
for (i=0; i < m; i++)
{
var vec = new Array
for (j=0; j < n;j++)
{
vec.push(val)
}
M_res.push(vec)
}
return M_res
}
function transpose_matrix(M)
{
var nr = M.length
var nc = M[0].length
var Mres = create_const_matrix(nc,nr,0);
for (i=0; i < nc; i++)
{
for (j=0; j < nr;j++)
{
Mres[i][j]=M[j][i]
}
}
return Mres
}
function add_matrices(A,B) {
var C = new Array()
var nr = A.length
var nc = A[0].length
var nrb = B.length
var ncb = B[0].length
C = create_const_matrix(nr,nc,0)
var res_valid = true
if (nr != nrb || nc != ncb) {
res_valid = false
}
for (ii=0; ii<nr; ii++) {
for (jj=0; jj<nc; jj++) {
C[ii][jj] = A[ii][jj] + B[ii][jj]
}
}
return C
}
function subtr_matrices(A,B) {
var C = new Array()
var nr = A.length
var nc = A[0].length
var nrb = B.length
var ncb = B[0].length
C = create_const_matrix(nr,nc,0)
var res_valid = true
if (nr != nrb|| nc != ncb) {
res_valid = false
}
for (ii=0; ii<nr; ii++) {
for (jj=0; jj<nc; jj++) {
C[ii][jj] = A[ii][jj] - B[ii][jj]
}
}
return C
}
function exp_matrix(A) {
var C = new Array()
var nr = A.length
var nc = A[0].length
C = create_const_matrix(nr,nc,0)
for (ii=0; ii<nr; ii++) {
for (jj=0; jj<nc; jj++) {
C[ii][jj] = Math.exp(A[ii][jj])
}
}
return C
}
function exp_over_exp_p_1(A) {
var C = new Array()
var nr = A.length
var nc = A[0].length
C = create_const_matrix(nr,nc,0)
for (ii=0; ii<nr; ii++) {
for (jj=0; jj<nc; jj++) {
var tmpres = Math.exp(A[ii][jj])
C[ii][jj] = tmpres/(1.0 + tmpres)
}
}
return C
}
function get_max_idx(V,offs) {
var idx = 0
var maxVal = 0
for (k=0; k < V.length; k++) {
if (k == 0) {
maxVal = V[k]
} else {
if (Math.abs(V[k])>Math.abs(maxVal)) {
maxVal = V[k]
idx = k
}
}
}
return idx+offs
}
function swap_rows(Minp,i,j) {
var M_res = Minp
var nr = Minp.length
var nc = Minp[0].length
var tmpVal = 0
var k = 0
for (k=0; k < nc; k++) {
tmpVal = M_res[i][k]
M_res[i][k] = M_res[j][k]
M_res[j][k] = tmpVal
}
return M_res
}
function lu_factorisation_partial_pivot(Mx) {
var nr = Mx.length
var U = create_const_matrix(nr,nr,0)
for (xx=0; xx < nr; xx++) {
for (yy=0; yy < nr; yy++) {
U[xx][yy] = Mx[xx][yy]
}
}
var nr = U.length
var L = create_const_matrix(nr,nr,0);
var P = create_const_matrix(nr,nr,0);
var num_swaps = 0
var k = 0
for (k=0; k < nr; k++) {
L[k][k] = 1.0
P[k][k] = 1.0
}
for (ii=0; ii< nr-1; ii++) {
var vec = new Array()
for (jj=ii; jj < nr; jj++) {
vec.push(U[jj][ii])
}
var elem = 0.0
var idx = get_max_idx(vec,ii)
var el_tmp = 0.0
for (jj =ii; jj < nr; jj++ ) {
el_tmp = U[ii][jj]
U[ii][jj] = U[idx][jj]
U[idx][jj] = el_tmp
}
for (jj =0; jj < ii; jj++ ) {
el_tmp = L[ii][jj]
L[ii][jj] = L[idx][jj]
L[idx][jj] = el_tmp
}
if (ii != idx) {
num_swaps += 1
}
Pnew = swap_rows(P,ii,idx)
P = Pnew
for (jj = ii + 1; jj < nr; jj++) {
if (Math.abs(U[jj][ii])<1e-16) {
L[jj][ii] = 0.0
} else {
L[jj][ii] = U[jj][ii]/U[ii][ii]
}
for (xx=ii;xx < nr; xx++) {
U[jj][xx] = U[jj][xx] - (L[jj][ii] * U[ii][xx])
}
}
}
var det_res = 1.0
for (vv=0; vv < nr; vv++) {
det_res = det_res * (U[vv][vv] * L[vv][vv])
}
det_res = det_res * ((-1)**num_swaps)
return [U,L,P,num_swaps,det_res]
}
function mult_matrices(A,B) {
var nra = A.length
var nca = A[0].length
var nrb = B.length
var ncb = B[0].length
var valid_result = true
if (nca != nrb) {
// alert("Cannot multiply these matrics!")
valid_result = false
}
var C = new Array()
C = create_const_matrix(nra,ncb,0)
for (aa = 0; aa < nra; aa++) {
for (bb = 0; bb < ncb; bb++) {
var tmp_res = 0.0
for (cc = 0; cc < nca; cc++) {
tmp_res = tmp_res + (A[aa][cc] * B[cc][bb])
}
C[aa][bb] = tmp_res
}
}
return C
}
function back_subst(U,d) {
var nr = d.length
var x = new Array()
x = create_const_matrix(nr,1,0)
x[nr - 1][0] = d[nr-1][0]/(U[nr-1][nr-1]);
for (i=nr-2; i >= 0; i--) {
var sum_ux = 0.0;
for (j=i+1; j < nr; j++) {
sum_ux += (U[i][j]*x[j][0]);
}
x[i][0] = (1/U[i][i]) * (d[i][0] - sum_ux);
}
return x;
}
function fwd_subst(L,b) {
var d = new Array()
var nr = b.length
d = create_const_matrix(nr,1,0)
d[0][0] = b[0][0]
for (i=1; i < nr; i++) {
var sum_ld = 0.0;
for (j=0; j < i; j++) {
sum_ld += (L[i][j] * d[j][0]);
}
d[i][0] = b[i][0] - sum_ld;
}
return d;
}
function invert_matrix(Minp) {
var res_lu = lu_factorisation_partial_pivot(Minp)
var U = res_lu[0]
var L = res_lu[1]
var P = res_lu[2]
var num_swaps = res_lu[3]
var det_res = res_lu[4]
var dim = U.length
console.log("U is ", U)
var inv_res = new Array()
inv_res = create_const_matrix(dim,dim,0)
var C_inv = new Array()
for (kk=0; kk < dim; kk++) {
var ei = new Array()
ei = create_const_matrix(dim,1,0);
ei[kk][0] = 1.0;
var ri = new Array()
ri = create_const_matrix(dim,1,0);
ri = back_subst(U,fwd_subst(L,ei));
for (mm=0; mm < dim; mm++) {
inv_res[mm][kk] = ri[mm][0];
}
}
C_inv = mult_matrices(inv_res,P)
return C_inv
}
function calc_norm(col_vec) {
var nr = col_vec.length
var res_acc = 0.0
for (mm = 0; mm < nr; mm++) {
res_acc += (col_vec[mm][0] * col_vec[mm][0])
}
return Math.sqrt(res_acc)
}
function calc_log_lf(Y, X_beta_vec, expbv) {
nr = expbv.length
var lg_term = create_const_matrix(nr,1,0)
var Y_X_beta_vec = create_const_matrix(nr,1,0)
for (ii=0; ii < nr; ii++) {
lg_term[ii][0] = Math.log(1.0 + expbv[ii][0])
Y_X_beta_vec[ii][0] = Y[ii][0] * X_beta_vec[ii][0]
}
console.log("lg_term ", lg_term)
var Mres = subtr_matrices(Y_X_beta_vec, lg_term)
var res = 0.0
for (ii=0; ii < nr; ii++) {
res += Mres[ii][0]
}
return res
}
function calc_dLdb(Y, X, p_x_b, pp1) {
var dLdb = create_const_matrix(pp1, 1, 0)
var N = Y.length
var accum = 0.0
for (ii=0; ii < pp1; ii++) {
accum = 0.0
for (jj=0; jj < N; jj++) {
accum += ((Y[jj][0] - p_x_b[jj][0]) * X[jj][ii])
}
dLdb[ii][0] = accum
}
return dLdb
}
function calc_d2Ldb2(X,p_x_b,pp1) {
var N = X.length
var d2Ldb2 = create_const_matrix(pp1, pp1, 0)
var accum = 0.0
for (row=0; row < pp1; row++) {
for (col=0; col < pp1; col++) {
accum = 0.0
for (elem=0; elem < N; elem++) {
acc_delta = -1 * (X[elem][row] * p_x_b[elem][0]) * (1.0-p_x_b[elem][0]) * X[elem][col]
accum += acc_delta
}
d2Ldb2[row][col] = accum
}
}
return d2Ldb2
}
function gen_roc_data_point(Y, prob_vec, cutoff) {
Y_est = create_const_matrix(Y.length,1,0)
var pos_meas = 0
var neg_meas = 0
for (var aa=0; aa < Y.length; aa++) {
if (prob_vec[aa] > cutoff) {
Y_est[aa][0] = 1
}
if (Y[aa][0] == 1) {
pos_meas += 1
} else if (Y[aa][0] == 0) {
neg_meas += 1
}
}
var pos_true = 0
var neg_true = 0
for (var aa=0; aa < Y.length; aa++) {
if (Y_est[aa][0] == Y[aa][0]) {
if (Y[aa][0] == 1) {
pos_true += 1
} else if (Y[aa][0] == 0) {
neg_true += 1
}
}
}
var sens = 0.0
var spec = 0.0
if (pos_meas > 1e-16) {
sens = (pos_true + 0.0)/(pos_meas + 0.0)
}
if (neg_meas > 1e-16) {
spec = (neg_true + 0.0)/(neg_meas + 0.0)
}
console.log(sens, spec, cutoff)
return [sens, spec]
}
function calc_beta_log_reg(Y,X,Niter) {
var N = Y.length
var nr = X.length
var nc = X[0].length
console.log("The main algorithm...")
console.log("Y",Y)
console.log("X",X)
console.log("Niter", Niter)
console.log("nr, nc ",nr, nc)
// Create the beta vector to estimate
var bvec = create_const_matrix(nc,1,0)
console.log("bvec: ", bvec)
var prod = mult_matrices(X, bvec)
console.log("Product ", prod)
M = create_const_matrix(2,2,0);
M[0][0] = 1; M[0][1] = -7;
M[1][0] = 3; M[1][1] = 4;
console.log("M:", M)
Minv = invert_matrix(M)
console.log("M again:", M)
console.log("Minv:", Minv)
expMinv = exp_matrix(Minv)
console.log("expMinv:", expMinv)
expMinvNorm = exp_over_exp_p_1(Minv)
console.log("expMinv/(1+expMinv):", expMinvNorm)
delta_norm_arr = []
log_lf_arr = []
// The algorithm proper
for (ww=0; ww < Niter; ww++) {
X_bvec = mult_matrices(X, bvec)
expbv = exp_matrix(X_bvec)
p_x_b = exp_over_exp_p_1(X_bvec)
console.log("Iteration ", ww)
console.log("bvec ", bvec)
console.log("X_bvec ", X_bvec)
console.log("expbv ", expbv)
console.log("p_x_b ", p_x_b)
dL2db2 = calc_d2Ldb2(X,p_x_b,bvec.length)
dLdb = calc_dLdb(Y,X,p_x_b,bvec.length)
console.log("dL2db2 ", dL2db2)
console.log("dLdb ", dLdb)
Hinv = invert_matrix(dL2db2)
console.log("Hinv ", Hinv)
delta_bvec = mult_matrices(Hinv, dLdb)
console.log("delta_bvec ", delta_bvec)
bvec = subtr_matrices(bvec, delta_bvec)
lf = calc_log_lf(Y, X_bvec, expbv)
delta_norm = calc_norm(delta_bvec)
console.log("Log likelihood: ", lf)
console.log("Delta norm : ", delta_norm)
delta_norm_arr.push(delta_norm)
log_lf_arr.push(lf)
}
console.log("Final beta results : ", bvec)
console.log("Delta norm array : ", delta_norm_arr)
console.log("Log likelihood array : ", log_lf_arr)
res_str = ""
res_str = res_str.concat("Beta Vector:-<br>")
for (uu=0; uu < bvec.length; uu++) {
res_str = res_str.concat(bvec[uu][0].toString())
res_str = res_str.concat("<br>")
}
var lf_data = new Array();
var dbvec_data = new Array();
for (var idx = 0; idx < log_lf_arr.length; idx++) {
lf_data[idx] = [idx, log_lf_arr[idx]];
dbvec_data[idx] = [idx, delta_norm_arr[idx]];
}
var ser1 = {
label: "",
data: lf_data,
color: "#ffffff",
points: { show: false },
lines: { show: true },
bars: { show: false},
yaxis: 1
};
var ser2 = {
label: "delta bvec",
data: dbvec_data,
color: "#ffffff",
points: { show: false },
lines: { show: true },
bars: { show: false},
yaxis: 1
};
var data = [ ser1];
$.plot($("#chart1"),data);
X_bvec_est = mult_matrices(X, bvec)
expbv_est = exp_matrix(X_bvec_est)
expbv_est_one = create_const_matrix(expbv_est.length,2,0)
Y_est = create_const_matrix(expbv_est.length,1,0)
norm_expbv_est_0 = create_const_matrix(expbv_est.length,1,0)
for (var pp=0; pp < expbv_est.length; pp++) {
expbv_est_one[pp][0] = (expbv_est[pp][0])/(expbv_est[pp][0] + 1.0)
norm_expbv_est_0[pp][0] = expbv_est_one[pp][0]
expbv_est_one[pp][1] = 1.0/(expbv_est[pp][0] + 1.0)
if (expbv_est_one[pp][0] > expbv_est_one[pp][1]) {
Y_est[pp][0] = 1
}
}
y_diff = 0
for (var pp=0; pp < Y.length; pp++) {
if (Math.abs(Y[pp][0] - Y_est[pp][0]) > 1e-16) {
y_diff += 1
}
}
err_percent = ((y_diff + 0.0)/(Y.length + 0.0)) * 100.0
// console.log("Mismatch in percentage: ", err_percent)
res_str = res_str.concat("<br>")
res_str = res_str.concat("Classification mismatch: " + err_percent.toString() + "%")
res_str = res_str.fontcolor("white")
document.getElementById("p1").innerHTML=res_str
// console.log("X_bvec_est: ", X_bvec_est)
// console.log("expbv_est: ", expbv_est)
var c = 0.0
var sens_list = new Array()
var one_m_spec_list = new Array()
while (c < 1.01) {
res_sens_spec = gen_roc_data_point(Y, norm_expbv_est_0 ,c)
sens_list.push(res_sens_spec[0])
one_m_spec_list.push(1.0 - res_sens_spec[1])
c = c + 0.01
}
console.log("Sensitivity ", sens_list)
console.log("One minus spec ", one_m_spec_list)
var roc_data = new Array();
var roc_lin_data = new Array();
for (var gg = 0; gg < sens_list.length; gg++) {
roc_data[gg] = [one_m_spec_list[gg], sens_list[gg]];
roc_lin_data[gg] = [one_m_spec_list[gg], one_m_spec_list[gg]];
}
var ser_roc = {
label: "",
data: roc_data,
color: "#ffffff",
points: { show: false },
lines: { show: true },
bars: { show: false},
yaxis: 1
};
var ser_lin_roc = {
label: "",
data: roc_lin_data,
color: "#888888",
points: { show: false },
lines: { show: true },
bars: { show: false},
yaxis: 1
};
var data_roc = [ ser_roc, ser_lin_roc];
$.plot($("#chart2"),data_roc);
}
function prepend_with_ones(x_in) {
x_out = new Array()
var nr = x_in.length
var nc = x_in[0].length
for (ii=0; ii < nr; ii++) {
x_vec = []
x_vec.push(1)
for (jj=0; jj < nc; jj++) {
x_vec.push(x_in[ii][jj])
}
x_out.push(x_vec)
}
return x_out
}
function validate_matrix(inpstr) {
// The Matrix proper
var M = new Array()
var inp_arr = new Array()
inp_arr = inpstr.split("\n");
var num_rows = inp_arr.length
if (inp_arr[num_rows - 1] == "")
num_rows = num_rows - 1
var splitchar = ","
var tmpStr = inp_arr[0]
if (tmpStr.indexOf(',') == -1) {
splitchar = " "
}
var row_arr_filtered = inp_arr[0].split(splitchar).filter(function (el) {
return el != "";
});
var num_cols
var num_cols = row_arr_filtered.length
var dbg_str = ""
var cols_match = 1
// Check if all rows have same number of elements
for (ii = 0; ii < num_rows; ii++) {
var vec = inp_arr[ii].split(splitchar).filter(function (el) {
return el != "";});
var num_vec = new Array()
for (kk=0; kk < vec.length; kk++) {
num_vec.push(Number(vec[kk]))
}
M.push(num_vec)
if (ii == 0){
num_cols = vec.length
} else {
if (num_cols != vec.length) {
cols_match = 0
}
}
}
var dbg_str = ""
dbg_str += "Number of rows is " + num_rows.toString()
dbg_str += ", number of columns is " + num_cols.toString()
var isSquare = 1
if (num_rows != num_cols) {
isSquare = 0
}
return [cols_match, dbg_str,M, isSquare]
}
</script>
Alijah Ahmedhttp://www.blogger.com/profile/01151589526613233918noreply@blogger.comtag:blogger.com,1999:blog-8441309013858171051.post-27624244742665577112020-10-04T05:29:00.055-07:002020-10-04T06:55:09.519-07:00Cluster Number for K-means algorithm<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<script language="Javascript">
/**
* jQuery-csv (jQuery Plugin)
*
* This document is licensed as free software under the terms of the
* MIT License: http://www.opensource.org/licenses/mit-license.php
*
* Acknowledgements:
* The original design and influence to implement this library as a jquery
* plugin is influenced by jquery-json (http://code.google.com/p/jquery-json/).
* If you're looking to use native JSON.Stringify but want additional backwards
* compatibility for browsers that don't support it, I highly recommend you
* check it out.
*
* A special thanks goes out to rwk@acm.org for providing a lot of valuable
* feedback to the project including the core for the new FSM
* (Finite State Machine) parsers. If you're looking for a stable TSV parser
* be sure to take a look at jquery-tsv (http://code.google.com/p/jquery-tsv/).
* For legal purposes I'll include the "NO WARRANTY EXPRESSED OR IMPLIED.
* USE AT YOUR OWN RISK.". Which, in 'layman's terms' means, by using this
* library you are accepting responsibility if it breaks your code.
*
* Legal jargon aside, I will do my best to provide a useful and stable core
* that can effectively be built on.
*
* Copyrighted 2012 by Evan Plaice.
*/
RegExp.escape= function(s) {
return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
};
(function (undefined) {
'use strict';
var $;
// to keep backwards compatibility
if (typeof jQuery !== 'undefined' && jQuery) {
$ = jQuery;
} else {
$ = {};
}
/**
* jQuery.csv.defaults
* Encapsulates the method paramater defaults for the CSV plugin module.
*/
$.csv = {
defaults: {
separator:',',
delimiter:'"',
headers:true
},
hooks: {
castToScalar: function(value, state) {
var hasDot = /\./;
if (isNaN(value)) {
return value;
} else {
if (hasDot.test(value)) {
return parseFloat(value);
} else {
var integer = parseInt(value);
if(isNaN(integer)) {
return null;
} else {
return integer;
}
}
}
}
},
parsers: {
parse: function(csv, options) {
// cache settings
var separator = options.separator;
var delimiter = options.delimiter;
// set initial state if it's missing
if(!options.state.rowNum) {
options.state.rowNum = 1;
}
if(!options.state.colNum) {
options.state.colNum = 1;
}
// clear initial state
var data = [];
var entry = [];
var state = 0;
var value = '';
var exit = false;
function endOfEntry() {
// reset the state
state = 0;
value = '';
// if 'start' hasn't been met, don't output
if(options.start && options.state.rowNum < options.start) {
// update global state
entry = [];
options.state.rowNum++;
options.state.colNum = 1;
return;
}
if(options.onParseEntry === undefined) {
// onParseEntry hook not set
data.push(entry);
} else {
var hookVal = options.onParseEntry(entry, options.state); // onParseEntry Hook
// false skips the row, configurable through a hook
if(hookVal !== false) {
data.push(hookVal);
}
}
//console.log('entry:' + entry);
// cleanup
entry = [];
// if 'end' is met, stop parsing
if(options.end && options.state.rowNum >= options.end) {
exit = true;
}
// update global state
options.state.rowNum++;
options.state.colNum = 1;
}
function endOfValue() {
if(options.onParseValue === undefined) {
// onParseValue hook not set
entry.push(value);
} else {
var hook = options.onParseValue(value, options.state); // onParseValue Hook
// false skips the row, configurable through a hook
if(hook !== false) {
entry.push(hook);
}
}
//console.log('value:' + value);
// reset the state
value = '';
state = 0;
// update global state
options.state.colNum++;
}
// escape regex-specific control chars
var escSeparator = RegExp.escape(separator);
var escDelimiter = RegExp.escape(delimiter);
// compile the regEx str using the custom delimiter/separator
var match = /(D|S|\r\n|\n|\r|[^DS\r\n]+)/;
var matchSrc = match.source;
matchSrc = matchSrc.replace(/S/g, escSeparator);
matchSrc = matchSrc.replace(/D/g, escDelimiter);
match = new RegExp(matchSrc, 'gm');
// put on your fancy pants...
// process control chars individually, use look-ahead on non-control chars
csv.replace(match, function (m0) {
if(exit) {
return;
}
switch (state) {
// the start of a value
case 0:
// null last value
if (m0 === separator) {
value += '';
endOfValue();
break;
}
// opening delimiter
if (m0 === delimiter) {
state = 1;
break;
}
// null last value
if (/^(\r\n|\n|\r)$/.test(m0)) {
endOfValue();
endOfEntry();
break;
}
// un-delimited value
value += m0;
state = 3;
break;
// delimited input
case 1:
// second delimiter? check further
if (m0 === delimiter) {
state = 2;
break;
}
// delimited data
value += m0;
state = 1;
break;
// delimiter found in delimited input
case 2:
// escaped delimiter?
if (m0 === delimiter) {
value += m0;
state = 1;
break;
}
// null value
if (m0 === separator) {
endOfValue();
break;
}
// end of entry
if (/^(\r\n|\n|\r)$/.test(m0)) {
endOfValue();
endOfEntry();
break;
}
// broken paser?
throw new Error('CSVDataError: Illegal State [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
// un-delimited input
case 3:
// null last value
if (m0 === separator) {
endOfValue();
break;
}
// end of entry
if (/^(\r\n|\n|\r)$/.test(m0)) {
endOfValue();
endOfEntry();
break;
}
if (m0 === delimiter) {
// non-compliant data
throw new Error('CSVDataError: Illegal Quote [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
}
// broken parser?
throw new Error('CSVDataError: Illegal Data [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
default:
// shenanigans
throw new Error('CSVDataError: Unknown State [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
}
//console.log('val:' + m0 + ' state:' + state);
});
// submit the last entry
// ignore null last line
if(entry.length !== 0) {
endOfValue();
endOfEntry();
}
return data;
},
// a csv-specific line splitter
splitLines: function(csv, options) {
// cache settings
var separator = options.separator;
var delimiter = options.delimiter;
// set initial state if it's missing
if(!options.state.rowNum) {
options.state.rowNum = 1;
}
// clear initial state
var entries = [];
var state = 0;
var entry = '';
var exit = false;
function endOfLine() {
// reset the state
state = 0;
// if 'start' hasn't been met, don't output
if(options.start && options.state.rowNum < options.start) {
// update global state
entry = '';
options.state.rowNum++;
return;
}
if(options.onParseEntry === undefined) {
// onParseEntry hook not set
entries.push(entry);
} else {
var hookVal = options.onParseEntry(entry, options.state); // onParseEntry Hook
// false skips the row, configurable through a hook
if(hookVal !== false) {
entries.push(hookVal);
}
}
// cleanup
entry = '';
// if 'end' is met, stop parsing
if(options.end && options.state.rowNum >= options.end) {
exit = true;
}
// update global state
options.state.rowNum++;
}
// escape regex-specific control chars
var escSeparator = RegExp.escape(separator);
var escDelimiter = RegExp.escape(delimiter);
// compile the regEx str using the custom delimiter/separator
var match = /(D|S|\n|\r|[^DS\r\n]+)/;
var matchSrc = match.source;
matchSrc = matchSrc.replace(/S/g, escSeparator);
matchSrc = matchSrc.replace(/D/g, escDelimiter);
match = new RegExp(matchSrc, 'gm');
// put on your fancy pants...
// process control chars individually, use look-ahead on non-control chars
csv.replace(match, function (m0) {
if(exit) {
return;
}
switch (state) {
// the start of a value/entry
case 0:
// null value
if (m0 === separator) {
entry += m0;
state = 0;
break;
}
// opening delimiter
if (m0 === delimiter) {
entry += m0;
state = 1;
break;
}
// end of line
if (m0 === '\n') {
endOfLine();
break;
}
// phantom carriage return
if (/^\r$/.test(m0)) {
break;
}
// un-delimit value
entry += m0;
state = 3;
break;
// delimited input
case 1:
// second delimiter? check further
if (m0 === delimiter) {
entry += m0;
state = 2;
break;
}
// delimited data
entry += m0;
state = 1;
break;
// delimiter found in delimited input
case 2:
// escaped delimiter?
var prevChar = entry.substr(entry.length - 1);
if (m0 === delimiter && prevChar === delimiter) {
entry += m0;
state = 1;
break;
}
// end of value
if (m0 === separator) {
entry += m0;
state = 0;
break;
}
// end of line
if (m0 === '\n') {
endOfLine();
break;
}
// phantom carriage return
if (m0 === '\r') {
break;
}
// broken paser?
throw new Error('CSVDataError: Illegal state [Row:' + options.state.rowNum + ']');
// un-delimited input
case 3:
// null value
if (m0 === separator) {
entry += m0;
state = 0;
break;
}
// end of line
if (m0 === '\n') {
endOfLine();
break;
}
// phantom carriage return
if (m0 === '\r') {
break;
}
// non-compliant data
if (m0 === delimiter) {
throw new Error('CSVDataError: Illegal quote [Row:' + options.state.rowNum + ']');
}
// broken parser?
throw new Error('CSVDataError: Illegal state [Row:' + options.state.rowNum + ']');
default:
// shenanigans
throw new Error('CSVDataError: Unknown state [Row:' + options.state.rowNum + ']');
}
//console.log('val:' + m0 + ' state:' + state);
});
// submit the last entry
// ignore null last line
if(entry !== '') {
endOfLine();
}
return entries;
},
// a csv entry parser
parseEntry: function(csv, options) {
// cache settings
var separator = options.separator;
var delimiter = options.delimiter;
// set initial state if it's missing
if(!options.state.rowNum) {
options.state.rowNum = 1;
}
if(!options.state.colNum) {
options.state.colNum = 1;
}
// clear initial state
var entry = [];
var state = 0;
var value = '';
function endOfValue() {
if(options.onParseValue === undefined) {
// onParseValue hook not set
entry.push(value);
} else {
var hook = options.onParseValue(value, options.state); // onParseValue Hook
// false skips the value, configurable through a hook
if(hook !== false) {
entry.push(hook);
}
}
// reset the state
value = '';
state = 0;
// update global state
options.state.colNum++;
}
// checked for a cached regEx first
if(!options.match) {
// escape regex-specific control chars
var escSeparator = RegExp.escape(separator);
var escDelimiter = RegExp.escape(delimiter);
// compile the regEx str using the custom delimiter/separator
var match = /(D|S|\n|\r|[^DS\r\n]+)/;
var matchSrc = match.source;
matchSrc = matchSrc.replace(/S/g, escSeparator);
matchSrc = matchSrc.replace(/D/g, escDelimiter);
options.match = new RegExp(matchSrc, 'gm');
}
// put on your fancy pants...
// process control chars individually, use look-ahead on non-control chars
csv.replace(options.match, function (m0) {
switch (state) {
// the start of a value
case 0:
// null last value
if (m0 === separator) {
value += '';
endOfValue();
break;
}
// opening delimiter
if (m0 === delimiter) {
state = 1;
break;
}
// skip un-delimited new-lines
if (m0 === '\n' || m0 === '\r') {
break;
}
// un-delimited value
value += m0;
state = 3;
break;
// delimited input
case 1:
// second delimiter? check further
if (m0 === delimiter) {
state = 2;
break;
}
// delimited data
value += m0;
state = 1;
break;
// delimiter found in delimited input
case 2:
// escaped delimiter?
if (m0 === delimiter) {
value += m0;
state = 1;
break;
}
// null value
if (m0 === separator) {
endOfValue();
break;
}
// skip un-delimited new-lines
if (m0 === '\n' || m0 === '\r') {
break;
}
// broken paser?
throw new Error('CSVDataError: Illegal State [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
// un-delimited input
case 3:
// null last value
if (m0 === separator) {
endOfValue();
break;
}
// skip un-delimited new-lines
if (m0 === '\n' || m0 === '\r') {
break;
}
// non-compliant data
if (m0 === delimiter) {
throw new Error('CSVDataError: Illegal Quote [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
}
// broken parser?
throw new Error('CSVDataError: Illegal Data [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
default:
// shenanigans
throw new Error('CSVDataError: Unknown State [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
}
//console.log('val:' + m0 + ' state:' + state);
});
// submit the last value
endOfValue();
return entry;
}
},
helpers: {
/**
* $.csv.helpers.collectPropertyNames(objectsArray)
* Collects all unique property names from all passed objects.
*
* @param {Array} objects Objects to collect properties from.
*
* Returns an array of property names (array will be empty,
* if objects have no own properties).
*/
collectPropertyNames: function (objects) {
var o, propName, props = [];
for (o in objects) {
for (propName in objects[o]) {
if ((objects[o].hasOwnProperty(propName)) &&
(props.indexOf(propName) < 0) &&
(typeof objects[o][propName] !== 'function')) {
props.push(propName);
}
}
}
return props;
}
},
/**
* $.csv.toArray(csv)
* Converts a CSV entry string to a javascript array.
*
* @param {Array} csv The string containing the CSV data.
* @param {Object} [options] An object containing user-defined options.
* @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
* @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
*
* This method deals with simple CSV strings only. It's useful if you only
* need to parse a single entry. If you need to parse more than one line,
* use $.csv2Array instead.
*/
toArray: function(csv, options, callback) {
options = (options !== undefined ? options : {});
var config = {};
config.callback = ((callback !== undefined && typeof(callback) === 'function') ? callback : false);
config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator;
config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter;
var state = (options.state !== undefined ? options.state : {});
// setup
options = {
delimiter: config.delimiter,
separator: config.separator,
onParseEntry: options.onParseEntry,
onParseValue: options.onParseValue,
state: state
};
var entry = $.csv.parsers.parseEntry(csv, options);
// push the value to a callback if one is defined
if(!config.callback) {
return entry;
} else {
config.callback('', entry);
}
},
/**
* $.csv.toArrays(csv)
* Converts a CSV string to a javascript array.
*
* @param {String} csv The string containing the raw CSV data.
* @param {Object} [options] An object containing user-defined options.
* @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
* @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
*
* This method deals with multi-line CSV. The breakdown is simple. The first
* dimension of the array represents the line (or entry/row) while the second
* dimension contains the values (or values/columns).
*/
toArrays: function(csv, options, callback) {
options = (options !== undefined ? options : {});
var config = {};
config.callback = ((callback !== undefined && typeof(callback) === 'function') ? callback : false);
config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator;
config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter;
// setup
var data = [];
options = {
delimiter: config.delimiter,
separator: config.separator,
onPreParse: options.onPreParse,
onParseEntry: options.onParseEntry,
onParseValue: options.onParseValue,
onPostParse: options.onPostParse,
start: options.start,
end: options.end,
state: {
rowNum: 1,
colNum: 1
}
};
// onPreParse hook
if(options.onPreParse !== undefined) {
options.onPreParse(csv, options.state);
}
// parse the data
data = $.csv.parsers.parse(csv, options);
// onPostParse hook
if(options.onPostParse !== undefined) {
options.onPostParse(data, options.state);
}
// push the value to a callback if one is defined
if(!config.callback) {
return data;
} else {
config.callback('', data);
}
},
/**
* $.csv.toObjects(csv)
* Converts a CSV string to a javascript object.
* @param {String} csv The string containing the raw CSV data.
* @param {Object} [options] An object containing user-defined options.
* @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
* @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
* @param {Boolean} [headers] Indicates whether the data contains a header line. Defaults to true.
*
* This method deals with multi-line CSV strings. Where the headers line is
* used as the key for each value per entry.
*/
toObjects: function(csv, options, callback) {
options = (options !== undefined ? options : {});
var config = {};
config.callback = ((callback !== undefined && typeof(callback) === 'function') ? callback : false);
config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator;
config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter;
config.headers = 'headers' in options ? options.headers : $.csv.defaults.headers;
options.start = 'start' in options ? options.start : 1;
// account for headers
if(config.headers) {
options.start++;
}
if(options.end && config.headers) {
options.end++;
}
// setup
var lines = [];
var data = [];
options = {
delimiter: config.delimiter,
separator: config.separator,
onPreParse: options.onPreParse,
onParseEntry: options.onParseEntry,
onParseValue: options.onParseValue,
onPostParse: options.onPostParse,
start: options.start,
end: options.end,
state: {
rowNum: 1,
colNum: 1
},
match: false,
transform: options.transform
};
// fetch the headers
var headerOptions = {
delimiter: config.delimiter,
separator: config.separator,
start: 1,
end: 1,
state: {
rowNum:1,
colNum:1
}
};
// onPreParse hook
if(options.onPreParse !== undefined) {
options.onPreParse(csv, options.state);
}
// parse the csv
var headerLine = $.csv.parsers.splitLines(csv, headerOptions);
var headers = $.csv.toArray(headerLine[0], options);
// fetch the data
lines = $.csv.parsers.splitLines(csv, options);
// reset the state for re-use
options.state.colNum = 1;
if(headers){
options.state.rowNum = 2;
} else {
options.state.rowNum = 1;
}
// convert data to objects
for(var i=0, len=lines.length; i<len; i++) {
var entry = $.csv.toArray(lines[i], options);
var object = {};
for(var j=0; j <headers.length; j++) {
object[headers[j]] = entry[j];
}
if (options.transform !== undefined) {
data.push(options.transform.call(undefined, object));
} else {
data.push(object);
}
// update row state
options.state.rowNum++;
}
// onPostParse hook
if(options.onPostParse !== undefined) {
options.onPostParse(data, options.state);
}
// push the value to a callback if one is defined
if(!config.callback) {
return data;
} else {
config.callback('', data);
}
},
/**
* $.csv.fromArrays(arrays)
* Converts a javascript array to a CSV String.
*
* @param {Array} arrays An array containing an array of CSV entries.
* @param {Object} [options] An object containing user-defined options.
* @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
* @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
*
* This method generates a CSV file from an array of arrays (representing entries).
*/
fromArrays: function(arrays, options, callback) {
options = (options !== undefined ? options : {});
var config = {};
config.callback = ((callback !== undefined && typeof(callback) === 'function') ? callback : false);
config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator;
config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter;
var output = '',
line,
lineValues,
i, j;
for (i = 0; i < arrays.length; i++) {
line = arrays[i];
lineValues = [];
for (j = 0; j < line.length; j++) {
var strValue = (line[j] === undefined || line[j] === null) ? '' : line[j].toString();
if (strValue.indexOf(config.delimiter) > -1) {
strValue = strValue.replace(config.delimiter, config.delimiter + config.delimiter);
}
var escMatcher = '\n|\r|S|D';
escMatcher = escMatcher.replace('S', config.separator);
escMatcher = escMatcher.replace('D', config.delimiter);
if (strValue.search(escMatcher) > -1) {
strValue = config.delimiter + strValue + config.delimiter;
}
lineValues.push(strValue);
}
output += lineValues.join(config.separator) + '\r\n';
}
// push the value to a callback if one is defined
if(!config.callback) {
return output;
} else {
config.callback('', output);
}
},
/**
* $.csv.fromObjects(objects)
* Converts a javascript dictionary to a CSV string.
*
* @param {Object} objects An array of objects containing the data.
* @param {Object} [options] An object containing user-defined options.
* @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
* @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
* @param {Character} [sortOrder] Sort order of columns (named after
* object properties). Use 'alpha' for alphabetic. Default is 'declare',
* which means, that properties will _probably_ appear in order they were
* declared for the object. But without any guarantee.
* @param {Character or Array} [manualOrder] Manually order columns. May be
* a strin in a same csv format as an output or an array of header names
* (array items won't be parsed). All the properties, not present in
* `manualOrder` will be appended to the end in accordance with `sortOrder`
* option. So the `manualOrder` always takes preference, if present.
*
* This method generates a CSV file from an array of objects (name:value pairs).
* It starts by detecting the headers and adding them as the first line of
* the CSV file, followed by a structured dump of the data.
*/
fromObjects: function(objects, options, callback) {
options = (options !== undefined ? options : {});
var config = {};
config.callback = ((callback !== undefined && typeof(callback) === 'function') ? callback : false);
config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator;
config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter;
config.headers = 'headers' in options ? options.headers : $.csv.defaults.headers;
config.sortOrder = 'sortOrder' in options ? options.sortOrder : 'declare';
config.manualOrder = 'manualOrder' in options ? options.manualOrder : [];
config.transform = options.transform;
if (typeof config.manualOrder === 'string') {
config.manualOrder = $.csv.toArray(config.manualOrder, config);
}
if (config.transform !== undefined) {
var origObjects = objects;
objects = [];
var i;
for (i = 0; i < origObjects.length; i++) {
objects.push(config.transform.call(undefined, origObjects[i]));
}
}
var props = $.csv.helpers.collectPropertyNames(objects);
if (config.sortOrder === 'alpha') {
props.sort();
} // else {} - nothing to do for 'declare' order
if (config.manualOrder.length > 0) {
var propsManual = [].concat(config.manualOrder);
var p;
for (p = 0; p < props.length; p++) {
if (propsManual.indexOf( props[p] ) < 0) {
propsManual.push( props[p] );
}
}
props = propsManual;
}
var o, p, line, output = [], propName;
if (config.headers) {
output.push(props);
}
for (o = 0; o < objects.length; o++) {
line = [];
for (p = 0; p < props.length; p++) {
propName = props[p];
if (propName in objects[o] && typeof objects[o][propName] !== 'function') {
line.push(objects[o][propName]);
} else {
line.push('');
}
}
output.push(line);
}
// push the value to a callback if one is defined
return $.csv.fromArrays(output, options, config.callback);
}
};
// Maintenance code to maintain backward-compatibility
// Will be removed in release 1.0
$.csvEntry2Array = $.csv.toArray;
$.csv2Array = $.csv.toArrays;
$.csv2Dictionary = $.csv.toObjects;
// CommonJS module is defined
if (typeof module !== 'undefined' && module.exports) {
module.exports = $.csv;
}
}).call( this );
</script>
<script>
$(document).ready(function() {
if(isAPIAvailable()) {
$('#files').bind('change', handleFileSelect);
}
});
function isAPIAvailable() {
// Check for the various File API support.
if (window.File && window.FileReader && window.FileList && window.Blob) {
// Great success! All the File APIs are supported.
return true;
} else {
// source: File API availability - http://caniuse.com/#feat=fileapi
// source: <output> availability - http://html5doctor.com/the-output-element/
document.writeln('The HTML5 APIs used in this form are only available in the following browsers:<br />');
// 6.0 File API & 13.0 <output>
document.writeln(' - Google Chrome: 13.0 or later<br />');
// 3.6 File API & 6.0 <output>
document.writeln(' - Mozilla Firefox: 6.0 or later<br />');
// 10.0 File API & 10.0 <output>
document.writeln(' - Internet Explorer: Not supported (partial support expected in 10.0)<br />');
// ? File API & 5.1 <output>
document.writeln(' - Safari: Not supported<br />');
// ? File API & 9.2 <output>
document.writeln(' - Opera: Not supported');
return false;
}
}
function handleFileSelect(evt) {
var files = evt.target.files; // FileList object
var file = files[0];
// read the file metadata
var output = ''
output += '<span style="font-weight:bold;">' + escape(file.name) + '</span><br />\n';
output += ' - FileType: ' + (file.type || 'n/a') + '<br />\n';
output += ' - FileSize: ' + file.size + ' bytes<br />\n';
output += ' - LastModified: ' + (file.lastModifiedDate ? file.lastModifiedDate.toLocaleDateString() : 'n/a') + '<br />\n';
// read the file contents
printTextArea(file);
// post the results
$('#list').append(output);
}
function printTextArea(file) {
var reader = new FileReader();
reader.readAsText(file);
reader.onload = function(event){
var csv = event.target.result;
var data = $.csv.toArrays(csv);
var mystr = "";
for(var row in data) {
vec_len = data[row].length
for (var ii = 0; ii < vec_len - 1; ii++)
{
if (!isNaN(Number(data[row][ii])))
{
mystr = mystr.concat((Number(data[row][ii]).toFixed(6)).toString());
mystr = mystr.concat(",");
}
}
mystr = mystr.concat((Number(data[row][ii]).toFixed(6)).toString());
mystr = mystr.concat("\n");
/*
if (!isNaN(Number(data[row][0])))
{
mystr = mystr.concat((Number(data[row][0]).toFixed(3)).toString());
}
if (!isNaN(Number(data[row][1])))
{
mystr = mystr.concat(",");
mystr = mystr.concat((Number(data[row][1]).toFixed(3)).toString());
mystr = mystr.concat("\n");
}
else
{
mystr = mystr.concat("\n");
}
*/
}
mystr = mystr.substring(0,mystr.length - 1);
document.getElementById("Real_inp").value = mystr;
};
reader.onerror = function(){ alert('Unable to read ' + file.fileName); };
}
</script>
<!--[if IE ]>
<script type="text/javascript" src="https://firebasestorage.googleapis.com/v0/b/js-static-assets.appspot.com/o/excanvas_min.js?alt=media&token=9dd78ad2-f09b-45dd-8a09-8db2b2546d1d"></script>
<![endif]-->
<script src="https://firebasestorage.googleapis.com/v0/b/js-static-assets.appspot.com/o/jquery_flot.js?alt=media&token=bde69a50-3df9-4794-b7d8-dd4e840472cd" type="text/javascript"></script>
<p style="text-align:justify">
This blog post plots the total Within-cluster Sum of Squares (WSS) against the number of clusters, for the k-means algorithm. By examining how this parameter decreases with the increasing number of clusters, an intuition can be gained over how many clusters are required for the dataset. The elbow method can be used, whereby increasing the number of clusters after a certain cluster number does not significantly decrease the total WSS. For a k-means calculator with 3d plot display, you can have a look <a href="https://scistatcalc.blogspot.com/2020/10/online-k-means-calculator-for-arbitrary.html">here.</a></p>
<p style="text-align:justify">
The cluster centres (or centroids) are initialised using a variant of the k-means++ algorithm as proposed by David Arthur and Sergei Vassilvitski in 2007.</p>
<p style="text-align:justify">
Please enter lines of comma separated numbers in the text areas below - after the last number in each line there must be no trailing comma. In addition, there must be no new line after the last sample. In addition, the maximum number of clusters in the appropriate field needs to be entered. </p>
<p style="text-align:justify">
Alternatively you can choose to load a CSV file, that must contain only comma separated numbers.</p>
<p style="text-align:justify">
To perform the k-means clustering for cluster size varying from 1 to the maximum specified number, simply press the button labelled "Perform k-means over multiple cluster numbers" below. The results for the maximum cluster number will populate the textareas below labelled "Label and data sample" and "Label and Centroid values". Most importantly, a graph plotting the varation of total WSS with cluster number will be updated.</p>
<p style="text-align:justify">
An example three-dimensional dataset has been loaded, with three clusters of 200 samples, as guidance. The maximum number of clusters is set to 6, so that one can visualise the elbow of the graph at cluster number 3.
</p>
<br />
<div class="clearfix" id="inputs">
<input id="files" name="files[]" single="" type="file" />
</div>
<br />
<div style="float: left; width: 200px;">
Input
</div>
<br />
<textarea cols="25" id="Real_inp" rows="15"></textarea>
<br />
<div style="float: left; width: 200px;">
<input name="b1" onclick="clear_real_inp()" style="margin-left: 0px;" type="Button" value="Clear Input" />
</div>
<br>
<br>
<form name="frmOne">
Enter maximum number of clusters (k value):-<br>
<input name="txtKNumber" size="30" type="TextArea" value="" />
<br>
<br>
<input name="b1" onclick="calc_k_means()" style="margin-left: 0px;" type="Button" value="Perform k-means over multiple cluster numbers" />
<br />
<br />
<div style="float: left; width: 200px;">
Label and data sample:-
</div>
<div style="float: left; width: 200px;">
Label and Centroid values:-
</div>
<br>
<textarea cols="25" id="Result_out" rows="10"></textarea>
<textarea cols="25" id="Centroid_out" rows="10"></textarea>
<br>
<br>
<table style="text-align: center;">
<tbody>
<tr>
<td></td>
<td>Total WSS vs Number of Clusters</td>
<td></td>
</tr>
<tr>
<td>t-WSS</td>
<td><div id='chart0' style='height:300px;width:500px;'>
</div>
</td>
<td></td>
</tr>
<tr>
<td></td>
<td>Number of clusters</td>
<td></td>
<div><p id="legendDiv"></p></div>
</tr>
</tbody></table>
<body>
<div id='myDiv'><!-- Plotly chart will be drawn inside this DIV --></div>
</body>
<!-- Load plotly.js into the DOM -->
<script src='https://cdn.plot.ly/plotly-latest.min.js'></script>
<script language="JavaScript">
// Load an example dataset
window.onload = set_inputs();
function set_inputs() {
var sample_data = ""
sample_data = sample_data + "2.015,2.150,1.701\n"
sample_data = sample_data + "2.208,1.875,1.525\n"
sample_data = sample_data + "0.806,1.180,1.100\n"
sample_data = sample_data + "1.656,2.186,1.974\n"
sample_data = sample_data + "1.128,1.100,0.653\n"
sample_data = sample_data + "0.105,-0.182,0.464\n"
sample_data = sample_data + "1.217,1.014,0.705\n"
sample_data = sample_data + "2.016,2.048,1.637\n"
sample_data = sample_data + "2.667,2.118,2.508\n"
sample_data = sample_data + "0.666,1.491,0.592\n"
sample_data = sample_data + "-0.195,0.163,0.014\n"
sample_data = sample_data + "0.293,0.668,1.251\n"
sample_data = sample_data + "2.626,2.274,1.917\n"
sample_data = sample_data + "1.239,0.657,1.153\n"
sample_data = sample_data + "1.596,1.997,1.961\n"
sample_data = sample_data + "0.241,-0.091,0.361\n"
sample_data = sample_data + "0.941,1.251,1.236\n"
sample_data = sample_data + "1.448,2.011,2.011\n"
sample_data = sample_data + "-0.234,0.054,-0.437\n"
sample_data = sample_data + "0.167,0.153,0.090\n"
sample_data = sample_data + "2.743,2.106,2.020\n"
sample_data = sample_data + "-0.220,0.089,-0.289\n"
sample_data = sample_data + "1.382,0.806,1.048\n"
sample_data = sample_data + "2.597,2.349,2.073\n"
sample_data = sample_data + "1.414,0.984,1.239\n"
sample_data = sample_data + "2.006,1.728,2.129\n"
sample_data = sample_data + "2.280,1.896,1.671\n"
sample_data = sample_data + "0.842,0.286,0.818\n"
sample_data = sample_data + "1.677,2.607,1.831\n"
sample_data = sample_data + "1.537,2.261,1.947\n"
sample_data = sample_data + "1.015,1.057,1.063\n"
sample_data = sample_data + "0.888,1.286,1.157\n"
sample_data = sample_data + "1.851,1.949,1.717\n"
sample_data = sample_data + "0.084,0.221,0.195\n"
sample_data = sample_data + "0.184,-0.038,0.474\n"
sample_data = sample_data + "0.938,1.009,0.899\n"
sample_data = sample_data + "1.024,0.928,1.610\n"
sample_data = sample_data + "1.128,0.495,0.870\n"
sample_data = sample_data + "1.575,2.264,2.412\n"
sample_data = sample_data + "1.865,2.184,1.937\n"
sample_data = sample_data + "2.122,2.520,1.895\n"
sample_data = sample_data + "-0.370,0.215,0.155\n"
sample_data = sample_data + "1.202,1.308,0.796\n"
sample_data = sample_data + "0.410,1.312,0.877\n"
sample_data = sample_data + "1.638,1.650,2.339\n"
sample_data = sample_data + "0.054,0.091,0.255\n"
sample_data = sample_data + "0.797,0.662,0.744\n"
sample_data = sample_data + "-0.331,-0.087,-0.267\n"
sample_data = sample_data + "2.012,1.856,1.976\n"
sample_data = sample_data + "0.195,0.420,0.467\n"
sample_data = sample_data + "2.034,2.064,1.847\n"
sample_data = sample_data + "0.302,0.460,-0.178\n"
sample_data = sample_data + "2.229,1.901,1.949\n"
sample_data = sample_data + "0.055,-0.120,0.269\n"
sample_data = sample_data + "-0.449,-0.209,-0.036\n"
sample_data = sample_data + "-0.313,0.108,-0.454\n"
sample_data = sample_data + "2.017,1.568,1.956\n"
sample_data = sample_data + "2.152,2.661,2.323\n"
sample_data = sample_data + "0.485,0.020,0.635\n"
sample_data = sample_data + "0.928,0.924,0.615\n"
sample_data = sample_data + "1.067,1.316,0.810\n"
sample_data = sample_data + "0.889,0.861,0.901\n"
sample_data = sample_data + "-0.177,-0.405,0.414\n"
sample_data = sample_data + "1.408,1.722,2.103\n"
sample_data = sample_data + "0.303,-0.063,-0.085\n"
sample_data = sample_data + "2.015,1.922,2.263\n"
sample_data = sample_data + "1.855,1.704,1.858\n"
sample_data = sample_data + "0.029,0.369,0.480\n"
sample_data = sample_data + "1.880,1.561,1.585\n"
sample_data = sample_data + "0.638,-0.121,0.137\n"
sample_data = sample_data + "0.575,-0.204,-0.058\n"
sample_data = sample_data + "0.826,0.671,1.240\n"
sample_data = sample_data + "1.320,0.438,1.295\n"
sample_data = sample_data + "1.929,2.055,2.059\n"
sample_data = sample_data + "-0.392,-0.093,-0.297\n"
sample_data = sample_data + "2.575,2.656,2.025\n"
sample_data = sample_data + "0.646,1.347,0.943\n"
sample_data = sample_data + "0.524,0.591,0.502\n"
sample_data = sample_data + "0.881,0.701,0.801\n"
sample_data = sample_data + "1.971,1.723,2.358\n"
sample_data = sample_data + "0.197,-0.010,-0.120\n"
sample_data = sample_data + "1.195,1.414,0.421\n"
sample_data = sample_data + "0.070,-0.090,-0.223\n"
sample_data = sample_data + "-0.102,0.040,0.561\n"
sample_data = sample_data + "2.017,1.871,1.632\n"
sample_data = sample_data + "2.232,1.774,1.966\n"
sample_data = sample_data + "1.214,1.016,0.884\n"
sample_data = sample_data + "1.948,2.270,1.994\n"
sample_data = sample_data + "0.073,0.127,-0.100\n"
sample_data = sample_data + "1.098,0.917,1.340\n"
sample_data = sample_data + "0.151,-0.519,-0.062\n"
sample_data = sample_data + "1.333,0.936,0.873\n"
sample_data = sample_data + "2.298,1.675,2.020\n"
sample_data = sample_data + "0.177,0.256,-0.310\n"
sample_data = sample_data + "0.626,1.448,0.766\n"
sample_data = sample_data + "1.973,2.729,1.860\n"
sample_data = sample_data + "-0.661,0.836,0.304\n"
sample_data = sample_data + "2.108,2.833,1.977\n"
sample_data = sample_data + "1.434,1.004,0.917\n"
sample_data = sample_data + "1.190,1.265,1.385\n"
sample_data = sample_data + "2.358,1.850,1.976\n"
sample_data = sample_data + "2.581,2.183,2.029\n"
sample_data = sample_data + "2.404,2.420,2.387\n"
sample_data = sample_data + "1.836,1.885,2.226\n"
sample_data = sample_data + "1.358,0.641,0.863\n"
sample_data = sample_data + "-0.069,0.302,-0.325\n"
sample_data = sample_data + "1.811,1.027,1.186\n"
sample_data = sample_data + "0.669,0.189,0.227\n"
sample_data = sample_data + "-0.052,0.143,-0.315\n"
sample_data = sample_data + "-0.021,0.013,-0.295\n"
sample_data = sample_data + "1.304,1.138,0.444\n"
sample_data = sample_data + "1.239,0.725,1.177\n"
sample_data = sample_data + "-0.238,-0.105,0.128\n"
sample_data = sample_data + "1.584,0.549,0.899\n"
sample_data = sample_data + "0.900,1.331,0.929\n"
sample_data = sample_data + "1.128,1.403,1.233\n"
sample_data = sample_data + "0.878,0.873,0.923\n"
sample_data = sample_data + "2.050,1.746,1.987\n"
sample_data = sample_data + "2.176,1.863,1.860\n"
sample_data = sample_data + "1.089,1.243,0.601\n"
sample_data = sample_data + "-0.293,0.388,-0.005\n"
sample_data = sample_data + "1.672,2.038,2.477\n"
sample_data = sample_data + "0.817,0.714,0.927\n"
sample_data = sample_data + "0.479,-0.196,0.160\n"
sample_data = sample_data + "1.302,1.187,0.898\n"
sample_data = sample_data + "0.196,0.051,0.244\n"
sample_data = sample_data + "1.118,1.043,0.481\n"
sample_data = sample_data + "0.194,-0.034,-0.621\n"
sample_data = sample_data + "-0.013,0.089,-0.035\n"
sample_data = sample_data + "1.269,1.783,1.712\n"
sample_data = sample_data + "1.905,1.312,2.085\n"
sample_data = sample_data + "2.160,2.172,1.984\n"
sample_data = sample_data + "0.971,1.302,1.136\n"
sample_data = sample_data + "1.876,2.090,2.209\n"
sample_data = sample_data + "1.360,1.141,0.767\n"
sample_data = sample_data + "1.855,2.068,2.094\n"
sample_data = sample_data + "0.306,-0.559,-0.211\n"
sample_data = sample_data + "1.659,2.325,2.232\n"
sample_data = sample_data + "1.102,1.244,0.834\n"
sample_data = sample_data + "1.160,1.022,1.032\n"
sample_data = sample_data + "-0.033,0.289,0.321\n"
sample_data = sample_data + "0.578,0.847,1.028\n"
sample_data = sample_data + "1.175,0.524,0.728\n"
sample_data = sample_data + "0.911,1.524,0.745\n"
sample_data = sample_data + "-0.114,-0.007,-0.028\n"
sample_data = sample_data + "1.049,0.994,0.946\n"
sample_data = sample_data + "2.087,2.084,2.115\n"
sample_data = sample_data + "1.121,0.655,1.054\n"
sample_data = sample_data + "1.048,1.235,1.208\n"
sample_data = sample_data + "2.464,1.766,1.964\n"
sample_data = sample_data + "1.067,0.816,0.923\n"
sample_data = sample_data + "0.894,0.862,1.681\n"
sample_data = sample_data + "-0.019,0.442,-0.250\n"
sample_data = sample_data + "0.088,-0.183,0.106\n"
sample_data = sample_data + "1.501,1.584,1.248\n"
sample_data = sample_data + "0.143,0.009,0.123\n"
sample_data = sample_data + "1.416,0.526,0.918\n"
sample_data = sample_data + "-0.280,0.308,-0.359\n"
sample_data = sample_data + "0.258,-0.333,-0.310\n"
sample_data = sample_data + "0.455,0.312,-0.257\n"
sample_data = sample_data + "1.494,1.730,2.184\n"
sample_data = sample_data + "2.000,1.730,2.169\n"
sample_data = sample_data + "1.416,0.674,0.984\n"
sample_data = sample_data + "-0.230,0.333,0.042\n"
sample_data = sample_data + "0.814,0.519,1.214\n"
sample_data = sample_data + "0.923,1.048,0.953\n"
sample_data = sample_data + "2.127,1.492,1.765\n"
sample_data = sample_data + "2.107,2.545,2.101\n"
sample_data = sample_data + "0.985,0.817,0.808\n"
sample_data = sample_data + "1.942,1.772,2.044\n"
sample_data = sample_data + "0.010,0.599,-0.020\n"
sample_data = sample_data + "2.336,1.882,2.291\n"
sample_data = sample_data + "1.733,0.840,0.667\n"
sample_data = sample_data + "0.327,0.029,-0.065\n"
sample_data = sample_data + "1.145,1.099,1.381\n"
sample_data = sample_data + "0.138,-0.156,-0.316\n"
sample_data = sample_data + "0.501,0.693,1.342\n"
sample_data = sample_data + "-0.414,0.029,0.378\n"
sample_data = sample_data + "0.187,0.467,0.232\n"
sample_data = sample_data + "1.478,0.842,0.766\n"
sample_data = sample_data + "2.593,2.115,2.039\n"
sample_data = sample_data + "0.844,1.488,1.157\n"
sample_data = sample_data + "1.161,0.820,1.037\n"
sample_data = sample_data + "1.885,1.982,2.107\n"
sample_data = sample_data + "1.860,1.761,2.245\n"
sample_data = sample_data + "1.709,2.426,2.466\n"
sample_data = sample_data + "-0.403,0.490,-0.234\n"
sample_data = sample_data + "0.401,-0.162,0.238\n"
sample_data = sample_data + "1.699,2.200,2.034\n"
sample_data = sample_data + "1.482,1.235,0.745\n"
sample_data = sample_data + "0.105,-0.190,0.066\n"
sample_data = sample_data + "1.469,1.479,1.061\n"
sample_data = sample_data + "1.771,2.036,2.632\n"
sample_data = sample_data + "-0.026,-0.420,-0.399\n"
sample_data = sample_data + "0.658,1.335,0.911\n"
sample_data = sample_data + "-0.209,0.117,0.300\n"
sample_data = sample_data + "1.967,1.773,2.075\n"
sample_data = sample_data + "1.255,0.519,0.594\n"
sample_data = sample_data + "2.605,2.140,2.119\n"
sample_data = sample_data + "-0.404,-0.282,-0.067\n"
document.getElementById("Real_inp").value = sample_data;
document.frmOne.txtKNumber.value = "6"
}
var options = {legend:{position:"s",noColumns: 4,container: document.getElementById("legendDiv")},selection:{mode:"xy"}};
var series1 = [ [0,0],[1,0],[2,0],[3,0]];
var data = [ series1 ];
$.plot($("#chart0"), data);
function create_data_array()
{
inp_str = document.getElementById("Real_inp").value
inp_arr = inp_str.split("\n")
// Deal with newline after last row, should this occur
var inp_arr_f = inp_arr.filter(function (el) {
return el != "";
})
Nrows = inp_arr_f.length
Ncols = inp_arr_f[0].split(",").length
data_arr = []
for (var ii=0; ii < Nrows; ii++)
{
data_arr[ii] = []
var row_split = inp_arr_f[ii].split(",")
for (var jj =0; jj < Ncols; jj++)
{
data_arr[ii][jj] = parseFloat(row_split[jj])
}
}
return [inp_str, Nrows, Ncols, data_arr]
}
function print_from_data_arr(inp_arr_v)
{
res_str = ""
var Nr = inp_arr_v.length
var Nc = inp_arr_v[0].length
for (ii=0; ii < Nr; ii++)
{
for (jj=0; jj < Nc; jj++)
{
res_str += inp_arr_v[ii][jj].toString() + ", "
}
res_str += "\n"
}
return res_str
}
function calc_distance(sample1, sample2)
{
Nc = sample1.length
res = 0.0
for (var i = 0; i < Nc; i++)
{
res = res + (sample1[i] - sample2[i])*(sample1[i] - sample2[i])
}
return Math.sqrt(res)
}
function find_min_distance(sample, centroid_vec, k)
{
Nc = sample.length
min_dist = 0.0
min_idx = 0
for (var i = 0; i < k; i++)
{
curr_dist = calc_distance(sample, centroid_vec[i])
if (i == 0)
{
min_dist = curr_dist
min_idx = 0
}
else
{
if (curr_dist < min_dist)
{
min_dist = curr_dist
min_idx = i
}
}
}
return [min_dist, min_idx]
}
function create_centroids(data_inp, K)
{
var Nr = data_inp.length
var Nc = data_inp[0].length
var centroid = []
for(var i = 0; i < Nc; i++)
{
centroid[i] = 0.0
}
var centroid_vec = []
for(var i = 0; i < K; i++)
{
centroid_vec[i] = []
for(var j = 0; j < Nc; j++)
{
centroid_vec[i][j] = centroid[j]
}
}
idx = Math.floor(Math.random() * Nr)
centroid_vec[0] = data_inp[idx]
max_dist = 0.0
mix_idx = 0
// Now for the rest of the centroids (1,2,..,,(K-1))
for (var i = 1; i < K; i++)
{
for (var j = 0; j < Nr; j++)
{
sample = data_inp[j]
curr_dist = find_min_distance(sample, centroid_vec, i)[0]
if (j == 0)
{
max_dist = curr_dist
max_idx = j
}
else
{
if (curr_dist > max_dist)
{
max_dist = curr_dist
max_idx = j
}
}
}
centroid_vec[i] = data_inp[max_idx]
}
return centroid_vec
}
function recalculate_centroids(data_inp, labels, K)
{
var Nr = data_inp.length
var Nc = data_inp[0].length
var local_centroid = []
var centroid_count = []
for (var i = 0; i < K; i++)
{
local_centroid[i] = []
centroid_count[i] = 0
for (var j = 0; j < Nc; j++)
{
local_centroid[i][j] = 0.0
}
}
for (var m = 0; m < Nr; m++)
{
for (var n = 0; n < Nc; n++)
{
local_centroid[labels[m]][n] = local_centroid[labels[m]][n]+data_inp[m][n]
}
centroid_count[labels[m]] = centroid_count[labels[m]] + 1
}
for (var p = 0; p < K; p++)
{
for (var q = 0; q < Nc; q++)
{
local_centroid[p][q] = local_centroid[p][q]/(centroid_count[p] + 0.0)
}
}
return local_centroid
}
function delta_centroids(inp1, inp2)
{
var nr = inp1.length
var nc = inp1[0].length
res = 0.0
for (var ii=0; ii < nr; ii++)
{
for (var jj=0; jj < nc; jj++)
{
res = res + ((inp1[ii][jj]-inp2[ii][jj])*(inp1[ii][jj]-inp2[ii][jj]))
}
}
return Math.sqrt(res)
}
function k_means_alg(data_inp, K)
{
var Nr = data_inp.length
var Nc = data_inp[0].length
centroids = create_centroids(data_inp,K)
labels = []
var dc = 1e6
var iterno = 0
var max_iter = 100
var dc_vec = new Array()
while ((dc > 0.0001) && (iterno < max_iter))
{
centroids_old = centroids
for (var i=0; i < Nr; i++)
{
labels[i] = find_min_distance(data_inp[i], centroids, K)[1]
}
centroids = recalculate_centroids(data_inp, labels, K)
dc = delta_centroids(centroids, centroids_old)
dc_vec.push(dc)
iterno = iterno + 1
}
// Calculate mse
var mse = 0.0
for (var xx=0; xx < Nr; xx++)
{
tmpvar = calc_distance(data_inp[xx],centroids[labels[xx]])
mse = mse + (tmpvar * tmpvar)
}
return [centroids, labels, dc_vec, iterno, mse]
}
function clear_real_inp() {
document.getElementById("Real_inp").value = "";
}
function calc_k_means()
{
res_array = create_data_array()
data_arr = res_array[3]
var K = document.frmOne.txtKNumber.value
mse_vec = new Array()
for (var mm = 1; mm < (Number(K)+1); mm++)
{
tmp_cluster_res = k_means_alg(data_arr,mm)
mse_vec.push(tmp_cluster_res[4])
}
k_alg_res = k_means_alg(data_arr,K)
centroids = k_alg_res[0]
labels = k_alg_res[1]
dc = k_alg_res[2]
iterno = k_alg_res[3]
mse = k_alg_res[4]
res_str = print_from_data_arr(data_arr)
dbg_str = ""
dbg_str += res_array[0] + "\n\n"
dbg_str += "Nrows " + res_array[1].toString() + "\n"
dbg_str += "Ncols " + res_array[2].toString() + "\n\n"
dbg_str += res_str
dbg_str += "No of centroids " + centroids.length + "\n"
dbg_str += "Feature length " + centroids[0].length + "\n"
dbg_str += centroids
dbg_str += "\nLabels\n"
dbg_str += labels
dbg_str += "\nDelta Centroids\n"
dbg_str += dc
dbg_str += "\nNum iterations\n"
dbg_str += iterno
centroid_str = ""
data_label_str = ""
for (var hh=0; hh < data_arr.length; hh++)
{
data_label_str += labels[hh] + ": " + data_arr[hh] + "\n"
}
for (var ii=0; ii < centroids.length; ii++)
{
feature_vec = centroids[ii]
feature_vec_str = ""
for (var jj=0; jj < feature_vec.length - 1; jj++)
{
feature_vec_str += feature_vec[jj].toFixed(6) + ","
}
feature_vec_str += feature_vec[feature_vec.length - 1].toFixed(6)
// centroid_str += ii + ": " + centroids[ii] + "\n"
centroid_str += ii + ": " + feature_vec_str + "\n"
}
document.getElementById("Centroid_out").value = centroid_str
document.getElementById("Result_out").value = data_label_str
num_features = centroids[0].length
Nr = res_array[1].toString()
var data2 = new Array();
xm = new Array()
ym = new Array()
zm = new Array()
for (var ii = 0; ii < K; ii++)
{
xm[ii] = new Array()
ym[ii] = new Array()
zm[ii] = new Array()
}
for (var ii = 0; ii < Nr; ii++)
{
if (num_features == 1)
{
xm[labels[ii]].push(data_arr[ii][0])
ym[labels[ii]].push(0.0)
zm[labels[ii]].push(0.0)
}
else if (num_features == 2)
{
xm[labels[ii]].push(data_arr[ii][0])
ym[labels[ii]].push(data_arr[ii][1])
zm[labels[ii]].push(0.0)
}
else if (num_features >= 3)
{
xm[labels[ii]].push(data_arr[ii][0])
ym[labels[ii]].push(data_arr[ii][1])
zm[labels[ii]].push(data_arr[ii][2])
}
}
// Centroid initialisation
xc = new Array()
yc = new Array()
zc = new Array()
for (var ii = 0; ii < K; ii++)
{
xc[ii] = new Array()
yc[ii] = new Array()
zc[ii] = new Array()
}
for (var ii = 0; ii < K; ii++)
{
if (num_features == 1)
{
xc[ii].push(centroids[ii][0])
yc[ii].push(0.0)
zc[ii].push(0.0)
}
else if (num_features == 2)
{
xc[ii].push(centroids[ii][0])
yc[ii].push(centroids[ii][1])
zc[ii].push(0.0)
}
else if (num_features >= 3)
{
xc[ii].push(centroids[ii][0])
yc[ii].push(centroids[ii][1])
zc[ii].push(centroids[ii][2])
}
}
data_to_plot = new Array()
for (var ii=0; ii < mse_vec.length; ii++)
{
data_to_plot.push([ii+1,mse_vec[ii]])
}
data_mse = {
label: "",
data: data_to_plot,
color: "#0000ff",
points: { show: true },
lines: { show: true },
bars: { show: false },
yaxis: 1,
selection: { mode: "xy" }};
data_plot = [data_mse]
$.plot($("#chart0"), data_plot, options);
/*
if (num_features == 1)
{
for (var idx=0; idx < K; idx++)
{
var num1 = Math.round(((K - idx)/K) * 255);
var num2 = Math.round((idx/K) * 255);
var num1hex = num1.toString(16);
var num2hex = num2.toString(16);
if (num1hex.length == 1)
num1hex = "0" + num1hex;
if (num2hex.length == 1)
num2hex = "0" + num2hex;
col_str = "#" + num1hex + num2hex + "00";
var dset = new Array();
var lbl = idx + 1;
tmpstr = "Cluster " + lbl;
for (var t = 0; t < Nr; t++)
{
if (labels[t] == idx )
dset.push([t,data_arr[t][0]]);
}
data2[idx] = {
label: tmpstr,
data: dset,
color: col_str,
points: { show: true },
lines: { show: false },
bars: { show: false },
yaxis: 1,
selection: { mode: "xy" }};
}
$.plot($("#chart0"), data2, options);
}
else if (num_features == 2)
{
for (var idx=0; idx < K; idx++)
{
var num1 = Math.round(((K - idx)/K) * 255);
var num2 = Math.round((idx/K) * 255);
var num1hex = num1.toString(16);
var num2hex = num2.toString(16);
if (num1hex.length == 1)
num1hex = "0" + num1hex;
if (num2hex.length == 1)
num2hex = "0" + num2hex;
col_str = "#" + num1hex + num2hex + "00";
var dset = new Array();
var lbl = idx + 1;
tmpstr = "Cluster " + lbl;
for (var t = 0; t < Nr; t++)
{
if (labels[t] == idx)
dset.push([data_arr[t][0], data_arr[t][1]]);
}
data2[idx] = {
label: tmpstr,
data: dset,
color: col_str,
points: { show: true },
lines: { show: false },
bars: { show: false },
yaxis: 1,
selection: { mode: "xy" }};
}
$.plot($("#chart0"), data2,options);
}
*/
// 3-d plot
}
</script>
Alijah Ahmedhttp://www.blogger.com/profile/01151589526613233918noreply@blogger.comtag:blogger.com,1999:blog-8441309013858171051.post-85971508183010756912020-10-02T12:28:00.114-07:002020-10-03T10:47:34.820-07:00k-means calculator for arbitrary sized vectors<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<script language="Javascript">
/**
* jQuery-csv (jQuery Plugin)
*
* This document is licensed as free software under the terms of the
* MIT License: http://www.opensource.org/licenses/mit-license.php
*
* Acknowledgements:
* The original design and influence to implement this library as a jquery
* plugin is influenced by jquery-json (http://code.google.com/p/jquery-json/).
* If you're looking to use native JSON.Stringify but want additional backwards
* compatibility for browsers that don't support it, I highly recommend you
* check it out.
*
* A special thanks goes out to rwk@acm.org for providing a lot of valuable
* feedback to the project including the core for the new FSM
* (Finite State Machine) parsers. If you're looking for a stable TSV parser
* be sure to take a look at jquery-tsv (http://code.google.com/p/jquery-tsv/).
* For legal purposes I'll include the "NO WARRANTY EXPRESSED OR IMPLIED.
* USE AT YOUR OWN RISK.". Which, in 'layman's terms' means, by using this
* library you are accepting responsibility if it breaks your code.
*
* Legal jargon aside, I will do my best to provide a useful and stable core
* that can effectively be built on.
*
* Copyrighted 2012 by Evan Plaice.
*/
RegExp.escape= function(s) {
return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
};
(function (undefined) {
'use strict';
var $;
// to keep backwards compatibility
if (typeof jQuery !== 'undefined' && jQuery) {
$ = jQuery;
} else {
$ = {};
}
/**
* jQuery.csv.defaults
* Encapsulates the method paramater defaults for the CSV plugin module.
*/
$.csv = {
defaults: {
separator:',',
delimiter:'"',
headers:true
},
hooks: {
castToScalar: function(value, state) {
var hasDot = /\./;
if (isNaN(value)) {
return value;
} else {
if (hasDot.test(value)) {
return parseFloat(value);
} else {
var integer = parseInt(value);
if(isNaN(integer)) {
return null;
} else {
return integer;
}
}
}
}
},
parsers: {
parse: function(csv, options) {
// cache settings
var separator = options.separator;
var delimiter = options.delimiter;
// set initial state if it's missing
if(!options.state.rowNum) {
options.state.rowNum = 1;
}
if(!options.state.colNum) {
options.state.colNum = 1;
}
// clear initial state
var data = [];
var entry = [];
var state = 0;
var value = '';
var exit = false;
function endOfEntry() {
// reset the state
state = 0;
value = '';
// if 'start' hasn't been met, don't output
if(options.start && options.state.rowNum < options.start) {
// update global state
entry = [];
options.state.rowNum++;
options.state.colNum = 1;
return;
}
if(options.onParseEntry === undefined) {
// onParseEntry hook not set
data.push(entry);
} else {
var hookVal = options.onParseEntry(entry, options.state); // onParseEntry Hook
// false skips the row, configurable through a hook
if(hookVal !== false) {
data.push(hookVal);
}
}
//console.log('entry:' + entry);
// cleanup
entry = [];
// if 'end' is met, stop parsing
if(options.end && options.state.rowNum >= options.end) {
exit = true;
}
// update global state
options.state.rowNum++;
options.state.colNum = 1;
}
function endOfValue() {
if(options.onParseValue === undefined) {
// onParseValue hook not set
entry.push(value);
} else {
var hook = options.onParseValue(value, options.state); // onParseValue Hook
// false skips the row, configurable through a hook
if(hook !== false) {
entry.push(hook);
}
}
//console.log('value:' + value);
// reset the state
value = '';
state = 0;
// update global state
options.state.colNum++;
}
// escape regex-specific control chars
var escSeparator = RegExp.escape(separator);
var escDelimiter = RegExp.escape(delimiter);
// compile the regEx str using the custom delimiter/separator
var match = /(D|S|\r\n|\n|\r|[^DS\r\n]+)/;
var matchSrc = match.source;
matchSrc = matchSrc.replace(/S/g, escSeparator);
matchSrc = matchSrc.replace(/D/g, escDelimiter);
match = new RegExp(matchSrc, 'gm');
// put on your fancy pants...
// process control chars individually, use look-ahead on non-control chars
csv.replace(match, function (m0) {
if(exit) {
return;
}
switch (state) {
// the start of a value
case 0:
// null last value
if (m0 === separator) {
value += '';
endOfValue();
break;
}
// opening delimiter
if (m0 === delimiter) {
state = 1;
break;
}
// null last value
if (/^(\r\n|\n|\r)$/.test(m0)) {
endOfValue();
endOfEntry();
break;
}
// un-delimited value
value += m0;
state = 3;
break;
// delimited input
case 1:
// second delimiter? check further
if (m0 === delimiter) {
state = 2;
break;
}
// delimited data
value += m0;
state = 1;
break;
// delimiter found in delimited input
case 2:
// escaped delimiter?
if (m0 === delimiter) {
value += m0;
state = 1;
break;
}
// null value
if (m0 === separator) {
endOfValue();
break;
}
// end of entry
if (/^(\r\n|\n|\r)$/.test(m0)) {
endOfValue();
endOfEntry();
break;
}
// broken paser?
throw new Error('CSVDataError: Illegal State [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
// un-delimited input
case 3:
// null last value
if (m0 === separator) {
endOfValue();
break;
}
// end of entry
if (/^(\r\n|\n|\r)$/.test(m0)) {
endOfValue();
endOfEntry();
break;
}
if (m0 === delimiter) {
// non-compliant data
throw new Error('CSVDataError: Illegal Quote [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
}
// broken parser?
throw new Error('CSVDataError: Illegal Data [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
default:
// shenanigans
throw new Error('CSVDataError: Unknown State [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
}
//console.log('val:' + m0 + ' state:' + state);
});
// submit the last entry
// ignore null last line
if(entry.length !== 0) {
endOfValue();
endOfEntry();
}
return data;
},
// a csv-specific line splitter
splitLines: function(csv, options) {
// cache settings
var separator = options.separator;
var delimiter = options.delimiter;
// set initial state if it's missing
if(!options.state.rowNum) {
options.state.rowNum = 1;
}
// clear initial state
var entries = [];
var state = 0;
var entry = '';
var exit = false;
function endOfLine() {
// reset the state
state = 0;
// if 'start' hasn't been met, don't output
if(options.start && options.state.rowNum < options.start) {
// update global state
entry = '';
options.state.rowNum++;
return;
}
if(options.onParseEntry === undefined) {
// onParseEntry hook not set
entries.push(entry);
} else {
var hookVal = options.onParseEntry(entry, options.state); // onParseEntry Hook
// false skips the row, configurable through a hook
if(hookVal !== false) {
entries.push(hookVal);
}
}
// cleanup
entry = '';
// if 'end' is met, stop parsing
if(options.end && options.state.rowNum >= options.end) {
exit = true;
}
// update global state
options.state.rowNum++;
}
// escape regex-specific control chars
var escSeparator = RegExp.escape(separator);
var escDelimiter = RegExp.escape(delimiter);
// compile the regEx str using the custom delimiter/separator
var match = /(D|S|\n|\r|[^DS\r\n]+)/;
var matchSrc = match.source;
matchSrc = matchSrc.replace(/S/g, escSeparator);
matchSrc = matchSrc.replace(/D/g, escDelimiter);
match = new RegExp(matchSrc, 'gm');
// put on your fancy pants...
// process control chars individually, use look-ahead on non-control chars
csv.replace(match, function (m0) {
if(exit) {
return;
}
switch (state) {
// the start of a value/entry
case 0:
// null value
if (m0 === separator) {
entry += m0;
state = 0;
break;
}
// opening delimiter
if (m0 === delimiter) {
entry += m0;
state = 1;
break;
}
// end of line
if (m0 === '\n') {
endOfLine();
break;
}
// phantom carriage return
if (/^\r$/.test(m0)) {
break;
}
// un-delimit value
entry += m0;
state = 3;
break;
// delimited input
case 1:
// second delimiter? check further
if (m0 === delimiter) {
entry += m0;
state = 2;
break;
}
// delimited data
entry += m0;
state = 1;
break;
// delimiter found in delimited input
case 2:
// escaped delimiter?
var prevChar = entry.substr(entry.length - 1);
if (m0 === delimiter && prevChar === delimiter) {
entry += m0;
state = 1;
break;
}
// end of value
if (m0 === separator) {
entry += m0;
state = 0;
break;
}
// end of line
if (m0 === '\n') {
endOfLine();
break;
}
// phantom carriage return
if (m0 === '\r') {
break;
}
// broken paser?
throw new Error('CSVDataError: Illegal state [Row:' + options.state.rowNum + ']');
// un-delimited input
case 3:
// null value
if (m0 === separator) {
entry += m0;
state = 0;
break;
}
// end of line
if (m0 === '\n') {
endOfLine();
break;
}
// phantom carriage return
if (m0 === '\r') {
break;
}
// non-compliant data
if (m0 === delimiter) {
throw new Error('CSVDataError: Illegal quote [Row:' + options.state.rowNum + ']');
}
// broken parser?
throw new Error('CSVDataError: Illegal state [Row:' + options.state.rowNum + ']');
default:
// shenanigans
throw new Error('CSVDataError: Unknown state [Row:' + options.state.rowNum + ']');
}
//console.log('val:' + m0 + ' state:' + state);
});
// submit the last entry
// ignore null last line
if(entry !== '') {
endOfLine();
}
return entries;
},
// a csv entry parser
parseEntry: function(csv, options) {
// cache settings
var separator = options.separator;
var delimiter = options.delimiter;
// set initial state if it's missing
if(!options.state.rowNum) {
options.state.rowNum = 1;
}
if(!options.state.colNum) {
options.state.colNum = 1;
}
// clear initial state
var entry = [];
var state = 0;
var value = '';
function endOfValue() {
if(options.onParseValue === undefined) {
// onParseValue hook not set
entry.push(value);
} else {
var hook = options.onParseValue(value, options.state); // onParseValue Hook
// false skips the value, configurable through a hook
if(hook !== false) {
entry.push(hook);
}
}
// reset the state
value = '';
state = 0;
// update global state
options.state.colNum++;
}
// checked for a cached regEx first
if(!options.match) {
// escape regex-specific control chars
var escSeparator = RegExp.escape(separator);
var escDelimiter = RegExp.escape(delimiter);
// compile the regEx str using the custom delimiter/separator
var match = /(D|S|\n|\r|[^DS\r\n]+)/;
var matchSrc = match.source;
matchSrc = matchSrc.replace(/S/g, escSeparator);
matchSrc = matchSrc.replace(/D/g, escDelimiter);
options.match = new RegExp(matchSrc, 'gm');
}
// put on your fancy pants...
// process control chars individually, use look-ahead on non-control chars
csv.replace(options.match, function (m0) {
switch (state) {
// the start of a value
case 0:
// null last value
if (m0 === separator) {
value += '';
endOfValue();
break;
}
// opening delimiter
if (m0 === delimiter) {
state = 1;
break;
}
// skip un-delimited new-lines
if (m0 === '\n' || m0 === '\r') {
break;
}
// un-delimited value
value += m0;
state = 3;
break;
// delimited input
case 1:
// second delimiter? check further
if (m0 === delimiter) {
state = 2;
break;
}
// delimited data
value += m0;
state = 1;
break;
// delimiter found in delimited input
case 2:
// escaped delimiter?
if (m0 === delimiter) {
value += m0;
state = 1;
break;
}
// null value
if (m0 === separator) {
endOfValue();
break;
}
// skip un-delimited new-lines
if (m0 === '\n' || m0 === '\r') {
break;
}
// broken paser?
throw new Error('CSVDataError: Illegal State [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
// un-delimited input
case 3:
// null last value
if (m0 === separator) {
endOfValue();
break;
}
// skip un-delimited new-lines
if (m0 === '\n' || m0 === '\r') {
break;
}
// non-compliant data
if (m0 === delimiter) {
throw new Error('CSVDataError: Illegal Quote [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
}
// broken parser?
throw new Error('CSVDataError: Illegal Data [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
default:
// shenanigans
throw new Error('CSVDataError: Unknown State [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
}
//console.log('val:' + m0 + ' state:' + state);
});
// submit the last value
endOfValue();
return entry;
}
},
helpers: {
/**
* $.csv.helpers.collectPropertyNames(objectsArray)
* Collects all unique property names from all passed objects.
*
* @param {Array} objects Objects to collect properties from.
*
* Returns an array of property names (array will be empty,
* if objects have no own properties).
*/
collectPropertyNames: function (objects) {
var o, propName, props = [];
for (o in objects) {
for (propName in objects[o]) {
if ((objects[o].hasOwnProperty(propName)) &&
(props.indexOf(propName) < 0) &&
(typeof objects[o][propName] !== 'function')) {
props.push(propName);
}
}
}
return props;
}
},
/**
* $.csv.toArray(csv)
* Converts a CSV entry string to a javascript array.
*
* @param {Array} csv The string containing the CSV data.
* @param {Object} [options] An object containing user-defined options.
* @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
* @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
*
* This method deals with simple CSV strings only. It's useful if you only
* need to parse a single entry. If you need to parse more than one line,
* use $.csv2Array instead.
*/
toArray: function(csv, options, callback) {
options = (options !== undefined ? options : {});
var config = {};
config.callback = ((callback !== undefined && typeof(callback) === 'function') ? callback : false);
config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator;
config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter;
var state = (options.state !== undefined ? options.state : {});
// setup
options = {
delimiter: config.delimiter,
separator: config.separator,
onParseEntry: options.onParseEntry,
onParseValue: options.onParseValue,
state: state
};
var entry = $.csv.parsers.parseEntry(csv, options);
// push the value to a callback if one is defined
if(!config.callback) {
return entry;
} else {
config.callback('', entry);
}
},
/**
* $.csv.toArrays(csv)
* Converts a CSV string to a javascript array.
*
* @param {String} csv The string containing the raw CSV data.
* @param {Object} [options] An object containing user-defined options.
* @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
* @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
*
* This method deals with multi-line CSV. The breakdown is simple. The first
* dimension of the array represents the line (or entry/row) while the second
* dimension contains the values (or values/columns).
*/
toArrays: function(csv, options, callback) {
options = (options !== undefined ? options : {});
var config = {};
config.callback = ((callback !== undefined && typeof(callback) === 'function') ? callback : false);
config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator;
config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter;
// setup
var data = [];
options = {
delimiter: config.delimiter,
separator: config.separator,
onPreParse: options.onPreParse,
onParseEntry: options.onParseEntry,
onParseValue: options.onParseValue,
onPostParse: options.onPostParse,
start: options.start,
end: options.end,
state: {
rowNum: 1,
colNum: 1
}
};
// onPreParse hook
if(options.onPreParse !== undefined) {
options.onPreParse(csv, options.state);
}
// parse the data
data = $.csv.parsers.parse(csv, options);
// onPostParse hook
if(options.onPostParse !== undefined) {
options.onPostParse(data, options.state);
}
// push the value to a callback if one is defined
if(!config.callback) {
return data;
} else {
config.callback('', data);
}
},
/**
* $.csv.toObjects(csv)
* Converts a CSV string to a javascript object.
* @param {String} csv The string containing the raw CSV data.
* @param {Object} [options] An object containing user-defined options.
* @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
* @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
* @param {Boolean} [headers] Indicates whether the data contains a header line. Defaults to true.
*
* This method deals with multi-line CSV strings. Where the headers line is
* used as the key for each value per entry.
*/
toObjects: function(csv, options, callback) {
options = (options !== undefined ? options : {});
var config = {};
config.callback = ((callback !== undefined && typeof(callback) === 'function') ? callback : false);
config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator;
config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter;
config.headers = 'headers' in options ? options.headers : $.csv.defaults.headers;
options.start = 'start' in options ? options.start : 1;
// account for headers
if(config.headers) {
options.start++;
}
if(options.end && config.headers) {
options.end++;
}
// setup
var lines = [];
var data = [];
options = {
delimiter: config.delimiter,
separator: config.separator,
onPreParse: options.onPreParse,
onParseEntry: options.onParseEntry,
onParseValue: options.onParseValue,
onPostParse: options.onPostParse,
start: options.start,
end: options.end,
state: {
rowNum: 1,
colNum: 1
},
match: false,
transform: options.transform
};
// fetch the headers
var headerOptions = {
delimiter: config.delimiter,
separator: config.separator,
start: 1,
end: 1,
state: {
rowNum:1,
colNum:1
}
};
// onPreParse hook
if(options.onPreParse !== undefined) {
options.onPreParse(csv, options.state);
}
// parse the csv
var headerLine = $.csv.parsers.splitLines(csv, headerOptions);
var headers = $.csv.toArray(headerLine[0], options);
// fetch the data
lines = $.csv.parsers.splitLines(csv, options);
// reset the state for re-use
options.state.colNum = 1;
if(headers){
options.state.rowNum = 2;
} else {
options.state.rowNum = 1;
}
// convert data to objects
for(var i=0, len=lines.length; i<len; i++) {
var entry = $.csv.toArray(lines[i], options);
var object = {};
for(var j=0; j <headers.length; j++) {
object[headers[j]] = entry[j];
}
if (options.transform !== undefined) {
data.push(options.transform.call(undefined, object));
} else {
data.push(object);
}
// update row state
options.state.rowNum++;
}
// onPostParse hook
if(options.onPostParse !== undefined) {
options.onPostParse(data, options.state);
}
// push the value to a callback if one is defined
if(!config.callback) {
return data;
} else {
config.callback('', data);
}
},
/**
* $.csv.fromArrays(arrays)
* Converts a javascript array to a CSV String.
*
* @param {Array} arrays An array containing an array of CSV entries.
* @param {Object} [options] An object containing user-defined options.
* @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
* @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
*
* This method generates a CSV file from an array of arrays (representing entries).
*/
fromArrays: function(arrays, options, callback) {
options = (options !== undefined ? options : {});
var config = {};
config.callback = ((callback !== undefined && typeof(callback) === 'function') ? callback : false);
config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator;
config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter;
var output = '',
line,
lineValues,
i, j;
for (i = 0; i < arrays.length; i++) {
line = arrays[i];
lineValues = [];
for (j = 0; j < line.length; j++) {
var strValue = (line[j] === undefined || line[j] === null) ? '' : line[j].toString();
if (strValue.indexOf(config.delimiter) > -1) {
strValue = strValue.replace(config.delimiter, config.delimiter + config.delimiter);
}
var escMatcher = '\n|\r|S|D';
escMatcher = escMatcher.replace('S', config.separator);
escMatcher = escMatcher.replace('D', config.delimiter);
if (strValue.search(escMatcher) > -1) {
strValue = config.delimiter + strValue + config.delimiter;
}
lineValues.push(strValue);
}
output += lineValues.join(config.separator) + '\r\n';
}
// push the value to a callback if one is defined
if(!config.callback) {
return output;
} else {
config.callback('', output);
}
},
/**
* $.csv.fromObjects(objects)
* Converts a javascript dictionary to a CSV string.
*
* @param {Object} objects An array of objects containing the data.
* @param {Object} [options] An object containing user-defined options.
* @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
* @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
* @param {Character} [sortOrder] Sort order of columns (named after
* object properties). Use 'alpha' for alphabetic. Default is 'declare',
* which means, that properties will _probably_ appear in order they were
* declared for the object. But without any guarantee.
* @param {Character or Array} [manualOrder] Manually order columns. May be
* a strin in a same csv format as an output or an array of header names
* (array items won't be parsed). All the properties, not present in
* `manualOrder` will be appended to the end in accordance with `sortOrder`
* option. So the `manualOrder` always takes preference, if present.
*
* This method generates a CSV file from an array of objects (name:value pairs).
* It starts by detecting the headers and adding them as the first line of
* the CSV file, followed by a structured dump of the data.
*/
fromObjects: function(objects, options, callback) {
options = (options !== undefined ? options : {});
var config = {};
config.callback = ((callback !== undefined && typeof(callback) === 'function') ? callback : false);
config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator;
config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter;
config.headers = 'headers' in options ? options.headers : $.csv.defaults.headers;
config.sortOrder = 'sortOrder' in options ? options.sortOrder : 'declare';
config.manualOrder = 'manualOrder' in options ? options.manualOrder : [];
config.transform = options.transform;
if (typeof config.manualOrder === 'string') {
config.manualOrder = $.csv.toArray(config.manualOrder, config);
}
if (config.transform !== undefined) {
var origObjects = objects;
objects = [];
var i;
for (i = 0; i < origObjects.length; i++) {
objects.push(config.transform.call(undefined, origObjects[i]));
}
}
var props = $.csv.helpers.collectPropertyNames(objects);
if (config.sortOrder === 'alpha') {
props.sort();
} // else {} - nothing to do for 'declare' order
if (config.manualOrder.length > 0) {
var propsManual = [].concat(config.manualOrder);
var p;
for (p = 0; p < props.length; p++) {
if (propsManual.indexOf( props[p] ) < 0) {
propsManual.push( props[p] );
}
}
props = propsManual;
}
var o, p, line, output = [], propName;
if (config.headers) {
output.push(props);
}
for (o = 0; o < objects.length; o++) {
line = [];
for (p = 0; p < props.length; p++) {
propName = props[p];
if (propName in objects[o] && typeof objects[o][propName] !== 'function') {
line.push(objects[o][propName]);
} else {
line.push('');
}
}
output.push(line);
}
// push the value to a callback if one is defined
return $.csv.fromArrays(output, options, config.callback);
}
};
// Maintenance code to maintain backward-compatibility
// Will be removed in release 1.0
$.csvEntry2Array = $.csv.toArray;
$.csv2Array = $.csv.toArrays;
$.csv2Dictionary = $.csv.toObjects;
// CommonJS module is defined
if (typeof module !== 'undefined' && module.exports) {
module.exports = $.csv;
}
}).call( this );
</script>
<script>
$(document).ready(function() {
if(isAPIAvailable()) {
$('#files').bind('change', handleFileSelect);
}
});
function isAPIAvailable() {
// Check for the various File API support.
if (window.File && window.FileReader && window.FileList && window.Blob) {
// Great success! All the File APIs are supported.
return true;
} else {
// source: File API availability - http://caniuse.com/#feat=fileapi
// source: <output> availability - http://html5doctor.com/the-output-element/
document.writeln('The HTML5 APIs used in this form are only available in the following browsers:<br />');
// 6.0 File API & 13.0 <output>
document.writeln(' - Google Chrome: 13.0 or later<br />');
// 3.6 File API & 6.0 <output>
document.writeln(' - Mozilla Firefox: 6.0 or later<br />');
// 10.0 File API & 10.0 <output>
document.writeln(' - Internet Explorer: Not supported (partial support expected in 10.0)<br />');
// ? File API & 5.1 <output>
document.writeln(' - Safari: Not supported<br />');
// ? File API & 9.2 <output>
document.writeln(' - Opera: Not supported');
return false;
}
}
function handleFileSelect(evt) {
var files = evt.target.files; // FileList object
var file = files[0];
// read the file metadata
var output = ''
output += '<span style="font-weight:bold;">' + escape(file.name) + '</span><br />\n';
output += ' - FileType: ' + (file.type || 'n/a') + '<br />\n';
output += ' - FileSize: ' + file.size + ' bytes<br />\n';
output += ' - LastModified: ' + (file.lastModifiedDate ? file.lastModifiedDate.toLocaleDateString() : 'n/a') + '<br />\n';
// read the file contents
printTextArea(file);
// post the results
$('#list').append(output);
}
function printTextArea(file) {
var reader = new FileReader();
reader.readAsText(file);
reader.onload = function(event){
var csv = event.target.result;
var data = $.csv.toArrays(csv);
var mystr = "";
for(var row in data) {
vec_len = data[row].length
for (var ii = 0; ii < vec_len - 1; ii++)
{
if (!isNaN(Number(data[row][ii])))
{
mystr = mystr.concat((Number(data[row][ii]).toFixed(6)).toString());
mystr = mystr.concat(",");
}
}
mystr = mystr.concat((Number(data[row][ii]).toFixed(6)).toString());
mystr = mystr.concat("\n");
/*
if (!isNaN(Number(data[row][0])))
{
mystr = mystr.concat((Number(data[row][0]).toFixed(3)).toString());
}
if (!isNaN(Number(data[row][1])))
{
mystr = mystr.concat(",");
mystr = mystr.concat((Number(data[row][1]).toFixed(3)).toString());
mystr = mystr.concat("\n");
}
else
{
mystr = mystr.concat("\n");
}
*/
}
mystr = mystr.substring(0,mystr.length - 1);
document.getElementById("Real_inp").value = mystr;
};
reader.onerror = function(){ alert('Unable to read ' + file.fileName); };
}
</script>
<!--[if IE ]>
<script type="text/javascript" src="https://firebasestorage.googleapis.com/v0/b/js-static-assets.appspot.com/o/excanvas_min.js?alt=media&token=9dd78ad2-f09b-45dd-8a09-8db2b2546d1d"></script>
<![endif]-->
<script src="https://firebasestorage.googleapis.com/v0/b/js-static-assets.appspot.com/o/jquery_flot.js?alt=media&token=bde69a50-3df9-4794-b7d8-dd4e840472cd" type="text/javascript"></script>
<p style="text-align:justify">
This blog post implements a basic k-means clustering algorithm, which can be applied to arbitrary sized vectors. The clusters will be displayed graphically using a 3d plot once the calculation button is clicked (using a great library from <a href="https://plotly.com/javascript/">Plotly</a>, where you can zoom and turn the plot amongst other handy capabilities). This will be useful for visualising the spatial clustering of vectors of size 1 to 3 inclusive - the centroids are higlighted in black, while each cluster has its own colour.</p>
<p style="text-align:justify">
The cluster centres (or centroids) are initialised using a variant of the k-means++ algorithm as proposed by David Arthur and Sergei Vassilvitski in 2007.</p>
<p style="text-align:justify">
Please enter lines of comma separated numbers in the text areas below - after the last number in each line there must be no trailing comma. In addition, there must be no new line after the last sample.</p>
<p style="text-align:justify">
Alternatively you can choose to load a CSV file, that must contain only comma separated numbers.</p>
<p style="text-align:justify">
To perform the k-means clustering, please enter the number of clusters in the appropriate field, then press the button labelled "Perform k-means clustering" below - the results will populate the textareas below labelled "Label and data sample" and "Label and Centroid values". In addition a 3-d graph plot will appear.</p>
<p style="text-align:justify">
Note that the k-means algorithm can converge to a local minimum, and also exhibit degeneracy, whereby one of the clusters has no members, although the kmeans++ initialisation should somewhat mitigate this. Should these scenarios occur, simply re-run the algorithm, by clicking on the button labelled "Perform k-means clustering".</p>
<p style="text-align:justify">
An example three-dimensional dataset has been loaded, with three clusters of 200 samples, as guidance.
</p>
<br />
<div class="clearfix" id="inputs">
<input id="files" name="files[]" single="" type="file" />
</div>
<br />
<div style="float: left; width: 200px;">
Input
</div>
<br />
<textarea cols="25" id="Real_inp" rows="15"></textarea>
<br />
<div style="float: left; width: 200px;">
<input name="b1" onclick="clear_real_inp()" style="margin-left: 0px;" type="Button" value="Clear Input" />
</div>
<br>
<br>
<form name="frmOne">
Enter number of clusters (k value):-<br>
<input name="txtKNumber" size="30" type="TextArea" value="" />
<br>
<br>
<input name="b1" onclick="calc_k_means()" style="margin-left: 0px;" type="Button" value="Perform k-means clustering" />
<br />
<br />
<div style="float: left; width: 200px;">
Label and data sample:-
</div>
<div style="float: left; width: 200px;">
Label and Centroid values:-
</div>
<br>
<textarea cols="25" id="Result_out" rows="10"></textarea>
<textarea cols="25" id="Centroid_out" rows="10"></textarea>
<br>
<br>
<body>
<div id='myDiv'><!-- Plotly chart will be drawn inside this DIV --></div>
</body>
<!-- Load plotly.js into the DOM -->
<script src='https://cdn.plot.ly/plotly-latest.min.js'></script>
<script language="JavaScript">
// Load an example dataset
window.onload = set_inputs();
function set_inputs() {
var sample_data = ""
sample_data = sample_data + "2.015,2.150,1.701\n"
sample_data = sample_data + "2.208,1.875,1.525\n"
sample_data = sample_data + "0.806,1.180,1.100\n"
sample_data = sample_data + "1.656,2.186,1.974\n"
sample_data = sample_data + "1.128,1.100,0.653\n"
sample_data = sample_data + "0.105,-0.182,0.464\n"
sample_data = sample_data + "1.217,1.014,0.705\n"
sample_data = sample_data + "2.016,2.048,1.637\n"
sample_data = sample_data + "2.667,2.118,2.508\n"
sample_data = sample_data + "0.666,1.491,0.592\n"
sample_data = sample_data + "-0.195,0.163,0.014\n"
sample_data = sample_data + "0.293,0.668,1.251\n"
sample_data = sample_data + "2.626,2.274,1.917\n"
sample_data = sample_data + "1.239,0.657,1.153\n"
sample_data = sample_data + "1.596,1.997,1.961\n"
sample_data = sample_data + "0.241,-0.091,0.361\n"
sample_data = sample_data + "0.941,1.251,1.236\n"
sample_data = sample_data + "1.448,2.011,2.011\n"
sample_data = sample_data + "-0.234,0.054,-0.437\n"
sample_data = sample_data + "0.167,0.153,0.090\n"
sample_data = sample_data + "2.743,2.106,2.020\n"
sample_data = sample_data + "-0.220,0.089,-0.289\n"
sample_data = sample_data + "1.382,0.806,1.048\n"
sample_data = sample_data + "2.597,2.349,2.073\n"
sample_data = sample_data + "1.414,0.984,1.239\n"
sample_data = sample_data + "2.006,1.728,2.129\n"
sample_data = sample_data + "2.280,1.896,1.671\n"
sample_data = sample_data + "0.842,0.286,0.818\n"
sample_data = sample_data + "1.677,2.607,1.831\n"
sample_data = sample_data + "1.537,2.261,1.947\n"
sample_data = sample_data + "1.015,1.057,1.063\n"
sample_data = sample_data + "0.888,1.286,1.157\n"
sample_data = sample_data + "1.851,1.949,1.717\n"
sample_data = sample_data + "0.084,0.221,0.195\n"
sample_data = sample_data + "0.184,-0.038,0.474\n"
sample_data = sample_data + "0.938,1.009,0.899\n"
sample_data = sample_data + "1.024,0.928,1.610\n"
sample_data = sample_data + "1.128,0.495,0.870\n"
sample_data = sample_data + "1.575,2.264,2.412\n"
sample_data = sample_data + "1.865,2.184,1.937\n"
sample_data = sample_data + "2.122,2.520,1.895\n"
sample_data = sample_data + "-0.370,0.215,0.155\n"
sample_data = sample_data + "1.202,1.308,0.796\n"
sample_data = sample_data + "0.410,1.312,0.877\n"
sample_data = sample_data + "1.638,1.650,2.339\n"
sample_data = sample_data + "0.054,0.091,0.255\n"
sample_data = sample_data + "0.797,0.662,0.744\n"
sample_data = sample_data + "-0.331,-0.087,-0.267\n"
sample_data = sample_data + "2.012,1.856,1.976\n"
sample_data = sample_data + "0.195,0.420,0.467\n"
sample_data = sample_data + "2.034,2.064,1.847\n"
sample_data = sample_data + "0.302,0.460,-0.178\n"
sample_data = sample_data + "2.229,1.901,1.949\n"
sample_data = sample_data + "0.055,-0.120,0.269\n"
sample_data = sample_data + "-0.449,-0.209,-0.036\n"
sample_data = sample_data + "-0.313,0.108,-0.454\n"
sample_data = sample_data + "2.017,1.568,1.956\n"
sample_data = sample_data + "2.152,2.661,2.323\n"
sample_data = sample_data + "0.485,0.020,0.635\n"
sample_data = sample_data + "0.928,0.924,0.615\n"
sample_data = sample_data + "1.067,1.316,0.810\n"
sample_data = sample_data + "0.889,0.861,0.901\n"
sample_data = sample_data + "-0.177,-0.405,0.414\n"
sample_data = sample_data + "1.408,1.722,2.103\n"
sample_data = sample_data + "0.303,-0.063,-0.085\n"
sample_data = sample_data + "2.015,1.922,2.263\n"
sample_data = sample_data + "1.855,1.704,1.858\n"
sample_data = sample_data + "0.029,0.369,0.480\n"
sample_data = sample_data + "1.880,1.561,1.585\n"
sample_data = sample_data + "0.638,-0.121,0.137\n"
sample_data = sample_data + "0.575,-0.204,-0.058\n"
sample_data = sample_data + "0.826,0.671,1.240\n"
sample_data = sample_data + "1.320,0.438,1.295\n"
sample_data = sample_data + "1.929,2.055,2.059\n"
sample_data = sample_data + "-0.392,-0.093,-0.297\n"
sample_data = sample_data + "2.575,2.656,2.025\n"
sample_data = sample_data + "0.646,1.347,0.943\n"
sample_data = sample_data + "0.524,0.591,0.502\n"
sample_data = sample_data + "0.881,0.701,0.801\n"
sample_data = sample_data + "1.971,1.723,2.358\n"
sample_data = sample_data + "0.197,-0.010,-0.120\n"
sample_data = sample_data + "1.195,1.414,0.421\n"
sample_data = sample_data + "0.070,-0.090,-0.223\n"
sample_data = sample_data + "-0.102,0.040,0.561\n"
sample_data = sample_data + "2.017,1.871,1.632\n"
sample_data = sample_data + "2.232,1.774,1.966\n"
sample_data = sample_data + "1.214,1.016,0.884\n"
sample_data = sample_data + "1.948,2.270,1.994\n"
sample_data = sample_data + "0.073,0.127,-0.100\n"
sample_data = sample_data + "1.098,0.917,1.340\n"
sample_data = sample_data + "0.151,-0.519,-0.062\n"
sample_data = sample_data + "1.333,0.936,0.873\n"
sample_data = sample_data + "2.298,1.675,2.020\n"
sample_data = sample_data + "0.177,0.256,-0.310\n"
sample_data = sample_data + "0.626,1.448,0.766\n"
sample_data = sample_data + "1.973,2.729,1.860\n"
sample_data = sample_data + "-0.661,0.836,0.304\n"
sample_data = sample_data + "2.108,2.833,1.977\n"
sample_data = sample_data + "1.434,1.004,0.917\n"
sample_data = sample_data + "1.190,1.265,1.385\n"
sample_data = sample_data + "2.358,1.850,1.976\n"
sample_data = sample_data + "2.581,2.183,2.029\n"
sample_data = sample_data + "2.404,2.420,2.387\n"
sample_data = sample_data + "1.836,1.885,2.226\n"
sample_data = sample_data + "1.358,0.641,0.863\n"
sample_data = sample_data + "-0.069,0.302,-0.325\n"
sample_data = sample_data + "1.811,1.027,1.186\n"
sample_data = sample_data + "0.669,0.189,0.227\n"
sample_data = sample_data + "-0.052,0.143,-0.315\n"
sample_data = sample_data + "-0.021,0.013,-0.295\n"
sample_data = sample_data + "1.304,1.138,0.444\n"
sample_data = sample_data + "1.239,0.725,1.177\n"
sample_data = sample_data + "-0.238,-0.105,0.128\n"
sample_data = sample_data + "1.584,0.549,0.899\n"
sample_data = sample_data + "0.900,1.331,0.929\n"
sample_data = sample_data + "1.128,1.403,1.233\n"
sample_data = sample_data + "0.878,0.873,0.923\n"
sample_data = sample_data + "2.050,1.746,1.987\n"
sample_data = sample_data + "2.176,1.863,1.860\n"
sample_data = sample_data + "1.089,1.243,0.601\n"
sample_data = sample_data + "-0.293,0.388,-0.005\n"
sample_data = sample_data + "1.672,2.038,2.477\n"
sample_data = sample_data + "0.817,0.714,0.927\n"
sample_data = sample_data + "0.479,-0.196,0.160\n"
sample_data = sample_data + "1.302,1.187,0.898\n"
sample_data = sample_data + "0.196,0.051,0.244\n"
sample_data = sample_data + "1.118,1.043,0.481\n"
sample_data = sample_data + "0.194,-0.034,-0.621\n"
sample_data = sample_data + "-0.013,0.089,-0.035\n"
sample_data = sample_data + "1.269,1.783,1.712\n"
sample_data = sample_data + "1.905,1.312,2.085\n"
sample_data = sample_data + "2.160,2.172,1.984\n"
sample_data = sample_data + "0.971,1.302,1.136\n"
sample_data = sample_data + "1.876,2.090,2.209\n"
sample_data = sample_data + "1.360,1.141,0.767\n"
sample_data = sample_data + "1.855,2.068,2.094\n"
sample_data = sample_data + "0.306,-0.559,-0.211\n"
sample_data = sample_data + "1.659,2.325,2.232\n"
sample_data = sample_data + "1.102,1.244,0.834\n"
sample_data = sample_data + "1.160,1.022,1.032\n"
sample_data = sample_data + "-0.033,0.289,0.321\n"
sample_data = sample_data + "0.578,0.847,1.028\n"
sample_data = sample_data + "1.175,0.524,0.728\n"
sample_data = sample_data + "0.911,1.524,0.745\n"
sample_data = sample_data + "-0.114,-0.007,-0.028\n"
sample_data = sample_data + "1.049,0.994,0.946\n"
sample_data = sample_data + "2.087,2.084,2.115\n"
sample_data = sample_data + "1.121,0.655,1.054\n"
sample_data = sample_data + "1.048,1.235,1.208\n"
sample_data = sample_data + "2.464,1.766,1.964\n"
sample_data = sample_data + "1.067,0.816,0.923\n"
sample_data = sample_data + "0.894,0.862,1.681\n"
sample_data = sample_data + "-0.019,0.442,-0.250\n"
sample_data = sample_data + "0.088,-0.183,0.106\n"
sample_data = sample_data + "1.501,1.584,1.248\n"
sample_data = sample_data + "0.143,0.009,0.123\n"
sample_data = sample_data + "1.416,0.526,0.918\n"
sample_data = sample_data + "-0.280,0.308,-0.359\n"
sample_data = sample_data + "0.258,-0.333,-0.310\n"
sample_data = sample_data + "0.455,0.312,-0.257\n"
sample_data = sample_data + "1.494,1.730,2.184\n"
sample_data = sample_data + "2.000,1.730,2.169\n"
sample_data = sample_data + "1.416,0.674,0.984\n"
sample_data = sample_data + "-0.230,0.333,0.042\n"
sample_data = sample_data + "0.814,0.519,1.214\n"
sample_data = sample_data + "0.923,1.048,0.953\n"
sample_data = sample_data + "2.127,1.492,1.765\n"
sample_data = sample_data + "2.107,2.545,2.101\n"
sample_data = sample_data + "0.985,0.817,0.808\n"
sample_data = sample_data + "1.942,1.772,2.044\n"
sample_data = sample_data + "0.010,0.599,-0.020\n"
sample_data = sample_data + "2.336,1.882,2.291\n"
sample_data = sample_data + "1.733,0.840,0.667\n"
sample_data = sample_data + "0.327,0.029,-0.065\n"
sample_data = sample_data + "1.145,1.099,1.381\n"
sample_data = sample_data + "0.138,-0.156,-0.316\n"
sample_data = sample_data + "0.501,0.693,1.342\n"
sample_data = sample_data + "-0.414,0.029,0.378\n"
sample_data = sample_data + "0.187,0.467,0.232\n"
sample_data = sample_data + "1.478,0.842,0.766\n"
sample_data = sample_data + "2.593,2.115,2.039\n"
sample_data = sample_data + "0.844,1.488,1.157\n"
sample_data = sample_data + "1.161,0.820,1.037\n"
sample_data = sample_data + "1.885,1.982,2.107\n"
sample_data = sample_data + "1.860,1.761,2.245\n"
sample_data = sample_data + "1.709,2.426,2.466\n"
sample_data = sample_data + "-0.403,0.490,-0.234\n"
sample_data = sample_data + "0.401,-0.162,0.238\n"
sample_data = sample_data + "1.699,2.200,2.034\n"
sample_data = sample_data + "1.482,1.235,0.745\n"
sample_data = sample_data + "0.105,-0.190,0.066\n"
sample_data = sample_data + "1.469,1.479,1.061\n"
sample_data = sample_data + "1.771,2.036,2.632\n"
sample_data = sample_data + "-0.026,-0.420,-0.399\n"
sample_data = sample_data + "0.658,1.335,0.911\n"
sample_data = sample_data + "-0.209,0.117,0.300\n"
sample_data = sample_data + "1.967,1.773,2.075\n"
sample_data = sample_data + "1.255,0.519,0.594\n"
sample_data = sample_data + "2.605,2.140,2.119\n"
sample_data = sample_data + "-0.404,-0.282,-0.067\n"
document.getElementById("Real_inp").value = sample_data;
document.frmOne.txtKNumber.value = "3"
}
function create_data_array()
{
inp_str = document.getElementById("Real_inp").value
inp_arr = inp_str.split("\n")
// Deal with newline after last row, should this occur
var inp_arr_f = inp_arr.filter(function (el) {
return el != "";
})
Nrows = inp_arr_f.length
Ncols = inp_arr_f[0].split(",").length
data_arr = []
for (var ii=0; ii < Nrows; ii++)
{
data_arr[ii] = []
var row_split = inp_arr_f[ii].split(",")
for (var jj =0; jj < Ncols; jj++)
{
data_arr[ii][jj] = parseFloat(row_split[jj])
}
}
return [inp_str, Nrows, Ncols, data_arr]
}
function print_from_data_arr(inp_arr_v)
{
res_str = ""
var Nr = inp_arr_v.length
var Nc = inp_arr_v[0].length
for (ii=0; ii < Nr; ii++)
{
for (jj=0; jj < Nc; jj++)
{
res_str += inp_arr_v[ii][jj].toString() + ", "
}
res_str += "\n"
}
return res_str
}
function calc_distance(sample1, sample2)
{
Nc = sample1.length
res = 0.0
for (var i = 0; i < Nc; i++)
{
res = res + (sample1[i] - sample2[i])*(sample1[i] - sample2[i])
}
return Math.sqrt(res)
}
function find_min_distance(sample, centroid_vec, k)
{
Nc = sample.length
min_dist = 0.0
min_idx = 0
for (var i = 0; i < k; i++)
{
curr_dist = calc_distance(sample, centroid_vec[i])
if (i == 0)
{
min_dist = curr_dist
min_idx = 0
}
else
{
if (curr_dist < min_dist)
{
min_dist = curr_dist
min_idx = i
}
}
}
return [min_dist, min_idx]
}
function create_centroids(data_inp, K)
{
var Nr = data_inp.length
var Nc = data_inp[0].length
var centroid = []
for(var i = 0; i < Nc; i++)
{
centroid[i] = 0.0
}
var centroid_vec = []
for(var i = 0; i < K; i++)
{
centroid_vec[i] = []
for(var j = 0; j < Nc; j++)
{
centroid_vec[i][j] = centroid[j]
}
}
idx = Math.floor(Math.random() * Nr)
centroid_vec[0] = data_inp[idx]
max_dist = 0.0
mix_idx = 0
// Now for the rest of the centroids (1,2,..,,(K-1))
for (var i = 1; i < K; i++)
{
for (var j = 0; j < Nr; j++)
{
sample = data_inp[j]
curr_dist = find_min_distance(sample, centroid_vec, i)[0]
if (j == 0)
{
max_dist = curr_dist
max_idx = j
}
else
{
if (curr_dist > max_dist)
{
max_dist = curr_dist
max_idx = j
}
}
}
centroid_vec[i] = data_inp[max_idx]
}
return centroid_vec
}
function recalculate_centroids(data_inp, labels, K)
{
var Nr = data_inp.length
var Nc = data_inp[0].length
var local_centroid = []
var centroid_count = []
for (var i = 0; i < K; i++)
{
local_centroid[i] = []
centroid_count[i] = 0
for (var j = 0; j < Nc; j++)
{
local_centroid[i][j] = 0.0
}
}
for (var m = 0; m < Nr; m++)
{
for (var n = 0; n < Nc; n++)
{
local_centroid[labels[m]][n] = local_centroid[labels[m]][n]+data_inp[m][n]
}
centroid_count[labels[m]] = centroid_count[labels[m]] + 1
}
for (var p = 0; p < K; p++)
{
for (var q = 0; q < Nc; q++)
{
local_centroid[p][q] = local_centroid[p][q]/(centroid_count[p] + 0.0)
}
}
return local_centroid
}
function delta_centroids(inp1, inp2)
{
var nr = inp1.length
var nc = inp1[0].length
res = 0.0
for (var ii=0; ii < nr; ii++)
{
for (var jj=0; jj < nc; jj++)
{
res = res + ((inp1[ii][jj]-inp2[ii][jj])*(inp1[ii][jj]-inp2[ii][jj]))
}
}
return Math.sqrt(res)
}
function k_means_alg(data_inp, K)
{
var Nr = data_inp.length
var Nc = data_inp[0].length
centroids = create_centroids(data_inp,K)
labels = []
var dc = 1e6
var iterno = 0
var max_iter = 100
var dc_vec = new Array()
while ((dc > 0.0001) && (iterno < max_iter))
{
centroids_old = centroids
for (var i=0; i < Nr; i++)
{
labels[i] = find_min_distance(data_inp[i], centroids, K)[1]
}
centroids = recalculate_centroids(data_inp, labels, K)
dc = delta_centroids(centroids, centroids_old)
dc_vec.push(dc)
iterno = iterno + 1
}
return [centroids, labels, dc_vec, iterno]
}
var options = {legend:{position:"s",noColumns: 4,container: document.getElementById("legendDiv")},selection:{mode:"xy"}};
/*
var series1 = [ [0,0],[1,0],[2,0],[3,0]];
var data = [ series1 ];
$.plot($("#chart0"), data);
*/
function clear_real_inp() {
document.getElementById("Real_inp").value = "";
}
function calc_k_means()
{
res_array = create_data_array()
data_arr = res_array[3]
var K = document.frmOne.txtKNumber.value
k_alg_res = k_means_alg(data_arr,K)
centroids = k_alg_res[0]
labels = k_alg_res[1]
dc = k_alg_res[2]
iterno = k_alg_res[3]
res_str = print_from_data_arr(data_arr)
dbg_str = ""
dbg_str += res_array[0] + "\n\n"
dbg_str += "Nrows " + res_array[1].toString() + "\n"
dbg_str += "Ncols " + res_array[2].toString() + "\n\n"
dbg_str += res_str
dbg_str += "No of centroids " + centroids.length + "\n"
dbg_str += "Feature length " + centroids[0].length + "\n"
dbg_str += centroids
dbg_str += "\nLabels\n"
dbg_str += labels
dbg_str += "\nDelta Centroids\n"
dbg_str += dc
dbg_str += "\nNum iterations\n"
dbg_str += iterno
centroid_str = ""
data_label_str = ""
for (var hh=0; hh < data_arr.length; hh++)
{
data_label_str += labels[hh] + ": " + data_arr[hh] + "\n"
}
for (var ii=0; ii < centroids.length; ii++)
{
feature_vec = centroids[ii]
feature_vec_str = ""
for (var jj=0; jj < feature_vec.length - 1; jj++)
{
feature_vec_str += feature_vec[jj].toFixed(6) + ","
}
feature_vec_str += feature_vec[feature_vec.length - 1].toFixed(6)
// centroid_str += ii + ": " + centroids[ii] + "\n"
centroid_str += ii + ": " + feature_vec_str + "\n"
}
document.getElementById("Centroid_out").value = centroid_str
document.getElementById("Result_out").value = data_label_str
num_features = centroids[0].length
Nr = res_array[1].toString()
var data2 = new Array();
xm = new Array()
ym = new Array()
zm = new Array()
for (var ii = 0; ii < K; ii++)
{
xm[ii] = new Array()
ym[ii] = new Array()
zm[ii] = new Array()
}
for (var ii = 0; ii < Nr; ii++)
{
if (num_features == 1)
{
xm[labels[ii]].push(data_arr[ii][0])
ym[labels[ii]].push(0.0)
zm[labels[ii]].push(0.0)
}
else if (num_features == 2)
{
xm[labels[ii]].push(data_arr[ii][0])
ym[labels[ii]].push(data_arr[ii][1])
zm[labels[ii]].push(0.0)
}
else if (num_features >= 3)
{
xm[labels[ii]].push(data_arr[ii][0])
ym[labels[ii]].push(data_arr[ii][1])
zm[labels[ii]].push(data_arr[ii][2])
}
}
// Centroid initialisation
xc = new Array()
yc = new Array()
zc = new Array()
for (var ii = 0; ii < K; ii++)
{
xc[ii] = new Array()
yc[ii] = new Array()
zc[ii] = new Array()
}
for (var ii = 0; ii < K; ii++)
{
if (num_features == 1)
{
xc[ii].push(centroids[ii][0])
yc[ii].push(0.0)
zc[ii].push(0.0)
}
else if (num_features == 2)
{
xc[ii].push(centroids[ii][0])
yc[ii].push(centroids[ii][1])
zc[ii].push(0.0)
}
else if (num_features >= 3)
{
xc[ii].push(centroids[ii][0])
yc[ii].push(centroids[ii][1])
zc[ii].push(centroids[ii][2])
}
}
/*
if (num_features == 1)
{
for (var idx=0; idx < K; idx++)
{
var num1 = Math.round(((K - idx)/K) * 255);
var num2 = Math.round((idx/K) * 255);
var num1hex = num1.toString(16);
var num2hex = num2.toString(16);
if (num1hex.length == 1)
num1hex = "0" + num1hex;
if (num2hex.length == 1)
num2hex = "0" + num2hex;
col_str = "#" + num1hex + num2hex + "00";
var dset = new Array();
var lbl = idx + 1;
tmpstr = "Cluster " + lbl;
for (var t = 0; t < Nr; t++)
{
if (labels[t] == idx )
dset.push([t,data_arr[t][0]]);
}
data2[idx] = {
label: tmpstr,
data: dset,
color: col_str,
points: { show: true },
lines: { show: false },
bars: { show: false },
yaxis: 1,
selection: { mode: "xy" }};
}
$.plot($("#chart0"), data2, options);
}
else if (num_features == 2)
{
for (var idx=0; idx < K; idx++)
{
var num1 = Math.round(((K - idx)/K) * 255);
var num2 = Math.round((idx/K) * 255);
var num1hex = num1.toString(16);
var num2hex = num2.toString(16);
if (num1hex.length == 1)
num1hex = "0" + num1hex;
if (num2hex.length == 1)
num2hex = "0" + num2hex;
col_str = "#" + num1hex + num2hex + "00";
var dset = new Array();
var lbl = idx + 1;
tmpstr = "Cluster " + lbl;
for (var t = 0; t < Nr; t++)
{
if (labels[t] == idx)
dset.push([data_arr[t][0], data_arr[t][1]]);
}
data2[idx] = {
label: tmpstr,
data: dset,
color: col_str,
points: { show: true },
lines: { show: false },
bars: { show: false },
yaxis: 1,
selection: { mode: "xy" }};
}
$.plot($("#chart0"), data2,options);
}
*/
// 3-d plot
Plotly.d3.csv('https://raw.githubusercontent.com/plotly/datasets/master/alpha_shape.csv', function(err, rows){
function unpack(rows, key) {
return rows.map(function(row) { return row[key]; });
}
/*
mystr = 'rgb(' + toString(255) + "," + toString(0) + ",0)"
var data = [{
x: xm[0],
y: ym[0],
z: zm[0],
mode: 'markers',
type: 'scatter3d',
marker: {
color: mystr,
size: 2
}}]
*/
var data = Array()
for (var vv=0; vv< K; vv++)
{
mystr = 'rgb(' + toString(Math.round(((K - vv+0.0)/(K+0.0)) * 255)) + "," + toString(Math.round(((vv+0.0)/(K+0.0)) * 255)) + ",0)"
// mystr = 'rgb(' + toString(Math.round(((vv)/K) * 255)) + "," + toString(Math.round(((K-vv)/K) * 255)) + ",0)"
//mystr = 'rgb(255,0,0)'
data.push({
x: xm[vv],
y: ym[vv],
z: zm[vv],
mode: 'markers',
type: 'scatter3d',
marker: {
color: mystr,
size: 2
}})
data.push({
x: xc[vv],
y: yc[vv],
z: zc[vv],
mode: 'markers',
type: 'scatter3d',
showlegend: false,
marker: {
color: 'rgb(0,0,0)',
size: 4
}})
}
var layout = {
autosize: true,
height: 580,
scene: {
aspectratio: {
x: 1,
y: 1,
z: 1
},
camera: {
center: {
x: 0,
y: 0,
z: 0
},
eye: {
x: 1.25,
y: 1.25,
z: 1.25
},
up: {
x: 0,
y: 0,
z: 1
}
},
xaxis: {
type: 'linear',
zeroline: false
},
yaxis: {
type: 'linear',
zeroline: false
},
zaxis: {
type: 'linear',
zeroline: false
}
},
legend: {"orientation": "h"},
title: '3d cluster visualisation',
width: 600
};
data_all = data
Plotly.newPlot('myDiv', data_all, layout);
});
}
</script>
Alijah Ahmedhttp://www.blogger.com/profile/01151589526613233918noreply@blogger.comtag:blogger.com,1999:blog-8441309013858171051.post-29650652325962743082020-09-30T12:09:00.006-07:002024-01-03T06:45:02.912-08:00Matrix Inverse<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-csv/0.8.3/jquery.csv.min.js"></script>
<script>
$(document).ready(function() {
if(isAPIAvailable()) {
$('#files').bind('change', handleFileSelect);
}
});
function isAPIAvailable() {
// Check for the various File API support.
if (window.File && window.FileReader && window.FileList && window.Blob) {
// Great success! All the File APIs are supported.
return true;
} else {
// source: File API availability - http://caniuse.com/#feat=fileapi
// source: <output> availability - http://html5doctor.com/the-output-element/
document.writeln('The HTML5 APIs used in this form are only available in the following browsers:<br />');
// 6.0 File API & 13.0 <output>
document.writeln(' - Google Chrome: 13.0 or later<br />');
// 3.6 File API & 6.0 <output>
document.writeln(' - Mozilla Firefox: 6.0 or later<br />');
// 10.0 File API & 10.0 <output>
document.writeln(' - Internet Explorer: Not supported (partial support expected in 10.0)<br />');
// ? File API & 5.1 <output>
document.writeln(' - Safari: Not supported<br />');
// ? File API & 9.2 <output>
document.writeln(' - Opera: Not supported');
return false;
}
}
function handleFileSelect(evt) {
var files = evt.target.files; // FileList object
var file = files[0];
// read the file metadata
var output = ''
output += '<span style="font-weight:bold;">' + escape(file.name) + '</span><br />\n';
output += ' - FileType: ' + (file.type || 'n/a') + '<br />\n';
output += ' - FileSize: ' + file.size + ' bytes<br />\n';
output += ' - LastModified: ' + (file.lastModifiedDate ? file.lastModifiedDate.toLocaleDateString() : 'n/a') + '<br />\n';
// read the file contents
printTextArea(file);
// post the results
$('#list').append(output);
}
function printTextArea(file) {
var reader = new FileReader();
reader.readAsText(file);
reader.onload = function(event){
var csv = event.target.result;
var data = $.csv.toArrays(csv);
var mystr = "";
for(var row in data) {
for (ii=0; ii < data[row].length; ii++) {
mystr += data[row][ii].toString()
if (ii < data[row].length-1) {
mystr += ","
}
}
mystr += "\n"
}
mystr = mystr.substring(0, mystr.length - 1)
document.getElementById("InpBox").value = mystr;
};
reader.onerror = function(){ alert('Unable to read ' + file.fileName); };
}
</script>
<body>
<script>
$(document).ready(function(){
$(".special").css("background-color", "white");
$(".special").css("width", "600px");
$(".special").css("height", "400px");
$(".special").css("font-size", "0.9em");
$(".special").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".special").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".special").css("font-weight","normal");
$(".special").css("border","1px solid black");
$(".precision").css("font-size", "1.0em");
$(".results").css("background-color", "pink");
$(".results").css("width", "600px");
$(".results").css("height", "100px");
$(".results").css("font-size", "0.9em");
$(".results").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".results").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".results").css("font-weight","normal");
$(".results").css("border","1px solid black");
$(".invres").css("background-color", "white");
$(".invres").css("width", "600px");
$(".invres").css("height", "400px");
$(".invres").css("font-size", "0.9em");
$(".invres").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".invres").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".invres").css("font-weight","normal");
$(".invres").css("border","1px solid black");
});
</script>
<p style="text-align:justify">This blog post calculates the inverse of a square matrix with complex numbers.</p>
<p style="text-align:justify">
For the (square) matrix entry, comma separated numbers need to be entered into the Input box, with no newline after the last line. A comma at the end of each line can be added optionally for comma separated numbers. Alternatively, you can load a CSV file into the Input box, by clicking on the Choose File button.</p>
<p style="text-align:justify">The precision of the results in decimal places can be specified in the text box headed "Precision (decimal places)". To perform the calculation, simply press the button labelled "Perform Matrix Inverse" further down this page. In addition, the matrix size and determinant will be printed.</p>
<br>
Precision (decimal places)
<br>
<textarea class="precision" name="textarea" rows="2" cols="10" id ="prec">
</textarea>
<br>
<br>
<div id=inputs class=clearfix>
<input type=file id=files name=files[] single />
</div>
<br>
<br>
<input name="b1" onclick="clear_input()" style="margin-left: 0px;" type="Button" value="Clear Input" />
<br>
<br>
Input<br>
<textarea class="special" name="textarea" rows="200" cols="50" id ="InpBox">
</textarea>
<br>
<input name="b1" onclick="calc()" style="margin-left: 0px;" type="Button" value="Perform Matrix Inverse" />
<br>
<p id="p1">Results pending...</p>
<br>
Inverse Matrix
<br>
<textarea class="invres" rows="200" cols="30" id ="I">
</textarea>
<br>
</body>
</html>
<script language="JavaScript">
window.onload = set_inputs();
function set_inputs(){
var str = "1,2-0.5i,4i,-3" + "\n" + "3,4+2i,6-i,2" + "\n" + "8+1.5i,6,7,-0.1i" + "\n" + "2i,-3+i,4,-0.5"
document.getElementById("InpBox").value = str;
document.getElementById("prec").value = "4";
}
function clear_input() {
document.getElementById("InpBox").value = "";
}
function validate_matrix(inpstr) {
// The Matrix proper
var M = new Array()
var inp_arr = new Array()
inp_arr = inpstr.split("\n");
var num_rows = inp_arr.length
if (inp_arr[num_rows - 1] == "")
num_rows = num_rows - 1
var splitchar = ","
var tmpStr = inp_arr[0]
if (tmpStr.indexOf(',') == -1) {
splitchar = " "
}
var row_arr_filtered = inp_arr[0].split(splitchar).filter(function (el) {
return el != "";
});
var num_cols
var num_cols = row_arr_filtered.length
var dbg_str = ""
var cols_match = 1
// Check if all rows have same number of elements
for (ii = 0; ii < num_rows; ii++) {
var vec = inp_arr[ii].split(splitchar).filter(function (el) {
return el != "";
});
var num_vec = new Array()
for (kk=0; kk < vec.length; kk++) {
num_vec.push(genCmplxFromString(vec[kk]))
}
M.push(num_vec)
if (ii == 0){
num_cols = vec.length
} else {
if (num_cols != vec.length) {
cols_match = 0
}
}
}
var dbg_str = ""
dbg_str += "Number of rows is " + num_rows.toString()
dbg_str += ", number of columns is " + num_cols.toString()
var isSquare = 1
if (num_rows != num_cols) {
isSquare = 0
}
return [cols_match, dbg_str,M, isSquare]
}
function gen_printable_string_from_matrix(M,dp,isTextArea)
{
var Nr = M.length
var Nc = M[0].length
var res_str = ""
for (i=0; i < Nr; i++)
{
for (j=0; j < Nc;j++)
{
res_str += genStringFromCmplx(M[i][j],dp)
if (j < Nc-1)
res_str += ", "
}
if (isTextArea) {
res_str += "\n"
} else {
res_str += "<br>"
}
}
return res_str
}
function gen_printable_real_string_from_matrix(M,dp,isTextArea)
{
var Nr = M.length
var Nc = M[0].length
var res_str = ""
for (i=0; i < Nr; i++)
{
for (j=0; j < Nc;j++)
{
res_str += M[i][j][0].toFixed(dp)
if (j < Nc-1)
res_str += ", "
}
if (isTextArea) {
res_str += "\n"
} else {
res_str += "<br>"
}
}
return res_str
}
function create_const_matrix(m,n,val)
{
var M_res = new Array
for (i=0; i < m; i++)
{
var vec = new Array
for (j=0; j < n;j++)
{
var C = new Array()
C.push(val)
C.push(val)
vec.push(C)
}
M_res.push(vec)
}
return M_res
}
function create_const_real_matrix(m,n,val)
{
var M_res = new Array
for (i=0; i < m; i++)
{
var vec = new Array
for (j=0; j < n;j++)
{
vec.push(val)
}
M_res.push(vec)
}
return M_res
}
function transpose_matrix(M)
{
var nr = M.length
var nc = M[0].length
var Mres = create_const_matrix(nc,nr,0);
for (i=0; i < nc; i++)
{
for (j=0; j < nr;j++)
{
Mres[i][j]=M[j][i]
}
}
return Mres
}
function get_max_idx(V,offs) {
var idx = 0
var maxVal = 0
for (k=0; k < V.length; k++) {
if (k == 0) {
maxVal = V[k]
} else {
if (magnCmplxNum(V[k]) > magnCmplxNum(maxVal)) {
maxVal = V[k]
idx = k
}
}
}
return idx+offs
}
function swap_rows(Minp,i,j) {
var M_res = Minp
var nr = Minp.length
var nc = Minp[0].length
var tmpVal = new Array()
for (k=0; k < nc; k++) {
tmpVal = M_res[i][k]
M_res[i][k] = M_res[j][k]
M_res[j][k] = tmpVal
}
return M_res
}
function lu_factorisation_partial_pivot(M) {
var U = M
var nr = U.length
var L = create_const_matrix(nr,nr,0);
var P = create_const_matrix(nr,nr,0);
var num_swaps = 0
for (k=0; k < nr; k++) {
var Z = new Array()
Z.push(1.0)
Z.push(0.0)
L[k][k] = Z
P[k][k] = Z
}
for (ii=0; ii< nr-1; ii++) {
var vec = new Array()
for (jj=ii; jj < nr; jj++) {
vec.push(U[jj][ii])
}
var elem = 0.0
var idx = get_max_idx(vec,ii)
var el_tmp = 0.0
for (jj =ii; jj < nr; jj++ ) {
el_tmp = U[ii][jj]
U[ii][jj] = U[idx][jj]
U[idx][jj] = el_tmp
}
for (jj =0; jj < ii; jj++ ) {
el_tmp = L[ii][jj]
L[ii][jj] = L[idx][jj]
L[idx][jj] = el_tmp
}
if (ii != idx) {
num_swaps += 1
}
Pnew = swap_rows(P,ii,idx)
P = Pnew
for (jj = ii + 1; jj < nr; jj++) {
if (magnCmplxNum(U[jj][ii])<1e-16) {
var C = new Array()
C.push(0.0)
C.push(0.0)
L[jj][ii] = C
} else {
L[jj][ii] = divCmplxNum(U[jj][ii],U[ii][ii])
}
for (xx=ii;xx < nr; xx++) {
U[jj][xx] = subtrCmplxNum(U[jj][xx],multCmplxNum(L[jj][ii], U[ii][xx]))
}
}
}
var det_res = new Array()
det_res.push(1.0)
det_res.push(0.0)
for (vv=0; vv < nr; vv++) {
det_res = multCmplxNum(det_res,multCmplxNum(U[vv][vv],L[vv][vv]))
}
det_res[0] = det_res[0] * ((-1)**num_swaps)
det_res[1] = det_res[1] * ((-1)**num_swaps)
return [U,L,P,num_swaps,det_res]
}
function mult_matrices(A,B) {
var nra = A.length
var nca = A[0].length
var nrb = B.length
var ncb = B[0].length
if (nca != nrb) {
alert("Cannot multiply these matrics!")
}
var C = new Array()
C = create_const_matrix(nra,ncb,0)
for (aa = 0; aa < nra; aa++) {
for (bb = 0; bb < ncb; bb++) {
var tmp_res = new Array()
tmp_res.push(0.0)
tmp_res.push(0.0)
for (cc = 0; cc < nca; cc++) {
tmp_res = addCmplxNum(tmp_res, multCmplxNum(A[aa][cc],B[cc][bb]))
}
C[aa][bb] = tmp_res
}
}
return C
}
function back_subst(U,d) {
var nr = d.length
var x = new Array()
x = create_const_matrix(nr,1,0)
x[nr - 1][0] = divCmplxNum(d[nr-1][0],U[nr-1][nr-1]);
for (i=nr-2; i >= 0; i--) {
var sum_ux = new Array()
sum_ux.push(0.0)
sum_ux.push(0.0)
for (j=i+1; j < nr; j++) {
sum_ux = addCmplxNum(sum_ux,multCmplxNum(U[i][j],x[j][0]));
}
var one_cmplx = new Array()
one_cmplx.push(1.0)
one_cmplx.push(0.0)
x[i][0] = multCmplxNum(divCmplxNum(one_cmplx,U[i][i]), subtrCmplxNum(d[i][0],sum_ux));
}
return x;
}
function fwd_subst(L,b) {
var d = new Array()
var nr = b.length
d = create_const_matrix(nr,1,0)
d[0][0] = b[0][0]
for (i=1; i < nr; i++) {
var sum_ld = new Array();
sum_ld.push(0.0)
sum_ld.push(0.0)
for (j=0; j < i; j++) {
sum_ld = addCmplxNum(sum_ld,multCmplxNum(L[i][j],d[j][0]));
}
d[i][0] = subtrCmplxNum(b[i][0],sum_ld);
}
return d;
}
/* Complex calculation related functions */
function genStringFromCmplx(A,dp) {
var res = ""
if (A[1] < 0) {
res = A[0].toFixed(dp) + " - " + Math.abs(A[1]).toFixed(dp) + "i"
} else {
res = A[0].toFixed(dp) + " + " + A[1].toFixed(dp) + "i"
}
return res
}
function conjCmplxNum(A) {
C = new Array()
C.push(A[0])
C.push(-A[1])
return C
}
function magnCmplxNum(A) {
return Math.sqrt((A[0]*A[0]) + (A[1]*A[1]))
}
function powCmplxNum(A,x) {
var r = Math.sqrt((A[0]*A[0]) + (A[1]*A[1]))
var theta = Math.atan2(A[1],A[0])
console.log("r is ")
console.log("theta is ", theta)
var res_re = Math.pow(r,x) * Math.cos(x*theta)
var res_im = Math.pow(r,x) * Math.sin(x*theta)
var C = new Array()
C.push(res_re)
C.push(res_im)
return C
}
function addCmplxNum(A,B){
C = new Array()
C.push(A[0] + B[0])
C.push(A[1] + B[1])
return C
}
function subtrCmplxNum(A,B){
C = new Array()
C.push(A[0] - B[0])
C.push(A[1] - B[1])
return C
}
function multCmplxNum(A,B){
C = new Array()
C.push((A[0]*B[0]) - (A[1]*B[1]))
C.push((A[0]*B[1]) + (A[1]*B[0]))
return C
}
function divCmplxNum(A,B){
C = new Array()
var magn = (B[0] * B[0]) + (B[1] * B[1])
C = multCmplxNum(A,conjCmplxNum(B))
C[0] = C[0]/magn
C[1] = C[1]/magn
return C
}
function genCmplxFromString(str) {
var NumCmplx = new Array()
NumCmplx.push(0.0)
NumCmplx.push(0.0)
var isCmplex = str.includes("i")
var scaleFac = +1
if (isCmplex) {
var idx_i = str.indexOf("i")
var ii = idx_i
var sgn_idx = 0
var sgn_found = false
for (ii = idx_i; ii >= 0; ii--) {
if ((str[ii] == "+" || (str[ii] == "-" && str[ii-1]!= "e")) && (sgn_found == false)) {
if (str[ii] == "-") {
scaleFac = -1
}
sgn_idx = ii
sgn_found = true
}
}
var real_str = str.substring(0,sgn_idx)
var imag_i_str = str.substring(sgn_idx + 1, str.length)
var imag_str = imag_i_str.replace("i","")
var imag_str = imag_str.replace(/ +/g, ' ');
NumCmplx[0] = Number(real_str)
NumCmplx[1] = scaleFac * Number(imag_str)
console.log("Scale fac ",scaleFac)
if (imag_str == ' ' || imag_str == '') {
NumCmplx[1] = scaleFac * 1
}
if (sgn_found == false) {
NumCmplx[0] = 0
NumCmplx[1] = Number(str.replace("i",""))
// Corner case if we have just "i"
if (str.replace("i","").replace(/ +/g, ' ')== "") {
NumCmplx[1] = 1
}
}
} else {
NumCmplx[0] = Number(str)
NumCmplx[1] = 0
}
console.log("Real ",NumCmplx[0])
console.log("Imag ",NumCmplx[1])
return NumCmplx
}
/* The calculate function called when the Perform LU Decomposition button is pressed
*/
function calc() {
var dp = Number(document.getElementById("prec").value)
var inpstr = document.getElementById("InpBox").value;
var check_params = new Array()
check_params = validate_matrix(inpstr)
if (check_params[0] == 0) {
alert("All rows should have the same number of entries!")
}
var isSquare = check_params[3]
if (isSquare == 0 && check_params[0] == 1) {
alert("Matrix needs to be square!")
}
var dbg_str = check_params[1]
var M = new Array()
M = check_params[2]
var M_str = gen_printable_string_from_matrix(M,dp,true)
var Mt = new Array()
Mt = transpose_matrix(M)
console.log("Stage 0")
var Mt_str = gen_printable_string_from_matrix(Mt,dp,true)
console.log("Stage 1")
// The LU factorisation proper
res_lu = lu_factorisation_partial_pivot(M)
console.log("Stage 2")
var U = res_lu[0]
var L = res_lu[1]
var P = res_lu[2]
var num_swaps = res_lu[3]
var det_res = res_lu[4]
console.log("Stage 3")
// Stringify so as to put results in the textareas
var U_str = gen_printable_string_from_matrix(U,dp,true)
console.log("Stage 3a")
var L_str = gen_printable_string_from_matrix(L,dp,true)
console.log("Stage 3b")
var P_str = gen_printable_real_string_from_matrix(P,0,true)
console.log("Stage 3c")
var det_res = res_lu[4]
console.log("Stage 4")
// Calculate the inverse matrix fron the LU factorisation
var dim = U.length
var inv_res = new Array()
inv_res = create_const_matrix(dim,dim,0)
var C_inv = new Array()
for (kk=0; kk < dim; kk++) {
var ei = new Array()
ei = create_const_matrix(dim,1,0);
var Z = new Array()
Z.push(1.0)
Z.push(0.0)
ei[kk][0] = Z;
var ri = new Array()
ri = create_const_matrix(dim,1,0);
ri = back_subst(U,fwd_subst(L,ei));
for (mm=0; mm < dim; mm++) {
inv_res[mm][kk] = ri[mm][0];
}
}
C_inv = mult_matrices(inv_res,P)
//C_inv = inv_res
var C_str = gen_printable_string_from_matrix(C_inv,dp,true)
// Append matrix determinant
dbg_str += "<br>" + "Determinant of matrix is " + genStringFromCmplx(det_res,dp)
document.getElementById("p1").innerHTML = dbg_str;
document.getElementById("I").innerHTML = C_str;
/*
document.getElementById("L").innerHTML = M_str;
document.getElementById("U").innerHTML = Mt_str;
*/
}
</script>
Alijah Ahmedhttp://www.blogger.com/profile/01151589526613233918noreply@blogger.comLondon, UK51.5072178 -0.127586223.196983963821154 -35.2838362 79.817451636178845 35.0286638tag:blogger.com,1999:blog-8441309013858171051.post-19016162490437547842020-05-20T06:13:00.003-07:002020-05-20T06:22:59.903-07:00Matrix Utilities: Binary Operators for Complex Matrices<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-csv/0.8.3/jquery.csv.min.js"></script>
<script>
</script>
<body>
<script>
$(document).ready(function(){
$(".special").css("background-color", "white");
$(".special").css("width", "600px");
$(".special").css("height", "150px");
$(".special").css("font-size", "0.9em");
$(".special").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".special").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".special").css("font-weight","normal");
$(".special").css("border","1px solid black");
$(".precision").css("font-size", "1.0em");
$(".results").css("background-color", "yellow");
$(".results").css("width", "600px");
$(".results").css("height", "150px");
$(".results").css("font-size", "0.9em");
$(".results").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".results").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".results").css("font-weight","normal");
$(".results").css("border","1px solid black");
$(".invres").css("background-color", "yellow");
$(".invres").css("width", "600px");
$(".invres").css("height", "100px");
$(".invres").css("font-size", "0.9em");
$(".invres").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".invres").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".invres").css("font-weight","normal");
$(".invres").css("border","1px solid black");
});
</script>
<p style="text-align:justify">Binary operations on two matrices are implemented in this blog post. The operations include the following (two types of operations for non-commutative operations, a single for commutative ones):
<ul>
<li>Addition : A + B</li>
<li>Subtraction : A - B</li>
<li>Subtraction : B - A</li>
<li>Multiplication : A * B</li>
<li>Multiplication : B * A</li>
<li>Hadamard product: A .* B</li>
<li>Kronecker product: A kron B</li>
<li>Kronecker product: B kron A</li>
</ul>
</p>
<p style="text-align:justify">
For the matrix entries in Input A and Input B, comma separated (the separation can also be a single whitespace) Complex numbers need to be entered into the Input boxes, with no newline after the last line. A comma at the end of each line can be added optionally for comma separated numbers. For guidance, the textareas for Input A and Input B have already been populated with example entries.</p>
<p style="text-align:justify">
Note that it is possible to either just transpose the matrices in the textareas for Input A and Input B, or perform a Hermitian tranpose on the matrices in the textareas for Input A and Input B. This is achieved by clicking the relevant buttons. When a Hermitian transpose is performed, the full complex form of the number is displayed.
</p>
<p style="text-align:justify">
Alternatively, you can load a CSV file into each of the Input boxes, by clicking on the Choose File button. There is a select drop down menu which allows you to specify into which of the Input boxes (A or B) should the CSV file be loaded. The drop down menu needs to be set appropriately before clicking on the "Chose File" button. The CSV files that need to be loaded into the two boxes must have a different name, even if the contents are the same (due to a limitation of how the CSV loading is implemented).</p>
<p style="text-align:justify">The precision of the results in decimal places can be specified in the text box headed "Precision (decimal places)".</p>
<p style="text-align:justify">To perform the calculation, simply press the button labelled "Perform matrix operation" further down this page. Before clicking on this button, you need to specify which binary operation to perform with another select drop down menu. The result will be displayed in the yellow textarea.</p>
<br>
Precision (decimal places)
<br>
<br>
<textarea class="precision" name="textarea" rows="2" cols="10" id ="prec">
</textarea>
<br>
<br>
<select id="SelectFile">
<option value="Input_A">Load CSV file into Input A</option>
<option value="Input_B">Load CSV file into Input B</option>
</select>
<div id=inputs class=clearfix>
<input type=file id=files name=files[] single />
</div>
<br>
<br>
<input name="b1" onclick="clear_inputA()" style="margin-left: 0px;" type="Button" value="Clear Input A" />
<input name="b1b" onclick="transpose_A()" style="margin-left: 0px;" type="Button" value="Transpose A" />
<input name="b1c" onclick="htranspose_A()" style="margin-left: 0px;" type="Button" value="Hermitian transpose A" />
<br>
Input A<br>
<textarea class="special" name="textarea" rows="20" cols="50" id ="InpABox">
</textarea>
<input name="b2" onclick="clear_inputB()" style="margin-left: 0px;" type="Button" value="Clear Input B" />
<input name="b2b" onclick="transpose_B()" style="margin-left: 0px;" type="Button" value="Transpose B" />
<input name="b2c" onclick="htranspose_B()" style="margin-left: 0px;" type="Button" value="Hermitian Transpose B" />
<br>
Input B<br>
<textarea class="special" name="textarea" rows="20" cols="50" id ="InpBBox">
</textarea>
<br>
<br>
<select id="SelectOperation">
<option value="A + B">A + B</option>
<option value="A - B">A - B</option>
<option value="B - A">B - A</option>
<option value="A * B">A * B</option>
<option value="B * A">B * A</option>
<option value="A .* B">A .* B</option>
<option value="A kron B">A kron B</option>
<option value="B kron A">B kron A</option>
</select>
<br>
<input name="b1" onclick="calc()" style="margin-left: 0px;" type="Button" value="Perform Matrix Operation" />
<br>
<p id="p3">A + B</p>
<textarea class="results" rows="20" cols="50" id ="C">
</textarea>
<br>
<br>
</body>
</html>
<script language="JavaScript">
window.onload = set_inputs();
function set_inputs(){
var str = "1,2,4+3i" + "\n" + "-3i,4,6" + "\n" + "8,6,7"
document.getElementById("InpABox").value = str;
document.getElementById("InpBBox").value = str;
document.getElementById("prec").value = "5";
}
function clear_inputA() {
document.getElementById("InpABox").value = "";
}
function validate_matrix(inpstr) {
// The Matrix proper
var M = new Array()
var inp_arr = new Array()
inp_arr = inpstr.split("\n");
var num_rows = inp_arr.length
if (inp_arr[num_rows - 1] == "")
num_rows = num_rows - 1
var splitchar = ","
var tmpStr = inp_arr[0]
if (tmpStr.indexOf(',') == -1) {
splitchar = " "
}
var row_arr_filtered = inp_arr[0].split(splitchar).filter(function (el) {
return el != "";
});
var num_cols
var num_cols = row_arr_filtered.length
var dbg_str = ""
var cols_match = 1
// Check if all rows have same number of elements
for (ii = 0; ii < num_rows; ii++) {
var vec = inp_arr[ii].split(splitchar).filter(function (el) {
return el != "";
});
var num_vec = new Array()
for (kk=0; kk < vec.length; kk++) {
num_vec.push(genCmplxFromString(vec[kk]))
}
M.push(num_vec)
if (ii == 0){
num_cols = vec.length
} else {
if (num_cols != vec.length) {
cols_match = 0
}
}
}
var dbg_str = ""
dbg_str += "Number of rows is " + num_rows.toString()
dbg_str += ", number of columns is " + num_cols.toString()
var isSquare = 1
if (num_rows != num_cols) {
isSquare = 0
}
return [cols_match, dbg_str,M, isSquare]
}
function transpose_string_matrix(inpstr) {
// The Matrix proper
var M = new Array()
// Transpose
var Mt = new Array()
var inp_arr = new Array()
inp_arr = inpstr.split("\n");
var num_rows = inp_arr.length
if (inp_arr[num_rows - 1] == "")
num_rows = num_rows - 1
var splitchar = ","
var tmpStr = inp_arr[0]
if (tmpStr.indexOf(',') == -1) {
splitchar = " "
}
var row_arr_filtered = inp_arr[0].split(splitchar).filter(function (el) {
return el != "";
});
var num_cols = row_arr_filtered.length
var dbg_str = ""
var cols_match = 1
// Check if all rows have same number of elements
for (ii = 0; ii < num_rows; ii++) {
var vec = inp_arr[ii].split(splitchar).filter(function (el) {
return el != "";
});
var num_vec = new Array()
for (kk=0; kk < vec.length; kk++) {
num_vec.push(vec[kk])
}
M.push(num_vec)
if (ii == 0){
num_cols = vec.length
} else {
if (num_cols != vec.length) {
cols_match = 0
}
}
}
var aa = 0
var bb = 0
for (aa = 0; aa < num_cols; aa++) {
var vec_t = new Array()
for (bb = 0; bb < num_rows; bb++) {
vec_t.push(M[bb][aa])
}
Mt.push(vec_t)
}
return Mt
}
// Do a Hermitian transpose
function htranspose_string_matrix(inpstr) {
// The Matrix proper
var M = new Array()
// Transpose
var Mt = new Array()
var inp_arr = new Array()
inp_arr = inpstr.split("\n");
var num_rows = inp_arr.length
if (inp_arr[num_rows - 1] == "")
num_rows = num_rows - 1
var splitchar = ","
var tmpStr = inp_arr[0]
if (tmpStr.indexOf(',') == -1) {
splitchar = " "
}
var row_arr_filtered = inp_arr[0].split(splitchar).filter(function (el) {
return el != "";
});
var num_cols = row_arr_filtered.length
var dbg_str = ""
var cols_match = 1
// Check if all rows have same number of elements
for (ii = 0; ii < num_rows; ii++) {
var vec = inp_arr[ii].split(splitchar).filter(function (el) {
return el != "";
});
var num_vec = new Array()
for (kk=0; kk < vec.length; kk++) {
var varCmplxNum = genCmplxFromString(vec[kk])
var varConjRes = conjCmplxNum(varCmplxNum)
var varStringAgain = ""
if (varConjRes[1] < 0) {
varStringAgain = varConjRes[0].toString() + " - " + Math.abs(varConjRes[1]).toString() + "i"
} else {
varStringAgain = varConjRes[0].toString() + " + " + varConjRes[1].toString() + "i"
}
console.log("Conjugated result ",varStringAgain)
num_vec.push(varStringAgain)
}
M.push(num_vec)
if (ii == 0){
num_cols = vec.length
} else {
if (num_cols != vec.length) {
cols_match = 0
}
}
}
var aa = 0
var bb = 0
for (aa = 0; aa < num_cols; aa++) {
var vec_t = new Array()
for (bb = 0; bb < num_rows; bb++) {
vec_t.push(M[bb][aa])
}
Mt.push(vec_t)
}
return Mt
}
function transpose_B() {
var inpstr = document.getElementById("InpBBox").value;
var Mt = new Array()
Mt = transpose_string_matrix(inpstr)
var mystr = ""
for (ii = 0; ii < Mt.length; ii++) {
for (kk=0; kk < Mt[0].length; kk++) {
mystr += Mt[ii][kk]
if (kk < Mt[0].length - 1) {
mystr += ","
}
}
mystr += "\n"
}
document.getElementById("InpBBox").value = mystr;
}
function transpose_A() {
var inpstr = document.getElementById("InpABox").value;
var Mt = new Array()
Mt = transpose_string_matrix(inpstr)
var mystr = ""
for (ii = 0; ii < Mt.length; ii++) {
for (kk=0; kk < Mt[0].length; kk++) {
mystr += Mt[ii][kk]
if (kk < Mt[0].length - 1) {
mystr += ","
}
}
mystr += "\n"
}
document.getElementById("InpABox").value = mystr;
}
function htranspose_B() {
var inpstr = document.getElementById("InpBBox").value;
var Mt = new Array()
Mt = htranspose_string_matrix(inpstr)
var mystr = ""
for (ii = 0; ii < Mt.length; ii++) {
for (kk=0; kk < Mt[0].length; kk++) {
mystr += Mt[ii][kk]
if (kk < Mt[0].length - 1) {
mystr += ","
}
}
mystr += "\n"
}
document.getElementById("InpBBox").value = mystr;
}
function htranspose_A() {
var inpstr = document.getElementById("InpABox").value;
var Mt = new Array()
Mt = htranspose_string_matrix(inpstr)
var mystr = ""
for (ii = 0; ii < Mt.length; ii++) {
for (kk=0; kk < Mt[0].length; kk++) {
mystr += Mt[ii][kk]
if (kk < Mt[0].length - 1) {
mystr += ","
}
}
mystr += "\n"
}
document.getElementById("InpABox").value = mystr;
}
function clear_inputB() {
document.getElementById("InpBBox").value = "";
}
function gen_printable_string_from_matrix(M,dp,isTextArea)
{
var Nr = M.length
var Nc = M[0].length
var res_str = ""
for (i=0; i < Nr; i++)
{
for (j=0; j < Nc;j++)
{
res_str += genStringFromCmplx(M[i][j],dp)
if (j < Nc-1)
res_str += ", "
}
if (isTextArea) {
res_str += "\n"
} else {
res_str += "<br>"
}
}
return res_str
}
function create_const_matrix(m,n,val)
{
var M_res = new Array
for (i=0; i < m; i++)
{
var vec = new Array
for (j=0; j < n;j++)
{
var C = new Array()
C.push(val)
C.push(val)
vec.push(C)
}
M_res.push(vec)
}
return M_res
}
function transpose_matrix(M)
{
var nr = M.length
var nc = M[0].length
var Mres = create_const_matrix(nc,nr,0);
for (i=0; i < nc; i++)
{
for (j=0; j < nr;j++)
{
Mres[i][j]=M[j][i]
}
}
return Mres
}
function htranspose_matrix(M)
{
var nr = M.length
var nc = M[0].length
var Mres = create_const_matrix(nc,nr,0);
for (i=0; i < nc; i++)
{
for (j=0; j < nr;j++)
{
Mres[i][j]=conjComplxNum(M[j][i])
}
}
return Mres
}
function get_max_idx(V,offs) {
var idx = 0
var maxVal = 0
for (k=0; k < V.length; k++) {
if (k == 0) {
maxVal = V[k]
} else {
if (magnCmplxNum(V[k]) > magnCmplxNum(maxVal)) {
maxVal = V[k]
idx = k
}
}
}
return idx+offs
}
function swap_rows(Minp,i,j) {
var M_res = Minp
var nr = Minp.length
var nc = Minp[0].length
var tmpVal = 0
for (k=0; k < nc; k++) {
tmpVal = M_res[i][k]
M_res[i][k] = M_res[j][k]
M_res[j][k] = tmpVal
}
return M_res
}
function lu_factorisation_partial_pivot(M) {
var U = M
var nr = U.length
var L = create_const_matrix(nr,nr,0);
var P = create_const_matrix(nr,nr,0);
var num_swaps = 0
for (k=0; k < nr; k++) {
var Z = new Array()
Z.push(1.0)
Z.push(0.0)
L[k][k] = Z
P[k][k] = Z
}
for (ii=0; ii< nr-1; ii++) {
var vec = new Array()
for (jj=ii; jj < nr; jj++) {
vec.push(U[jj][ii])
}
var elem = 0.0
var idx = get_max_idx(vec,ii)
var el_tmp = 0.0
for (jj =ii; jj < nr; jj++ ) {
el_tmp = U[ii][jj]
U[ii][jj] = U[idx][jj]
U[idx][jj] = el_tmp
}
for (jj =0; jj < ii; jj++ ) {
el_tmp = L[ii][jj]
L[ii][jj] = L[idx][jj]
L[idx][jj] = el_tmp
}
if (ii != idx) {
num_swaps += 1
}
Pnew = swap_rows(P,ii,idx)
P = Pnew
for (jj = ii + 1; jj < nr; jj++) {
if (magnCmplxNum(U[jj][ii])<1e-16) {
var C = new Array()
C.push(0.0)
C.push(0.0)
L[jj][ii] = C
} else {
L[jj][ii] = divCmplxNum(U[jj][ii],U[ii][ii])
}
for (xx=ii;xx < nr; xx++) {
U[jj][xx] = subtrCmplxNum(U[jj][xx],multCmplxNum(L[jj][ii], U[ii][xx]))
}
}
}
var det_res = new Array()
det_res.push(1.0)
det_res.push(0.0)
for (vv=0; vv < nr; vv++) {
det_res = multCmplxNum(det_res,multCmplxNum(U[vv][vv],L[vv][vv]))
}
det_res[0] = det_res[0] * ((-1)**num_swaps)
det_res[1] = det_res[1] * ((-1)**num_swaps)
return [U,L,P,num_swaps,det_res]
}
function mult_matrices(A,B) {
var nra = A.length
var nca = A[0].length
var nrb = B.length
var ncb = B[0].length
var res_valid = true
if (nca != nrb) {
res_valid = false
}
var C = new Array()
C = create_const_matrix(nra,ncb,0)
for (aa = 0; aa < nra; aa++) {
for (bb = 0; bb < ncb; bb++) {
var tmp_res = new Array()
tmp_res.push(0.0)
tmp_res.push(0.0)
for (cc = 0; cc < nca; cc++) {
tmp_res = addCmplxNum(tmp_res, multCmplxNum(A[aa][cc],B[cc][bb]))
}
C[aa][bb] = tmp_res
}
}
return [C,res_valid]
}
function back_subst(U,d) {
var nr = d.length
var x = new Array()
x = create_const_matrix(nr,1,0)
x[nr - 1][0] = divCmplxNum(d[nr-1][0],U[nr-1][nr-1]);
for (i=nr-2; i >= 0; i--) {
var sum_ux = new Array()
sum_ux.push(0.0)
sum_ux.push(0.0)
for (j=i+1; j < nr; j++) {
sum_ux = addCmplxNum(sum_ux,multCmplxNum(U[i][j],x[j][0]));
}
var one_cmplx = new Array()
one_cmplx.push(1.0)
one_cmplx.push(0.0)
x[i][0] = multCmplxNum(divCmplxNum(one_cmplx,U[i][i]), subtrCmplxNum(d[i][0],sum_ux));
}
return x;
}
function fwd_subst(L,b) {
var d = new Array()
var nr = b.length
d = create_const_matrix(nr,1,0)
d[0][0] = b[0][0]
for (i=1; i < nr; i++) {
var sum_ld = new Array();
sum_ld.push(0.0)
sum_ld.push(0.0)
for (j=0; j < i; j++) {
sum_ld = addCmplxNum(sum_ld,multCmplxNum(L[i][j],d[j][0]));
}
d[i][0] = subtrCmplxNum(b[i][0],sum_ld);
}
return d;
}
function extract_column(M,k) {
var R = new Array()
var dim = M.length
R = create_const_matrix(dim,1,0)
for (ii = 0; ii < dim; ii++) {
R[ii][0] = M[ii][k]
}
return R
}
function calc_norm(M) {
var nr = M.length
var nc = M[0].length
var sum_sq = 0.0
for (ii = 0; ii < nr; ii++) {
for (jj=0; jj < nc; jj++) {
sum_sq = sum_sq + (M[ii][jj] * M[ii][jj])
}
}
return Math.sqrt(sum_sq)
}
function mult_matrix_by_scalar(M,k) {
var nr = M.length
var nc = M[0].length
var R = create_const_matrix(nr,nc,0)
for (ii = 0; ii < nr; ii++) {
for (jj=0; jj < nc; jj++) {
R[ii][jj] = multCmplxNum(k,M[ii][jj])
}
}
return R
}
function dot_product(a,b) {
var vec_len = a[0].length
var res = 0.0
for (ii=0; ii < vec_len; ii++) {
res = res + (a[0][ii] * b[ii][0])
}
return res
}
function add_matrices(A,B) {
var C = new Array()
var nr = A.length
var nc = A[0].length
var nrb = B.length
var ncb = B[0].length
C = create_const_matrix(nr,nc,0)
var res_valid = true
if (nr != nrb || nc != ncb) {
res_valid = false
}
for (ii=0; ii<nr; ii++) {
for (jj=0; jj<nc; jj++) {
C[ii][jj] = addCmplxNum(A[ii][jj],B[ii][jj])
}
}
return [C,res_valid]
}
function mult_elem_matrices(A,B) {
var C = new Array()
var nr = A.length
var nc = A[0].length
var nrb = B.length
var ncb = B[0].length
C = create_const_matrix(nr,nc,0)
var res_valid = true
if (nr != nrb || nc != ncb) {
res_valid = false
}
for (ii=0; ii<nr; ii++) {
for (jj=0; jj<nc; jj++) {
C[ii][jj] = multCmplxNum(A[ii][jj],B[ii][jj])
}
}
return [C,res_valid]
}
function kronecker(A,B) {
var C = new Array()
var nr = A.length
var nc = A[0].length
var nrb = B.length
var ncb = B[0].length
C = create_const_matrix(nr*nrb,nc*ncb,0)
var ii = 0
var jj = 0
var kk = 0
var mm = 0
for (ii=0; ii<nr; ii++) {
for (jj=0; jj<nc; jj++) {
var Mtmp = new Array()
Mtmp = mult_matrix_by_scalar(B,A[ii][jj])
for (kk=0; kk<nrb; kk++) {
for (mm=0; mm<ncb; mm++) {
C[(ii*nrb)+kk][(jj*ncb)+mm] = Mtmp[kk][mm]
}
}
}
}
return [C,true]
}
function subtr_matrices(A,B) {
var C = new Array()
var nr = A.length
var nc = A[0].length
var nrb = B.length
var ncb = B[0].length
C = create_const_matrix(nr,nc,0)
var res_valid = true
if (nr != nrb|| nc != ncb) {
res_valid = false
}
for (ii=0; ii<nr; ii++) {
for (jj=0; jj<nc; jj++) {
C[ii][jj] = subtrCmplxNum(A[ii][jj],B[ii][jj])
}
}
return [C,res_valid]
}
var select = document.getElementById("SelectFile");
select.onchange = function(){
selectedString = select.options[select.selectedIndex].value
document.getElementById("p2").innerHTML = selectedString
}
$(document).ready(function() {
if(isAPIAvailable()) {
$('#files').bind('change', handleFileSelect);
}
});
function isAPIAvailable() {
// Check for the various File API support.
if (window.File && window.FileReader && window.FileList && window.Blob) {
// Great success! All the File APIs are supported.
return true;
} else {
// source: File API availability - http://caniuse.com/#feat=fileapi
// source: <output> availability - http://html5doctor.com/the-output-element/
document.writeln('The HTML5 APIs used in this form are only available in the following browsers:<br />');
// 6.0 File API & 13.0 <output>
document.writeln(' - Google Chrome: 13.0 or later<br />');
// 3.6 File API & 6.0 <output>
document.writeln(' - Mozilla Firefox: 6.0 or later<br />');
// 10.0 File API & 10.0 <output>
document.writeln(' - Internet Explorer: Not supported (partial support expected in 10.0)<br />');
// ? File API & 5.1 <output>
document.writeln(' - Safari: Not supported<br />');
// ? File API & 9.2 <output>
document.writeln(' - Opera: Not supported');
return false;
}
}
function handleFileSelect(evt) {
var files = evt.target.files; // FileList object
var file = files[0];
// read the file metadata
var output = ''
output += '<span style="font-weight:bold;">' + escape(file.name) + '</span><br />\n';
output += ' - FileType: ' + (file.type || 'n/a') + '<br />\n';
output += ' - FileSize: ' + file.size + ' bytes<br />\n';
output += ' - LastModified: ' + (file.lastModifiedDate ? file.lastModifiedDate.toLocaleDateString() : 'n/a') + '<br />\n';
// read the file contents
printTextArea(file);
// post the results
$('#list').append(output);
}
var selectedString = "Input_A"
var select = document.getElementById("SelectFile");
select.onchange = function(){
selectedString = select.options[select.selectedIndex].value
document.getElementById("p2").innerHTML = selectedString
}
var selectedString2 = "A_plus_B"
var select2 = document.getElementById("SelectOperation");
select2.onchange = function(){
selectedString2 = select2.options[select2.selectedIndex].value
document.getElementById("p3").innerHTML = selectedString2
}
var Res_mat = new Array()
function printTextArea(file) {
var reader = new FileReader();
reader.readAsText(file);
reader.onload = function(event){
var csv = event.target.result;
var data = $.csv.toArrays(csv);
var mystr = "";
for(var row in data) {
for (ii=0; ii < data[row].length; ii++) {
mystr += data[row][ii].toString()
if (ii < data[row].length-1) {
mystr += ","
}
}
mystr += "\n"
}
mystr = mystr.substring(0, mystr.length - 1)
if (selectedString == "Input_A") {
document.getElementById("InpABox").value = mystr;
} else {
document.getElementById("InpBBox").value = mystr;
}
};
reader.onerror = function(){ alert('Unable to read ' + file.fileName); };
}
/* Complex calculation related functions */
function genStringFromCmplx(A,dp) {
var res = ""
if (A[1] < 0) {
res = A[0].toFixed(dp) + " - " + Math.abs(A[1]).toFixed(dp) + "i"
} else {
res = A[0].toFixed(dp) + " + " + A[1].toFixed(dp) + "i"
}
return res
}
function conjCmplxNum(A) {
C = new Array()
C.push(A[0])
C.push(-A[1])
return C
}
function magnCmplxNum(A) {
return Math.sqrt((A[0]*A[0]) + (A[1]*A[1]))
}
function powCmplxNum(A,x) {
var r = Math.sqrt((A[0]*A[0]) + (A[1]*A[1]))
var theta = Math.atan2(A[1],A[0])
console.log("r is ")
console.log("theta is ", theta)
var res_re = Math.pow(r,x) * Math.cos(x*theta)
var res_im = Math.pow(r,x) * Math.sin(x*theta)
var C = new Array()
C.push(res_re)
C.push(res_im)
return C
}
function addCmplxNum(A,B){
C = new Array()
C.push(A[0] + B[0])
C.push(A[1] + B[1])
return C
}
function subtrCmplxNum(A,B){
C = new Array()
C.push(A[0] - B[0])
C.push(A[1] - B[1])
return C
}
function multCmplxNum(A,B){
C = new Array()
C.push((A[0]*B[0]) - (A[1]*B[1]))
C.push((A[0]*B[1]) + (A[1]*B[0]))
return C
}
function divCmplxNum(A,B){
C = new Array()
var magn = (B[0] * B[0]) + (B[1] * B[1])
C = multCmplxNum(A,conjCmplxNum(B))
C[0] = C[0]/magn
C[1] = C[1]/magn
return C
}
function genCmplxFromString(str) {
var NumCmplx = new Array()
NumCmplx.push(0.0)
NumCmplx.push(0.0)
var isCmplex = str.includes("i")
var scaleFac = +1
if (isCmplex) {
var idx_i = str.indexOf("i")
var ii = idx_i
var sgn_idx = 0
var sgn_found = false
for (ii = idx_i; ii >= 0; ii--) {
if ((str[ii] == "+" || (str[ii] == "-" && str[ii-1]!= "e")) && (sgn_found == false)) {
if (str[ii] == "-") {
scaleFac = -1
}
sgn_idx = ii
sgn_found = true
}
}
var real_str = str.substring(0,sgn_idx)
var imag_i_str = str.substring(sgn_idx + 1, str.length)
var imag_str = imag_i_str.replace("i","")
var imag_str = imag_str.replace(/ +/g, ' ');
NumCmplx[0] = Number(real_str)
NumCmplx[1] = scaleFac * Number(imag_str)
console.log("Scale fac ",scaleFac)
if (imag_str == ' ' || imag_str == '') {
NumCmplx[1] = scaleFac * 1
}
if (sgn_found == false) {
NumCmplx[0] = 0
NumCmplx[1] = Number(str.replace("i",""))
// Corner case if we have just "i"
if (str.replace("i","").replace(/ +/g, ' ')== "") {
NumCmplx[1] = 1
}
}
} else {
NumCmplx[0] = Number(str)
NumCmplx[1] = 0
}
console.log("Real ",NumCmplx[0])
console.log("Imag ",NumCmplx[1])
return NumCmplx
}
/* The calculate function called when the Perform LU Decomposition button is pressed
*/
function calc() {
var dp = Number(document.getElementById("prec").value)
var inpstr = document.getElementById("InpABox").value;
var check_params = new Array()
check_params = validate_matrix(inpstr)
var inpstrB = document.getElementById("InpBBox").value;
var check_paramsB = new Array()
check_paramsB = validate_matrix(inpstrB)
if (check_params[0] == 0) {
alert("All rows should have the same number of entries for InputA!")
}
if (check_paramsB[0] == 0) {
alert("All rows should have the same number of entries for InputB!")
}
var A = new Array()
A = check_params[2]
var B = new Array()
B = check_paramsB[2]
var res_mult = new Array()
var res_add = new Array()
var res_sub = new Array()
var Res_mat = new Array()
if (selectedString2 == "A + B") {
res_add = add_matrices(A,B)
Res_mat = res_add[0]
} else if (selectedString2 == "A - B") {
res_sub = subtr_matrices(A,B)
Res_mat = res_sub[0]
} else if (selectedString2 == "B - A") {
res_sub = subtr_matrices(B,A)
Res_mat = res_sub[0]
} else if (selectedString2 == "A * B") {
res_mult = mult_matrices(A,B)
Res_mat = res_mult[0]
} else if (selectedString2 == "B * A") {
res_mult = mult_matrices(B,A)
Res_mat = res_mult[0]
} else if (selectedString2 == "A .* B") {
res_mult = mult_elem_matrices(A,B)
Res_mat = res_mult[0]
} else if (selectedString2 == "A kron B") {
res_mult = kronecker(A,B)
Res_mat = res_mult[0]
} else if (selectedString2 == "B kron A") {
res_mult = kronecker(B,A)
Res_mat = res_mult[0]
} else {
res_add = add_matrices(A,B)
Res_mat = res_add[0]
}
var Res_mat_str = ""
Res_mat_str = gen_printable_string_from_matrix(Res_mat,dp,true)
if (res_add[1] == false || res_sub[1] == false || res_mult[1] == false) {
Res_mat_str = "Invalid Result, matrices have incompatible sizes!"
}
document.getElementById("C").innerHTML = Res_mat_str;
}
</script>
Alijah Ahmedhttp://www.blogger.com/profile/01151589526613233918noreply@blogger.comtag:blogger.com,1999:blog-8441309013858171051.post-89577052248619203052020-05-17T03:33:00.001-07:002020-05-17T03:33:33.486-07:00Matrix Utilities: QR factorisation using Householder reflections for Complex Matrix<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-csv/0.8.3/jquery.csv.min.js"></script>
<script>
$(document).ready(function() {
if(isAPIAvailable()) {
$('#files').bind('change', handleFileSelect);
}
});
function isAPIAvailable() {
// Check for the various File API support.
if (window.File && window.FileReader && window.FileList && window.Blob) {
// Great success! All the File APIs are supported.
return true;
} else {
// source: File API availability - http://caniuse.com/#feat=fileapi
// source: <output> availability - http://html5doctor.com/the-output-element/
document.writeln('The HTML5 APIs used in this form are only available in the following browsers:<br />');
// 6.0 File API & 13.0 <output>
document.writeln(' - Google Chrome: 13.0 or later<br />');
// 3.6 File API & 6.0 <output>
document.writeln(' - Mozilla Firefox: 6.0 or later<br />');
// 10.0 File API & 10.0 <output>
document.writeln(' - Internet Explorer: Not supported (partial support expected in 10.0)<br />');
// ? File API & 5.1 <output>
document.writeln(' - Safari: Not supported<br />');
// ? File API & 9.2 <output>
document.writeln(' - Opera: Not supported');
return false;
}
}
function handleFileSelect(evt) {
var files = evt.target.files; // FileList object
var file = files[0];
// read the file metadata
var output = ''
output += '<span style="font-weight:bold;">' + escape(file.name) + '</span><br />\n';
output += ' - FileType: ' + (file.type || 'n/a') + '<br />\n';
output += ' - FileSize: ' + file.size + ' bytes<br />\n';
output += ' - LastModified: ' + (file.lastModifiedDate ? file.lastModifiedDate.toLocaleDateString() : 'n/a') + '<br />\n';
// read the file contents
printTextArea(file);
// post the results
$('#list').append(output);
}
function printTextArea(file) {
var reader = new FileReader();
reader.readAsText(file);
reader.onload = function(event){
var csv = event.target.result;
var data = $.csv.toArrays(csv);
var mystr = "";
for(var row in data) {
for (ii=0; ii < data[row].length; ii++) {
mystr += data[row][ii].toString()
if (ii < data[row].length-1) {
mystr += ","
}
}
mystr += "\n"
}
mystr = mystr.substring(0, mystr.length - 1)
document.getElementById("InpBox").value = mystr;
};
reader.onerror = function(){ alert('Unable to read ' + file.fileName); };
}
</script>
<body>
<script>
$(document).ready(function(){
$(".special").css("background-color", "white");
$(".special").css("width", "600px");
$(".special").css("height", "150px");
$(".special").css("font-size", "0.9em");
$(".special").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".special").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".special").css("font-weight","normal");
$(".special").css("border","1px solid black");
$(".precision").css("font-size", "1.0em");
$(".results").css("background-color", "pink");
$(".results").css("width", "600px");
$(".results").css("height", "150px");
$(".results").css("font-size", "0.9em");
$(".results").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".results").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".results").css("font-weight","normal");
$(".results").css("border","1px solid black");
$(".invres").css("background-color", "yellow");
$(".invres").css("width", "600px");
$(".invres").css("height", "100px");
$(".invres").css("font-size", "0.9em");
$(".invres").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".invres").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".invres").css("font-weight","normal");
$(".invres").css("border","1px solid black");
});
</script>
<p style="text-align:justify">This blog post implements the QR factorisation of a square complex matrix, using Householder reflections.</p>
<p style="text-align:justify">
For the (square) matrix entry, comma separated (the separation can also be a single whitespace) numbers need to be entered into the Input box, with no newline after the last line. A comma at the end of each line can be added optionally for comma separated numbers. Alternatively, you can load a CSV file into the Input box, by clicking on the Choose File button.</p>
<p style="text-align:justify">The precision of the results in decimal places can be specified in the text box headed "Precision (decimal places)". To perform the calculation, simply press the button labelled "Perform QR factorisation" further down this page. Two matrices will be printed in coloured textareas: the Q matrix which is orthogonal, and the upper-triangular R matrix. In addition, intermediate values of the Q and R matrices will be displayed at the bottom.</p>
<br>
Precision (decimal places)
<br>
<textarea class="precision" name="textarea" rows="2" cols="10" id ="prec">
</textarea>
<br>
<br>
<div id=inputs class=clearfix>
<input type=file id=files name=files[] single />
</div>
<br>
<br>
<input name="b1" onclick="clear_input()" style="margin-left: 0px;" type="Button" value="Clear Input" />
<br>
<br>
Input<br>
<textarea class="special" name="textarea" rows="20" cols="50" id ="InpBox">
</textarea>
<br>
<input name="b1" onclick="calc()" style="margin-left: 0px;" type="Button" value="Perform QR factorisation" />
<br>
<br>
Q Matrix
<br>
<textarea class="results" rows="20" cols="50" id ="Q_hr">
</textarea>
<br>
R Matrix
<br>
<textarea class="results" rows="20" cols="50" id ="R_hr">
</textarea>
<br>
<br>
<p id="p1">Householder intermediates...</p>
<br>
<br>
</body>
</html>
<script language="JavaScript">
window.onload = set_inputs();
function set_inputs(){
var str = "1+i,2,4" + "\n" + "3,4-0.5i,6" + "\n" + "8,6,7i"
document.getElementById("InpBox").value = str;
document.getElementById("prec").value = "5";
}
function clear_input() {
document.getElementById("InpBox").value = "";
}
function validate_matrix(inpstr) {
// The Matrix proper
var M = new Array()
var inp_arr = new Array()
inp_arr = inpstr.split("\n");
var num_rows = inp_arr.length
if (inp_arr[num_rows - 1] == "")
num_rows = num_rows - 1
var splitchar = ","
var tmpStr = inp_arr[0]
if (tmpStr.indexOf(',') == -1) {
splitchar = " "
}
var row_arr_filtered = inp_arr[0].split(splitchar).filter(function (el) {
return el != "";
});
var num_cols
var num_cols = row_arr_filtered.length
var dbg_str = ""
var cols_match = 1
// Check if all rows have same number of elements
for (ii = 0; ii < num_rows; ii++) {
var vec = inp_arr[ii].split(splitchar).filter(function (el) {
return el != "";
});
var num_vec = new Array()
for (kk=0; kk < vec.length; kk++) {
num_vec.push(genCmplxFromString(vec[kk]))
}
M.push(num_vec)
if (ii == 0){
num_cols = vec.length
} else {
if (num_cols != vec.length) {
cols_match = 0
}
}
}
var dbg_str = ""
dbg_str += "Number of rows is " + num_rows.toString()
dbg_str += ", number of columns is " + num_cols.toString()
var isSquare = 1
if (num_rows != num_cols) {
isSquare = 0
}
return [cols_match, dbg_str,M, isSquare]
}
function gen_printable_string_from_matrix(M,dp,isTextArea)
{
var Nr = M.length
var Nc = M[0].length
var res_str = ""
for (i=0; i < Nr; i++)
{
for (j=0; j < Nc;j++)
{
res_str += genStringFromCmplx(M[i][j],dp)
if (j < Nc-1)
res_str += ", "
}
if (isTextArea) {
res_str += "\n"
} else {
res_str += "<br>"
}
}
return res_str
}
function create_const_matrix(m,n,val)
{
var M_res = new Array
for (i=0; i < m; i++)
{
var vec = new Array
for (j=0; j < n;j++)
{
var C = new Array()
C.push(val)
C.push(val)
vec.push(C)
}
M_res.push(vec)
}
return M_res
}
function transpose_matrix(M)
{
var nr = M.length
var nc = M[0].length
var Mres = create_const_matrix(nc,nr,0);
for (i=0; i < nc; i++)
{
for (j=0; j < nr;j++)
{
Mres[i][j]=conjCmplxNum(M[j][i])
}
}
return Mres
}
function get_max_idx(V,offs) {
var idx = 0
var maxVal = 0
for (k=0; k < V.length; k++) {
if (k == 0) {
maxVal = V[k]
} else {
if (magnCmplxNum(V[k]) > magnCmplxNum(maxVal)) {
maxVal = V[k]
idx = k
}
}
}
return idx+offs
}
function swap_rows(Minp,i,j) {
var M_res = Minp
var nr = Minp.length
var nc = Minp[0].length
var tmpVal = 0
for (k=0; k < nc; k++) {
tmpVal = M_res[i][k]
M_res[i][k] = M_res[j][k]
M_res[j][k] = tmpVal
}
return M_res
}
function lu_factorisation_partial_pivot(M) {
var U = M
var nr = U.length
var L = create_const_matrix(nr,nr,0);
var P = create_const_matrix(nr,nr,0);
var num_swaps = 0
for (k=0; k < nr; k++) {
var Z = new Array()
Z.push(1.0)
Z.push(0.0)
L[k][k] = Z
P[k][k] = Z
}
for (ii=0; ii< nr-1; ii++) {
var vec = new Array()
for (jj=ii; jj < nr; jj++) {
vec.push(U[jj][ii])
}
var elem = 0.0
var idx = get_max_idx(vec,ii)
var el_tmp = 0.0
for (jj =ii; jj < nr; jj++ ) {
el_tmp = U[ii][jj]
U[ii][jj] = U[idx][jj]
U[idx][jj] = el_tmp
}
for (jj =0; jj < ii; jj++ ) {
el_tmp = L[ii][jj]
L[ii][jj] = L[idx][jj]
L[idx][jj] = el_tmp
}
if (ii != idx) {
num_swaps += 1
}
Pnew = swap_rows(P,ii,idx)
P = Pnew
for (jj = ii + 1; jj < nr; jj++) {
if (magnCmplxNum(U[jj][ii])<1e-16) {
var C = new Array()
C.push(0.0)
C.push(0.0)
L[jj][ii] = C
} else {
L[jj][ii] = divCmplxNum(U[jj][ii],U[ii][ii])
}
for (xx=ii;xx < nr; xx++) {
U[jj][xx] = subtrCmplxNum(U[jj][xx],multCmplxNum(L[jj][ii], U[ii][xx]))
}
}
}
var det_res = new Array()
det_res.push(1.0)
det_res.push(0.0)
for (vv=0; vv < nr; vv++) {
det_res = multCmplxNum(det_res,multCmplxNum(U[vv][vv],L[vv][vv]))
}
det_res[0] = det_res[0] * ((-1)**num_swaps)
det_res[1] = det_res[1] * ((-1)**num_swaps)
return [U,L,P,num_swaps,det_res]
}
function mult_matrices(A,B) {
var nra = A.length
var nca = A[0].length
var nrb = B.length
var ncb = B[0].length
if (nca != nrb) {
alert("Cannot multiply these matrics!")
}
var C = new Array()
C = create_const_matrix(nra,ncb,0)
for (aa = 0; aa < nra; aa++) {
for (bb = 0; bb < ncb; bb++) {
var tmp_res = new Array()
tmp_res.push(0.0)
tmp_res.push(0.0)
for (cc = 0; cc < nca; cc++) {
tmp_res = addCmplxNum(tmp_res, multCmplxNum(A[aa][cc],B[cc][bb]))
}
C[aa][bb] = tmp_res
}
}
return C
}
function extract_column(M,k) {
var R = new Array()
var dim = M.length
R = create_const_matrix(dim,1,0)
for (ii = 0; ii < dim; ii++) {
R[ii][0] = M[ii][k]
}
return R
}
function back_subst(U,d) {
var nr = d.length
var x = new Array()
x = create_const_matrix(nr,1,0)
x[nr - 1][0] = divCmplxNum(d[nr-1][0],U[nr-1][nr-1]);
for (i=nr-2; i >= 0; i--) {
var sum_ux = new Array()
sum_ux.push(0.0)
sum_ux.push(0.0)
for (j=i+1; j < nr; j++) {
sum_ux = addCmplxNum(sum_ux,multCmplxNum(U[i][j],x[j][0]));
}
var one_cmplx = new Array()
one_cmplx.push(1.0)
one_cmplx.push(0.0)
x[i][0] = multCmplxNum(divCmplxNum(one_cmplx,U[i][i]), subtrCmplxNum(d[i][0],sum_ux));
}
return x;
}
function fwd_subst(L,b) {
var d = new Array()
var nr = b.length
d = create_const_matrix(nr,1,0)
d[0][0] = b[0][0]
for (i=1; i < nr; i++) {
var sum_ld = new Array();
sum_ld.push(0.0)
sum_ld.push(0.0)
for (j=0; j < i; j++) {
sum_ld = addCmplxNum(sum_ld,multCmplxNum(L[i][j],d[j][0]));
}
d[i][0] = subtrCmplxNum(b[i][0],sum_ld);
}
return d;
}
/* Complex calculation related functions */
function genStringFromCmplx(A,dp) {
var res = ""
if (A[1] < 0) {
res = A[0].toFixed(dp) + " - " + Math.abs(A[1]).toFixed(dp) + "i"
} else {
res = A[0].toFixed(dp) + " + " + A[1].toFixed(dp) + "i"
}
return res
}
function conjCmplxNum(A) {
C = new Array()
C.push(A[0])
C.push(-A[1])
return C
}
function magnCmplxNum(A) {
return Math.sqrt((A[0]*A[0]) + (A[1]*A[1]))
}
function powCmplxNum(A,x) {
var r = Math.sqrt((A[0]*A[0]) + (A[1]*A[1]))
var theta = Math.atan2(A[1],A[0])
console.log("r is ")
console.log("theta is ", theta)
var res_re = Math.pow(r,x) * Math.cos(x*theta)
var res_im = Math.pow(r,x) * Math.sin(x*theta)
var C = new Array()
C.push(res_re)
C.push(res_im)
return C
}
function addCmplxNum(A,B){
C = new Array()
C.push(A[0] + B[0])
C.push(A[1] + B[1])
return C
}
function subtrCmplxNum(A,B){
C = new Array()
C.push(A[0] - B[0])
C.push(A[1] - B[1])
return C
}
function multCmplxNum(A,B){
C = new Array()
C.push((A[0]*B[0]) - (A[1]*B[1]))
C.push((A[0]*B[1]) + (A[1]*B[0]))
return C
}
function divCmplxNum(A,B){
C = new Array()
var magn = (B[0] * B[0]) + (B[1] * B[1])
C = multCmplxNum(A,conjCmplxNum(B))
C[0] = C[0]/magn
C[1] = C[1]/magn
return C
}
function genCmplxFromString(str) {
var NumCmplx = new Array()
NumCmplx.push(0.0)
NumCmplx.push(0.0)
var isCmplex = str.includes("i")
var scaleFac = +1
if (isCmplex) {
var idx_i = str.indexOf("i")
var ii = idx_i
var sgn_idx = 0
var sgn_found = false
for (ii = idx_i; ii >= 0; ii--) {
if ((str[ii] == "+" || (str[ii] == "-" && str[ii-1]!= "e")) && (sgn_found == false)) {
if (str[ii] == "-") {
scaleFac = -1
}
sgn_idx = ii
sgn_found = true
}
}
var real_str = str.substring(0,sgn_idx)
var imag_i_str = str.substring(sgn_idx + 1, str.length)
var imag_str = imag_i_str.replace("i","")
var imag_str = imag_str.replace(/ +/g, ' ');
NumCmplx[0] = Number(real_str)
NumCmplx[1] = scaleFac * Number(imag_str)
console.log("Scale fac ",scaleFac)
if (imag_str == ' ' || imag_str == '') {
NumCmplx[1] = scaleFac * 1
}
if (sgn_found == false) {
NumCmplx[0] = 0
NumCmplx[1] = Number(str.replace("i",""))
// Corner case if we have just "i"
if (str.replace("i","").replace(/ +/g, ' ')== "") {
NumCmplx[1] = 1
}
}
} else {
NumCmplx[0] = Number(str)
NumCmplx[1] = 0
}
console.log("Real ",NumCmplx[0])
console.log("Imag ",NumCmplx[1])
return NumCmplx
}
function calc_norm(M) {
var nr = M.length
var nc = M[0].length
var sum_sq = 0.0
for (ii = 0; ii < nr; ii++) {
for (jj=0; jj < nc; jj++) {
sum_sq = sum_sq + (magnCmplxNum(M[ii][jj]) * magnCmplxNum(M[ii][jj]))
}
}
return Math.sqrt(sum_sq)
}
function mult_matrix_by_scalar(M,k) {
var nr = M.length
var nc = M[0].length
var R = create_const_matrix(nr,nc,0)
for (ii = 0; ii < nr; ii++) {
for (jj=0; jj < nc; jj++) {
R[ii][jj] = multCmplxNum(M[ii][jj],k)
}
}
return R
}
function dot_product(a,b) {
var vec_len = a[0].length
var res = new Array()
res.push(0.0)
res.push(0.0)
for (ii=0; ii < vec_len; ii++) {
res = addCmplxNum(res,multCmplxNum(a[0][ii],b[ii][0]))
}
return res
}
function add_matrices(A,B) {
var C = new Array()
var nr = A.length
var nc = A[0].length
C = create_const_matrix(nr,nc,0)
for (ii=0; ii<nr; ii++) {
for (jj=0; jj<nc; jj++) {
C[ii][jj] = A[ii][jj] + B[ii][jj]
}
}
return C
}
function subtr_matrices(A,B) {
var C = new Array()
var nr = A.length
var nc = A[0].length
C = create_const_matrix(nr,nc,0)
for (ii=0; ii<nr; ii++) {
for (jj=0; jj<nc; jj++) {
C[ii][jj] = subtrCmplxNum(A[ii][jj],B[ii][jj])
}
}
return C
}
function householder_qr_2(A) {
var m = A.length
var n = A[0].length
var Qh_arr = new Array()
var Rh_arr = new Array()
var Rh = []
Rh = create_const_matrix(m,n,0)
for (aa=0; aa < m; aa++) {
for (bb=0; bb < n; bb++) {
Rh[aa][bb] = A[aa][bb]
}
}
msg_str = ""
var Qh = []
Qh = create_const_matrix(m,m,0)
var ii = 0
for (ii=0; ii<m; ii++) {
Qh[ii][ii][0] = 1.0
Qh[ii][ii][1] = 0.0
}
Qh_arr.push(Qh)
Rh_arr.push(Rh)
msg_str += "<br>Iteration 0<br>"
msg_str += "Q matrix " + "<br>"
msg_str += gen_printable_string_from_matrix(Qh,5,false)
msg_str += "<br>" + "R matrix " + "<br>"
msg_str += gen_printable_string_from_matrix(Rh,5,false)
msg_str += "<br>"
console.log("Qh ",gen_printable_string_from_matrix(Qh,5,false))
console.log("Rh ",gen_printable_string_from_matrix(Rh,5,false))
var jj = 0
var mm = 0
var nn = 0
for (jj=0; jj < n; jj++) {
var R_j_end_j = new Array()
R_j_end_j = create_const_matrix(n-jj,1,0)
var R_j_end_a = new Array()
R_j_end_a = create_const_matrix(n-jj,n,0)
// Assign from R
for (mm=jj; mm < n; mm++) {
R_j_end_j[mm-jj][0] = Rh[mm][jj]
for (nn=0; nn < n; nn++) {
R_j_end_a[mm-jj][nn] = Rh[mm][nn]
}
}
// Deal with the Q matrix
var Q_a_j_end = new Array()
Q_a_j_end = create_const_matrix(m,m-jj,0)
// Assign from Q
for (mm=jj; mm < m; mm++) {
for (nn=0; nn < m; nn++) {
Q_a_j_end[nn][mm-jj] = Qh[nn][mm]
}
}
console.log("Rh[jj][jj] ",Rh[jj][jj][0],Rh[jj][jj][1])
var normx = calc_norm(R_j_end_j)
console.log("normx ",normx)
var s = -Math.sign(Rh[jj][jj][0])
console.log("s ",s)
var dval = new Array()
dval.push(s*normx)
dval.push(0.0)
console.log("dval ", dval[0], dval[1])
var u1 = new Array()
u1 = subtrCmplxNum(Rh[jj][jj],dval)
console.log("u1 ", u1[0],u1[1])
// The original v
var u = new Array()
var e1 = new Array()
e1 = create_const_matrix(R_j_end_j.length,1,0)
e1[0][0][0] = 1
e1[0][0][1] = 0.0
var normx_c = new Array()
normx_c.push(normx)
normx_c.push(0.0)
// Additional scaling factor for Complex entries??
var theta = Math.atan2(R_j_end_j[0][0][1],R_j_end_j[0][0][0])
var cs = new Array()
cs.push(-1*Math.cos(theta))
cs.push(-1*Math.sin(theta))
var cmplx_scale = new Array()
cmplx_scale = multCmplxNum(normx_c,cs)
//u = subtr_matrices(R_j_end_j,mult_matrix_by_scalar(e1,normx_c))
u = subtr_matrices(R_j_end_j,mult_matrix_by_scalar(e1,cmplx_scale))
console.log("u: ",gen_printable_string_from_matrix(u,4,false))
var v = new Array()
if (calc_norm(u) < 1e-16) {
var zero_cmplx = new Array()
zero_cmplx.push(0.0)
zero_cmplx.push(0.0)
v = mult_matrix_by_scalar(u,zero_cmplx)
} else {
var rec_cnu = new Array()
rec_cnu.push(1.0/calc_norm(u))
rec_cnu.push(0.0)
v = mult_matrix_by_scalar(u,rec_cnu)
}
var w = new Array()
var one_cmplx = new Array()
one_cmplx.push(1.0)
one_cmplx.push(0.0)
w = mult_matrix_by_scalar(R_j_end_j,divCmplxNum(one_cmplx,u1))
w[0][0][0] = 1.0
w[0][0][1] = 0.0
var tau = new Array()
tau.push(-s*(u1[0]/normx))
tau.push(-s*(u1[1]/normx))
var tau_mult_w = new Array()
tau_mult_w = mult_matrix_by_scalar(w,tau)
console.log("tau_mult_w", gen_printable_string_from_matrix(tau_mult_w,4,true))
console.log("Stage 1")
var vt_mult_R_j_end_a = mult_matrices(transpose_matrix(v),R_j_end_a)
console.log("Stage 2")
console.log("v: ", gen_printable_string_from_matrix(v,4,true))
console.log("R_j_end_a: ", gen_printable_string_from_matrix(R_j_end_a,4,true))
console.log("vt_mult_R_j_end_a ", gen_printable_string_from_matrix(vt_mult_R_j_end_a,4,true))
var two_cmplx_2 = new Array()
two_cmplx_2.push(2.0)
two_cmplx_2.push(0.0)
R_j_end_a = subtr_matrices(R_j_end_a, mult_matrix_by_scalar(mult_matrices(v,vt_mult_R_j_end_a),two_cmplx_2))
console.log("R_j_end_a ",gen_printable_string_from_matrix(R_j_end_a,4,true))
console.log("Stage 3")
// Assign back to R
for (mm=jj; mm < n; mm++) {
for (nn=0; nn < n; nn++) {
Rh[mm][nn] = R_j_end_a[mm-jj][nn]
}
}
// deal with the Q matrix
var Q_a_j_end_mult_v = new Array()
console.log("Stage 4")
Q_a_j_end_mult_v = mult_matrices(Q_a_j_end,v)
console.log("Q_a_j_end_mult_v ",gen_printable_string_from_matrix(Q_a_j_end_mult_v,4,true))
console.log("Stage 5")
var two_cmplx = new Array()
two_cmplx.push(2.0)
two_cmplx.push(0.0)
Q_a_j_end = subtr_matrices(Q_a_j_end, mult_matrix_by_scalar(mult_matrices(Q_a_j_end_mult_v,transpose_matrix(v)),two_cmplx))
console.log("Q_a_j_end ", gen_printable_string_from_matrix(Q_a_j_end,4,true))
console.log("Stage 6")
// Assign back to Q
for (mm=jj; mm < m; mm++) {
for (nn=0; nn < m; nn++) {
Qh[nn][mm] = Q_a_j_end[nn][mm-jj]
}
}
Qh_arr.push(Qh)
Rh_arr.push(Rh)
var iterno_p_1 = jj + 1
msg_str += "<br>Iteration " + iterno_p_1.toString() + "<br>"
msg_str += "Q matrix " + "<br>"
msg_str += gen_printable_string_from_matrix(Qh,5,false)
msg_str += "<br>" + "R matrix " + "<br>"
msg_str += gen_printable_string_from_matrix(Rh,5,false)
msg_str += "<br>"
console.log("Qh ",gen_printable_string_from_matrix(Qh,5,false))
console.log("Rh ",gen_printable_string_from_matrix(Rh,5,false))
}
console.log("Length of Qh_arr is ",Qh_arr.length)
console.log("Length of Rh_arr is ",Rh_arr.length)
return [Qh,Rh,Qh_arr,Rh_arr, msg_str]
}
/* The calculate function called when the Perform LU Decomposition button is pressed
*/
function calc() {
var dp = Number(document.getElementById("prec").value)
var inpstr = document.getElementById("InpBox").value;
var check_params = new Array()
check_params = validate_matrix(inpstr)
if (check_params[0] == 0) {
alert("All rows should have the same number of entries!")
}
var isSquare = check_params[3]
if (isSquare == 0 && check_params[0] == 1) {
alert("Matrix needs to be square!")
}
var dbg_str = check_params[1]
var M = new Array()
M = check_params[2]
var M_str = gen_printable_string_from_matrix(M,dp,true)
var house_res = householder_qr_2(M)
Q_hr_mat = house_res[0]
R_hr_mat = house_res[1]
var Q_hr_mat_str = gen_printable_string_from_matrix(Q_hr_mat,dp,true)
var R_hr_mat_str = gen_printable_string_from_matrix(R_hr_mat,dp,true)
Q_hr_mat_arr = house_res[2]
R_hr_mat_arr = house_res[3]
var house_dbg_str = house_res[4]
console.log("M matrix", M_str)
document.getElementById("Q_hr").innerHTML = Q_hr_mat_str;
document.getElementById("R_hr").innerHTML = R_hr_mat_str;
document.getElementById("p1").innerHTML = house_dbg_str;
}
</script>
Alijah Ahmedhttp://www.blogger.com/profile/01151589526613233918noreply@blogger.comtag:blogger.com,1999:blog-8441309013858171051.post-88267932030587611282020-05-15T12:43:00.000-07:002020-05-15T12:43:31.496-07:00Matrix Utilities: QR factorisation using Gram-Schmidt for Complex Matrix<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-csv/0.8.3/jquery.csv.min.js"></script>
<script>
$(document).ready(function() {
if(isAPIAvailable()) {
$('#files').bind('change', handleFileSelect);
}
});
function isAPIAvailable() {
// Check for the various File API support.
if (window.File && window.FileReader && window.FileList && window.Blob) {
// Great success! All the File APIs are supported.
return true;
} else {
// source: File API availability - http://caniuse.com/#feat=fileapi
// source: <output> availability - http://html5doctor.com/the-output-element/
document.writeln('The HTML5 APIs used in this form are only available in the following browsers:<br />');
// 6.0 File API & 13.0 <output>
document.writeln(' - Google Chrome: 13.0 or later<br />');
// 3.6 File API & 6.0 <output>
document.writeln(' - Mozilla Firefox: 6.0 or later<br />');
// 10.0 File API & 10.0 <output>
document.writeln(' - Internet Explorer: Not supported (partial support expected in 10.0)<br />');
// ? File API & 5.1 <output>
document.writeln(' - Safari: Not supported<br />');
// ? File API & 9.2 <output>
document.writeln(' - Opera: Not supported');
return false;
}
}
function handleFileSelect(evt) {
var files = evt.target.files; // FileList object
var file = files[0];
// read the file metadata
var output = ''
output += '<span style="font-weight:bold;">' + escape(file.name) + '</span><br />\n';
output += ' - FileType: ' + (file.type || 'n/a') + '<br />\n';
output += ' - FileSize: ' + file.size + ' bytes<br />\n';
output += ' - LastModified: ' + (file.lastModifiedDate ? file.lastModifiedDate.toLocaleDateString() : 'n/a') + '<br />\n';
// read the file contents
printTextArea(file);
// post the results
$('#list').append(output);
}
function printTextArea(file) {
var reader = new FileReader();
reader.readAsText(file);
reader.onload = function(event){
var csv = event.target.result;
var data = $.csv.toArrays(csv);
var mystr = "";
for(var row in data) {
for (ii=0; ii < data[row].length; ii++) {
mystr += data[row][ii].toString()
if (ii < data[row].length-1) {
mystr += ","
}
}
mystr += "\n"
}
mystr = mystr.substring(0, mystr.length - 1)
document.getElementById("InpBox").value = mystr;
};
reader.onerror = function(){ alert('Unable to read ' + file.fileName); };
}
</script>
<body>
<script>
$(document).ready(function(){
$(".special").css("background-color", "white");
$(".special").css("width", "600px");
$(".special").css("height", "150px");
$(".special").css("font-size", "0.9em");
$(".special").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".special").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".special").css("font-weight","normal");
$(".special").css("border","1px solid black");
$(".precision").css("font-size", "1.0em");
$(".results").css("background-color", "pink");
$(".results").css("width", "600px");
$(".results").css("height", "150px");
$(".results").css("font-size", "0.9em");
$(".results").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".results").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".results").css("font-weight","normal");
$(".results").css("border","1px solid black");
$(".invres").css("background-color", "yellow");
$(".invres").css("width", "600px");
$(".invres").css("height", "100px");
$(".invres").css("font-size", "0.9em");
$(".invres").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".invres").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".invres").css("font-weight","normal");
$(".invres").css("border","1px solid black");
});
</script>
<p style="text-align:justify">This blog post implements the QR factorisation of a square matrix whose entries are complex numbers. The Gram-Schmidt method is used to calculate the orthogonal Q matrix.</p>
<p style="text-align:justify">
For the (square) matrix entry, comma separated (the separation can also be a single whitespace) numbers need to be entered into the Input box, with no newline after the last line. You can examine the pre-loaded Input textarea for guidance. A comma at the end of each line can be added optionally for comma separated numbers. Alternatively, you can load a CSV file into the Input box, by clicking on the Choose File button.</p>
<p style="text-align:justify">The precision of the results in decimal places can be specified in the text box headed "Precision (decimal places)". To perform the calculation, simply press the button labelled "Perform QR factorisation" further down this page. Two matrices will be printed in coloured textareas: the Q matrix which is orthogonal, and the upper-triangular R matrix. In addition, the matrix size and determinant will be printed.</p>
<br>
Precision (decimal places)
<br>
<textarea class="precision" name="textarea" rows="2" cols="10" id ="prec">
</textarea>
<br>
<br>
<div id=inputs class=clearfix>
<input type=file id=files name=files[] single />
</div>
<br>
<br>
<input name="b1" onclick="clear_input()" style="margin-left: 0px;" type="Button" value="Clear Input" />
<br>
<br>
Input<br>
<textarea class="special" name="textarea" rows="20" cols="50" id ="InpBox">
</textarea>
<br>
<input name="b1" onclick="calc()" style="margin-left: 0px;" type="Button" value="Perform QR factorisation" />
<br>
<p id="p1">Results pending...</p>
<br>
Q Matrix
<br>
<textarea class="results" rows="20" cols="50" id ="Q">
</textarea>
<br>
R Matrix
<br>
<textarea class="results" rows="20" cols="50" id ="R">
</textarea>
<br>
<br>
</body>
</html>
<script language="JavaScript">
window.onload = set_inputs();
function set_inputs(){
var str = "1+2i,2,4-7i" + "\n" + "3,4,6-i" + "\n" + "8,6,7i"
document.getElementById("InpBox").value = str;
document.getElementById("prec").value = "5";
}
function clear_input() {
document.getElementById("InpBox").value = "";
}
function validate_matrix(inpstr) {
// The Matrix proper
var M = new Array()
var inp_arr = new Array()
inp_arr = inpstr.split("\n");
var num_rows = inp_arr.length
if (inp_arr[num_rows - 1] == "")
num_rows = num_rows - 1
var splitchar = ","
var tmpStr = inp_arr[0]
if (tmpStr.indexOf(',') == -1) {
splitchar = " "
}
var row_arr_filtered = inp_arr[0].split(splitchar).filter(function (el) {
return el != "";
});
var num_cols
var num_cols = row_arr_filtered.length
var dbg_str = ""
var cols_match = 1
// Check if all rows have same number of elements
for (ii = 0; ii < num_rows; ii++) {
var vec = inp_arr[ii].split(splitchar).filter(function (el) {
return el != "";
});
var num_vec = new Array()
for (kk=0; kk < vec.length; kk++) {
num_vec.push(genCmplxFromString(vec[kk]))
}
M.push(num_vec)
if (ii == 0){
num_cols = vec.length
} else {
if (num_cols != vec.length) {
cols_match = 0
}
}
}
var dbg_str = ""
dbg_str += "Number of rows is " + num_rows.toString()
dbg_str += ", number of columns is " + num_cols.toString()
var isSquare = 1
if (num_rows != num_cols) {
isSquare = 0
}
return [cols_match, dbg_str,M, isSquare]
}
function create_const_matrix(m,n,val)
{
var M_res = new Array
for (i=0; i < m; i++)
{
var vec = new Array
for (j=0; j < n;j++)
{
var C = new Array()
C.push(val)
C.push(val)
vec.push(C)
}
M_res.push(vec)
}
return M_res
}
function gen_printable_string_from_matrix(M,dp,isTextArea)
{
var Nr = M.length
var Nc = M[0].length
var res_str = ""
for (i=0; i < Nr; i++)
{
for (j=0; j < Nc;j++)
{
res_str += genStringFromCmplx(M[i][j],dp)
if (j < Nc-1)
res_str += ", "
}
if (isTextArea) {
res_str += "\n"
} else {
res_str += "<br>"
}
}
return res_str
}
function gen_printable_real_string_from_matrix(M,dp,isTextArea)
{
var Nr = M.length
var Nc = M[0].length
var res_str = ""
for (i=0; i < Nr; i++)
{
for (j=0; j < Nc;j++)
{
res_str += M[i][j][0].toFixed(dp)
if (j < Nc-1)
res_str += ", "
}
if (isTextArea) {
res_str += "\n"
} else {
res_str += "<br>"
}
}
return res_str
}
function transpose_matrix(M)
{
var nr = M.length
var nc = M[0].length
var Mres = create_const_matrix(nc,nr,0);
for (i=0; i < nc; i++)
{
for (j=0; j < nr;j++)
{
Mres[i][j]= M[j][i]
}
}
return Mres
}
function htranspose_matrix(M)
{
var nr = M.length
var nc = M[0].length
var Mres = create_const_matrix(nc,nr,0);
for (i=0; i < nc; i++)
{
for (j=0; j < nr;j++)
{
Mres[i][j]= conjCmplxNum(M[j][i])
}
}
return Mres
}
function get_max_idx(V,offs) {
var idx = 0
var maxVal = 0
for (k=0; k < V.length; k++) {
if (k == 0) {
maxVal = V[k]
} else {
if (magnCmplxNum(V[k]) > magnCmplxNum(maxVal)) {
maxVal = V[k]
idx = k
}
}
}
return idx+offs
}
function swap_rows(Minp,i,j) {
var M_res = Minp
var nr = Minp.length
var nc = Minp[0].length
var tmpVal = 0
for (k=0; k < nc; k++) {
tmpVal = M_res[i][k]
M_res[i][k] = M_res[j][k]
M_res[j][k] = tmpVal
}
return M_res
}
function lu_factorisation_partial_pivot(M) {
var U = M
var nr = U.length
var L = create_const_matrix(nr,nr,0);
var P = create_const_matrix(nr,nr,0);
var num_swaps = 0
for (k=0; k < nr; k++) {
var Z = new Array()
Z.push(1.0)
Z.push(0.0)
L[k][k] = Z
P[k][k] = Z
}
for (ii=0; ii< nr-1; ii++) {
var vec = new Array()
for (jj=ii; jj < nr; jj++) {
vec.push(U[jj][ii])
}
var elem = 0.0
var idx = get_max_idx(vec,ii)
var el_tmp = 0.0
for (jj =ii; jj < nr; jj++ ) {
el_tmp = U[ii][jj]
U[ii][jj] = U[idx][jj]
U[idx][jj] = el_tmp
}
for (jj =0; jj < ii; jj++ ) {
el_tmp = L[ii][jj]
L[ii][jj] = L[idx][jj]
L[idx][jj] = el_tmp
}
if (ii != idx) {
num_swaps += 1
}
Pnew = swap_rows(P,ii,idx)
P = Pnew
for (jj = ii + 1; jj < nr; jj++) {
if (magnCmplxNum(U[jj][ii])<1e-16) {
var C = new Array()
C.push(0.0)
C.push(0.0)
L[jj][ii] = C
} else {
L[jj][ii] = divCmplxNum(U[jj][ii],U[ii][ii])
}
for (xx=ii;xx < nr; xx++) {
U[jj][xx] = subtrCmplxNum(U[jj][xx],multCmplxNum(L[jj][ii], U[ii][xx]))
}
}
}
var det_res = new Array()
det_res.push(1.0)
det_res.push(0.0)
for (vv=0; vv < nr; vv++) {
det_res = multCmplxNum(det_res,multCmplxNum(U[vv][vv],L[vv][vv]))
}
det_res[0] = det_res[0] * ((-1)**num_swaps)
det_res[1] = det_res[1] * ((-1)**num_swaps)
return [U,L,P,num_swaps,det_res]
}
function mult_matrices(A,B) {
var nra = A.length
var nca = A[0].length
var nrb = B.length
var ncb = B[0].length
if (nca != nrb) {
alert("Cannot multiply these matrics!")
}
var C = new Array()
C = create_const_matrix(nra,ncb,0)
for (aa = 0; aa < nra; aa++) {
for (bb = 0; bb < ncb; bb++) {
var tmp_res = new Array()
tmp_res.push(0.0)
tmp_res.push(0.0)
for (cc = 0; cc < nca; cc++) {
tmp_res = addCmplxNum(tmp_res, multCmplxNum(A[aa][cc],B[cc][bb]))
}
C[aa][bb] = tmp_res
}
}
return C
}
function back_subst(U,d) {
var nr = d.length
var x = new Array()
x = create_const_matrix(nr,1,0)
x[nr - 1][0] = divCmplxNum(d[nr-1][0],U[nr-1][nr-1]);
for (i=nr-2; i >= 0; i--) {
var sum_ux = new Array()
sum_ux.push(0.0)
sum_ux.push(0.0)
for (j=i+1; j < nr; j++) {
sum_ux = addCmplxNum(sum_ux,multCmplxNum(U[i][j],x[j][0]));
}
var one_cmplx = new Array()
one_cmplx.push(1.0)
one_cmplx.push(0.0)
x[i][0] = multCmplxNum(divCmplxNum(one_cmplx,U[i][i]), subtrCmplxNum(d[i][0],sum_ux));
}
return x;
}
function fwd_subst(L,b) {
var d = new Array()
var nr = b.length
d = create_const_matrix(nr,1,0)
d[0][0] = b[0][0]
for (i=1; i < nr; i++) {
var sum_ld = new Array();
sum_ld.push(0.0)
sum_ld.push(0.0)
for (j=0; j < i; j++) {
sum_ld = addCmplxNum(sum_ld,multCmplxNum(L[i][j],d[j][0]));
}
d[i][0] = subtrCmplxNum(b[i][0],sum_ld);
}
return d;
}
function extract_column(M,k) {
var R = new Array()
var dim = M.length
R = create_const_matrix(dim,1,0)
for (ii = 0; ii < dim; ii++) {
R[ii][0] = M[ii][k]
}
return R
}
function calc_norm(M) {
var nr = M.length
var nc = M[0].length
var sum_sq = 0.0
for (ii = 0; ii < nr; ii++) {
for (jj=0; jj < nc; jj++) {
sum_sq = sum_sq + (magnCmplxNum(M[ii][jj]) * magnCmplxNum(M[ii][jj]))
}
}
return Math.sqrt(sum_sq)
}
function mult_matrix_by_scalar(M,k) {
var nr = M.length
var nc = M[0].length
var R = create_const_matrix(nr,nc,0)
for (ii = 0; ii < nr; ii++) {
for (jj=0; jj < nc; jj++) {
R[ii][jj] = multCmplxNum(M[ii][jj],k)
}
}
return R
}
function dot_product(a,b) {
var vec_len = a[0].length
var res = new Array()
res.push(0.0)
res.push(0.0)
for (ii=0; ii < vec_len; ii++) {
res = addCmplxNum(res,multCmplxNum(a[0][ii],b[ii][0]))
}
return res
}
function add_matrices(A,B) {
var C = new Array()
var nr = A.length
var nc = A[0].length
C = create_const_matrix(nr,nc,0)
for (ii=0; ii<nr; ii++) {
for (jj=0; jj<nc; jj++) {
C[ii][jj] = A[ii][jj] + B[ii][jj]
}
}
return C
}
function subtr_matrices(A,B) {
var C = new Array()
var nr = A.length
var nc = A[0].length
C = create_const_matrix(nr,nc,0)
for (ii=0; ii<nr; ii++) {
for (jj=0; jj<nc; jj++) {
C[ii][jj] = subtrCmplxNum(A[ii][jj],B[ii][jj])
}
}
return C
}
function gram_schmidt(A) {
var dim = A.length
var U = new Array()
var E = new Array()
var u = new Array()
var e = new Array()
var a = new Array()
var dbg_info = "Debug Info"
U = create_const_matrix(dim,dim,0)
E = create_const_matrix(dim,dim,0)
u = create_const_matrix(dim,1,0)
e = create_const_matrix(dim,1,0)
a = create_const_matrix(dim,1,0)
dbg_info += " Dimension is " + dim.toString() + "<br>"
var jj = 0
var kk = 0
var mm = 0
var ii = 0
for (jj = 0; jj < dim; jj++) {
dbg_info += ("Index " + jj.toString() + "<br>")
if (jj == 0) {
u = extract_column(A,jj)
dbg_info += ("u<br>" + gen_printable_string_from_matrix(u,4,false) + "<br>")
var scaleFac = new Array()
scaleFac.push(1.0/calc_norm(u))
scaleFac.push(0.0)
e = mult_matrix_by_scalar(u,scaleFac)
for (ii = 0; ii < dim; ii++) {
E[ii][jj] = e[ii][0]
}
} else {
a = extract_column(A,jj)
//console.log("Extract column jj")
//console.log(gen_printable_string_from_matrix(a,5,false))
u = a
//console.log("Assign a to u")
//console.log(gen_printable_string_from_matrix(u,5,false))
dbg_info += ("u<br>" + gen_printable_string_from_matrix(u,5,false) + "<br>")
for (kk=0; kk < jj; kk++) {
dbg_info += ("**a column:<br>" + gen_printable_string_from_matrix(a,5,false) + "<br>")
dbg_info += ("**Ekk column:<br>" + gen_printable_string_from_matrix(extract_column(E,kk),5,false) + "<br>")
console.log("E(:,kk) ", gen_printable_string_from_matrix(extract_column(E,kk),5,false))
console.log("a: ", gen_printable_string_from_matrix(a,5,false))
var inner_prod = dot_product(htranspose_matrix(extract_column(E,kk)),a)
dbg_info += ("**Inner prod: " + inner_prod.toString() + "<br>")
console.log("Inner prod ", inner_prod[0]," ",inner_prod[1])
u = subtr_matrices(u,mult_matrix_by_scalar(extract_column(E,kk),inner_prod))
console.log("Post subtraction")
console.log(gen_printable_string_from_matrix(u,5,false))
}
dbg_info += ("Final u<br>" + gen_printable_string_from_matrix(u,4,false) + "<br>")
var scaleFac2 = new Array()
scaleFac2.push(1.0/calc_norm(u))
scaleFac2.push(0.0)
console.log("Before mult matrix by scalar")
console.log(gen_printable_string_from_matrix(u,5,false))
e = mult_matrix_by_scalar(u,scaleFac2)
console.log(gen_printable_string_from_matrix(e,5,false))
console.log("After mult matrix by scalar")
for (mm = 0; mm < dim; mm++) {
E[mm][jj] = e[mm][0]
}
}
}
// Create the R matrix
var R = new Array()
R = create_const_matrix(dim,dim,0)
for (aa=0; aa<dim; aa++) {
for (bb=aa; bb<dim; bb++) {
R[aa][bb] = dot_product(htranspose_matrix(extract_column(E,aa)),extract_column(A,bb))
}
}
return [E,R,dbg_info]
}
/* Complex calculation related functions */
function genStringFromCmplx(A,dp) {
var res = ""
if (A[1] < 0) {
res = A[0].toFixed(dp) + " - " + Math.abs(A[1]).toFixed(dp) + "i"
} else {
res = A[0].toFixed(dp) + " + " + A[1].toFixed(dp) + "i"
}
return res
}
function conjCmplxNum(A) {
C = new Array()
C.push(A[0])
C.push(-A[1])
return C
}
function conjCmplxMatrix(M) {
var nr = M.length
var nc = M[0].length
var C = new Array()
C = create_const_matrix(nr,nc,0)
var ii = 0
var jj = 0
for (ii = 0; ii < nr; ii++) {
for (jj = 0; jj < nc; jj++) {
C[ii][jj] = conjCmplxNum(M[ii][jj])
}
}
return C
}
function magnCmplxNum(A) {
return Math.sqrt((A[0]*A[0]) + (A[1]*A[1]))
}
function powCmplxNum(A,x) {
var r = Math.sqrt((A[0]*A[0]) + (A[1]*A[1]))
var theta = Math.atan2(A[1],A[0])
console.log("r is ")
console.log("theta is ", theta)
var res_re = Math.pow(r,x) * Math.cos(x*theta)
var res_im = Math.pow(r,x) * Math.sin(x*theta)
var C = new Array()
C.push(res_re)
C.push(res_im)
return C
}
function addCmplxNum(A,B){
C = new Array()
C.push(A[0] + B[0])
C.push(A[1] + B[1])
return C
}
function subtrCmplxNum(A,B){
C = new Array()
C.push(A[0] - B[0])
C.push(A[1] - B[1])
return C
}
function multCmplxNum(A,B){
C = new Array()
C.push((A[0]*B[0]) - (A[1]*B[1]))
C.push((A[0]*B[1]) + (A[1]*B[0]))
return C
}
function divCmplxNum(A,B){
C = new Array()
var magn = (B[0] * B[0]) + (B[1] * B[1])
C = multCmplxNum(A,conjCmplxNum(B))
C[0] = C[0]/magn
C[1] = C[1]/magn
return C
}
function genCmplxFromString(str) {
var NumCmplx = new Array()
NumCmplx.push(0.0)
NumCmplx.push(0.0)
var isCmplex = str.includes("i")
var scaleFac = +1
if (isCmplex) {
var idx_i = str.indexOf("i")
var ii = idx_i
var sgn_idx = 0
var sgn_found = false
for (ii = idx_i; ii >= 0; ii--) {
if ((str[ii] == "+" || (str[ii] == "-" && str[ii-1]!= "e")) && (sgn_found == false)) {
if (str[ii] == "-") {
scaleFac = -1
}
sgn_idx = ii
sgn_found = true
}
}
var real_str = str.substring(0,sgn_idx)
var imag_i_str = str.substring(sgn_idx + 1, str.length)
var imag_str = imag_i_str.replace("i","")
var imag_str = imag_str.replace(/ +/g, ' ');
NumCmplx[0] = Number(real_str)
NumCmplx[1] = scaleFac * Number(imag_str)
console.log("Scale fac ",scaleFac)
if (imag_str == ' ' || imag_str == '') {
NumCmplx[1] = scaleFac * 1
}
if (sgn_found == false) {
NumCmplx[0] = 0
NumCmplx[1] = Number(str.replace("i",""))
// Corner case if we have just "i"
if (str.replace("i","").replace(/ +/g, ' ')== "") {
NumCmplx[1] = 1
}
}
} else {
NumCmplx[0] = Number(str)
NumCmplx[1] = 0
}
console.log("Real ",NumCmplx[0])
console.log("Imag ",NumCmplx[1])
return NumCmplx
}
/* The calculate function called when the Perform LU Decomposition button is pressed
*/
function calc() {
var dp = Number(document.getElementById("prec").value)
var inpstr = document.getElementById("InpBox").value;
var check_params = new Array()
check_params = validate_matrix(inpstr)
if (check_params[0] == 0) {
alert("All rows should have the same number of entries!")
}
var isSquare = check_params[3]
if (isSquare == 0 && check_params[0] == 1) {
alert("Matrix needs to be square!")
}
var dbg_str = check_params[1]
var M = new Array()
M = check_params[2]
var M_str = gen_printable_string_from_matrix(M,dp,true)
var E_mat = new Array()
gs_res = gram_schmidt(M)
console.log(M_str)
E_mat = gs_res[0]
var E_mat_str = gen_printable_string_from_matrix(E_mat,dp,true)
R_mat = gs_res[1]
var R_mat_str = gen_printable_string_from_matrix(R_mat,dp,true)
var M_col_1 = new Array()
M_col_1 = extract_column(M,1)
var norm_M_col_1 = calc_norm(M_col_1)
var M_col_1_str = gen_printable_string_from_matrix(M_col_1,dp,false)
var M_sc = new Array()
M_sc = mult_matrix_by_scalar(M,1.0)
var M_sc_str = gen_printable_string_from_matrix(M_sc,dp,false)
var Mt = new Array()
Mt = transpose_matrix(M)
var Mt_str = gen_printable_string_from_matrix(Mt,dp,true)
// The LU factorisation proper
res_lu = lu_factorisation_partial_pivot(M)
var U = res_lu[0]
var L = res_lu[1]
var P = res_lu[2]
var num_swaps = res_lu[3]
var det_res = res_lu[4]
// Stringify so as to put results in the textareas
/*
var U_str = gen_printable_string_from_matrix(U,dp,true)
var L_str = gen_printable_string_from_matrix(L,dp,true)
var P_str = gen_printable_string_from_matrix(P,0,true)
*/
var det_res = res_lu[4]
// Calculate the inverse matrix fron the LU factorisation
var dim = U.length
var inv_res = new Array()
inv_res = create_const_matrix(dim,dim,0)
var C_inv = new Array()
for (kk=0; kk < dim; kk++) {
var ei = new Array()
ei = create_const_matrix(dim,1,0);
ei[kk][0] = 1.0;
var ri = new Array()
ri = create_const_matrix(dim,1,0);
ri = back_subst(U,fwd_subst(L,ei));
for (mm=0; mm < dim; mm++) {
inv_res[mm][kk] = ri[mm][0];
}
}
C_inv = mult_matrices(inv_res,P)
var C_str = gen_printable_string_from_matrix(C_inv,dp,true)
// Append matrix determinant
dbg_str += "<br>" + "Determinant of matrix is " + genStringFromCmplx(det_res,dp)
document.getElementById("p1").innerHTML = dbg_str;
document.getElementById("Q").innerHTML = E_mat_str;
document.getElementById("R").innerHTML = R_mat_str;
}
</script>
Alijah Ahmedhttp://www.blogger.com/profile/01151589526613233918noreply@blogger.comtag:blogger.com,1999:blog-8441309013858171051.post-23367186900467629222020-05-14T09:57:00.001-07:002020-05-14T09:57:41.155-07:00Matrix Utilities: LU factorisarion and Inverse of Complex Matrix<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-csv/0.8.3/jquery.csv.min.js"></script>
<script>
$(document).ready(function() {
if(isAPIAvailable()) {
$('#files').bind('change', handleFileSelect);
}
});
function isAPIAvailable() {
// Check for the various File API support.
if (window.File && window.FileReader && window.FileList && window.Blob) {
// Great success! All the File APIs are supported.
return true;
} else {
// source: File API availability - http://caniuse.com/#feat=fileapi
// source: <output> availability - http://html5doctor.com/the-output-element/
document.writeln('The HTML5 APIs used in this form are only available in the following browsers:<br />');
// 6.0 File API & 13.0 <output>
document.writeln(' - Google Chrome: 13.0 or later<br />');
// 3.6 File API & 6.0 <output>
document.writeln(' - Mozilla Firefox: 6.0 or later<br />');
// 10.0 File API & 10.0 <output>
document.writeln(' - Internet Explorer: Not supported (partial support expected in 10.0)<br />');
// ? File API & 5.1 <output>
document.writeln(' - Safari: Not supported<br />');
// ? File API & 9.2 <output>
document.writeln(' - Opera: Not supported');
return false;
}
}
function handleFileSelect(evt) {
var files = evt.target.files; // FileList object
var file = files[0];
// read the file metadata
var output = ''
output += '<span style="font-weight:bold;">' + escape(file.name) + '</span><br />\n';
output += ' - FileType: ' + (file.type || 'n/a') + '<br />\n';
output += ' - FileSize: ' + file.size + ' bytes<br />\n';
output += ' - LastModified: ' + (file.lastModifiedDate ? file.lastModifiedDate.toLocaleDateString() : 'n/a') + '<br />\n';
// read the file contents
printTextArea(file);
// post the results
$('#list').append(output);
}
function printTextArea(file) {
var reader = new FileReader();
reader.readAsText(file);
reader.onload = function(event){
var csv = event.target.result;
var data = $.csv.toArrays(csv);
var mystr = "";
for(var row in data) {
for (ii=0; ii < data[row].length; ii++) {
mystr += data[row][ii].toString()
if (ii < data[row].length-1) {
mystr += ","
}
}
mystr += "\n"
}
mystr = mystr.substring(0, mystr.length - 1)
document.getElementById("InpBox").value = mystr;
};
reader.onerror = function(){ alert('Unable to read ' + file.fileName); };
}
</script>
<body>
<script>
$(document).ready(function(){
$(".special").css("background-color", "white");
$(".special").css("width", "600px");
$(".special").css("height", "100px");
$(".special").css("font-size", "0.9em");
$(".special").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".special").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".special").css("font-weight","normal");
$(".special").css("border","1px solid black");
$(".precision").css("font-size", "1.0em");
$(".results").css("background-color", "pink");
$(".results").css("width", "600px");
$(".results").css("height", "100px");
$(".results").css("font-size", "0.9em");
$(".results").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".results").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".results").css("font-weight","normal");
$(".results").css("border","1px solid black");
$(".invres").css("background-color", "yellow");
$(".invres").css("width", "600px");
$(".invres").css("height", "100px");
$(".invres").css("font-size", "0.9em");
$(".invres").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".invres").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".invres").css("font-weight","normal");
$(".invres").css("border","1px solid black");
});
</script>
<p style="text-align:justify">This blog post implements the LU factorisation and inverse calculation of a square matrix with complex numbers. A partial pivot method is used for the LU factorisation. The inverse matrix is calculated from the results of the LU factorisation, using backward and forward substitution.</p>
<p style="text-align:justify">
For the (square) matrix entry, comma separated (the separation can also be a single whitespace) numbers need to be entered into the Input box, with no newline after the last line. A comma at the end of each line can be added optionally for comma separated numbers. Alternatively, you can load a CSV file into the Input box, by clicking on the Choose File button.</p>
<p style="text-align:justify">The precision of the results in decimal places can be specified in the text box headed "Precision (decimal places)". To perform the calculation, simply press the button labelled "Perform LU factorisation and Inverse" further down this page. Four matrices will be printed in coloured textareas: the Lower Triangular, Upper Triangular, Permutation and Inverse. In addition, the matrix size and determinant will be printed.</p>
<br>
Precision (decimal places)
<br>
<textarea class="precision" name="textarea" rows="2" cols="10" id ="prec">
</textarea>
<br>
<br>
<div id=inputs class=clearfix>
<input type=file id=files name=files[] single />
</div>
<br>
<br>
<input name="b1" onclick="clear_input()" style="margin-left: 0px;" type="Button" value="Clear Input" />
<br>
<br>
Input<br>
<textarea class="special" name="textarea" rows="20" cols="50" id ="InpBox">
</textarea>
<br>
<input name="b1" onclick="calc()" style="margin-left: 0px;" type="Button" value="Perform LU factorisation and Inverse" />
<br>
<p id="p1">Results pending...</p>
<br>
Lower Triangular Matrix
<br>
<textarea class="results" rows="20" cols="50" id ="L">
</textarea>
<br>
Upper Triangular Matrix
<br>
<textarea class="results" rows="20" cols="50" id ="U">
</textarea>
<br>
Permutation Matrix
<br>
<textarea class="results" rows="10" cols="30" id ="P">
</textarea>
<br>
Inverse Matrix
<br>
<textarea class="invres" rows="20" cols="30" id ="I">
</textarea>
<br>
</body>
</html>
<script language="JavaScript">
window.onload = set_inputs();
function set_inputs(){
var str = "1,2-0.5i,4i" + "\n" + "3,4+2i,6-i" + "\n" + "8+1.5i,6,7"
document.getElementById("InpBox").value = str;
document.getElementById("prec").value = "5";
}
function clear_input() {
document.getElementById("InpBox").value = "";
}
function validate_matrix(inpstr) {
// The Matrix proper
var M = new Array()
var inp_arr = new Array()
inp_arr = inpstr.split("\n");
var num_rows = inp_arr.length
if (inp_arr[num_rows - 1] == "")
num_rows = num_rows - 1
var splitchar = ","
var tmpStr = inp_arr[0]
if (tmpStr.indexOf(',') == -1) {
splitchar = " "
}
var row_arr_filtered = inp_arr[0].split(splitchar).filter(function (el) {
return el != "";
});
var num_cols
var num_cols = row_arr_filtered.length
var dbg_str = ""
var cols_match = 1
// Check if all rows have same number of elements
for (ii = 0; ii < num_rows; ii++) {
var vec = inp_arr[ii].split(splitchar).filter(function (el) {
return el != "";
});
var num_vec = new Array()
for (kk=0; kk < vec.length; kk++) {
num_vec.push(genCmplxFromString(vec[kk]))
}
M.push(num_vec)
if (ii == 0){
num_cols = vec.length
} else {
if (num_cols != vec.length) {
cols_match = 0
}
}
}
var dbg_str = ""
dbg_str += "Number of rows is " + num_rows.toString()
dbg_str += ", number of columns is " + num_cols.toString()
var isSquare = 1
if (num_rows != num_cols) {
isSquare = 0
}
return [cols_match, dbg_str,M, isSquare]
}
function gen_printable_string_from_matrix(M,dp,isTextArea)
{
var Nr = M.length
var Nc = M[0].length
var res_str = ""
for (i=0; i < Nr; i++)
{
for (j=0; j < Nc;j++)
{
res_str += genStringFromCmplx(M[i][j],dp)
if (j < Nc-1)
res_str += ", "
}
if (isTextArea) {
res_str += "\n"
} else {
res_str += "<br>"
}
}
return res_str
}
function gen_printable_real_string_from_matrix(M,dp,isTextArea)
{
var Nr = M.length
var Nc = M[0].length
var res_str = ""
for (i=0; i < Nr; i++)
{
for (j=0; j < Nc;j++)
{
res_str += M[i][j][0].toFixed(dp)
if (j < Nc-1)
res_str += ", "
}
if (isTextArea) {
res_str += "\n"
} else {
res_str += "<br>"
}
}
return res_str
}
function create_const_matrix(m,n,val)
{
var M_res = new Array
for (i=0; i < m; i++)
{
var vec = new Array
for (j=0; j < n;j++)
{
var C = new Array()
C.push(val)
C.push(val)
vec.push(C)
}
M_res.push(vec)
}
return M_res
}
function create_const_real_matrix(m,n,val)
{
var M_res = new Array
for (i=0; i < m; i++)
{
var vec = new Array
for (j=0; j < n;j++)
{
vec.push(val)
}
M_res.push(vec)
}
return M_res
}
function transpose_matrix(M)
{
var nr = M.length
var nc = M[0].length
var Mres = create_const_matrix(nc,nr,0);
for (i=0; i < nc; i++)
{
for (j=0; j < nr;j++)
{
Mres[i][j]=M[j][i]
}
}
return Mres
}
function get_max_idx(V,offs) {
var idx = 0
var maxVal = 0
for (k=0; k < V.length; k++) {
if (k == 0) {
maxVal = V[k]
} else {
if (magnCmplxNum(V[k]) > magnCmplxNum(maxVal)) {
maxVal = V[k]
idx = k
}
}
}
return idx+offs
}
function swap_rows(Minp,i,j) {
var M_res = Minp
var nr = Minp.length
var nc = Minp[0].length
var tmpVal = new Array()
for (k=0; k < nc; k++) {
tmpVal = M_res[i][k]
M_res[i][k] = M_res[j][k]
M_res[j][k] = tmpVal
}
return M_res
}
function lu_factorisation_partial_pivot(M) {
var U = M
var nr = U.length
var L = create_const_matrix(nr,nr,0);
var P = create_const_matrix(nr,nr,0);
var num_swaps = 0
for (k=0; k < nr; k++) {
var Z = new Array()
Z.push(1.0)
Z.push(0.0)
L[k][k] = Z
P[k][k] = Z
}
for (ii=0; ii< nr-1; ii++) {
var vec = new Array()
for (jj=ii; jj < nr; jj++) {
vec.push(U[jj][ii])
}
var elem = 0.0
var idx = get_max_idx(vec,ii)
var el_tmp = 0.0
for (jj =ii; jj < nr; jj++ ) {
el_tmp = U[ii][jj]
U[ii][jj] = U[idx][jj]
U[idx][jj] = el_tmp
}
for (jj =0; jj < ii; jj++ ) {
el_tmp = L[ii][jj]
L[ii][jj] = L[idx][jj]
L[idx][jj] = el_tmp
}
if (ii != idx) {
num_swaps += 1
}
Pnew = swap_rows(P,ii,idx)
P = Pnew
for (jj = ii + 1; jj < nr; jj++) {
if (magnCmplxNum(U[jj][ii])<1e-16) {
var C = new Array()
C.push(0.0)
C.push(0.0)
L[jj][ii] = C
} else {
L[jj][ii] = divCmplxNum(U[jj][ii],U[ii][ii])
}
for (xx=ii;xx < nr; xx++) {
U[jj][xx] = subtrCmplxNum(U[jj][xx],multCmplxNum(L[jj][ii], U[ii][xx]))
}
}
}
var det_res = new Array()
det_res.push(1.0)
det_res.push(0.0)
for (vv=0; vv < nr; vv++) {
det_res = multCmplxNum(det_res,multCmplxNum(U[vv][vv],L[vv][vv]))
}
det_res[0] = det_res[0] * ((-1)**num_swaps)
det_res[1] = det_res[1] * ((-1)**num_swaps)
return [U,L,P,num_swaps,det_res]
}
function mult_matrices(A,B) {
var nra = A.length
var nca = A[0].length
var nrb = B.length
var ncb = B[0].length
if (nca != nrb) {
alert("Cannot multiply these matrics!")
}
var C = new Array()
C = create_const_matrix(nra,ncb,0)
for (aa = 0; aa < nra; aa++) {
for (bb = 0; bb < ncb; bb++) {
var tmp_res = new Array()
tmp_res.push(0.0)
tmp_res.push(0.0)
for (cc = 0; cc < nca; cc++) {
tmp_res = addCmplxNum(tmp_res, multCmplxNum(A[aa][cc],B[cc][bb]))
}
C[aa][bb] = tmp_res
}
}
return C
}
function back_subst(U,d) {
var nr = d.length
var x = new Array()
x = create_const_matrix(nr,1,0)
x[nr - 1][0] = divCmplxNum(d[nr-1][0],U[nr-1][nr-1]);
for (i=nr-2; i >= 0; i--) {
var sum_ux = new Array()
sum_ux.push(0.0)
sum_ux.push(0.0)
for (j=i+1; j < nr; j++) {
sum_ux = addCmplxNum(sum_ux,multCmplxNum(U[i][j],x[j][0]));
}
var one_cmplx = new Array()
one_cmplx.push(1.0)
one_cmplx.push(0.0)
x[i][0] = multCmplxNum(divCmplxNum(one_cmplx,U[i][i]), subtrCmplxNum(d[i][0],sum_ux));
}
return x;
}
function fwd_subst(L,b) {
var d = new Array()
var nr = b.length
d = create_const_matrix(nr,1,0)
d[0][0] = b[0][0]
for (i=1; i < nr; i++) {
var sum_ld = new Array();
sum_ld.push(0.0)
sum_ld.push(0.0)
for (j=0; j < i; j++) {
sum_ld = addCmplxNum(sum_ld,multCmplxNum(L[i][j],d[j][0]));
}
d[i][0] = subtrCmplxNum(b[i][0],sum_ld);
}
return d;
}
/* Complex calculation related functions */
function genStringFromCmplx(A,dp) {
var res = ""
if (A[1] < 0) {
res = A[0].toFixed(dp) + " - " + Math.abs(A[1]).toFixed(dp) + "i"
} else {
res = A[0].toFixed(dp) + " + " + A[1].toFixed(dp) + "i"
}
return res
}
function conjCmplxNum(A) {
C = new Array()
C.push(A[0])
C.push(-A[1])
return C
}
function magnCmplxNum(A) {
return Math.sqrt((A[0]*A[0]) + (A[1]*A[1]))
}
function powCmplxNum(A,x) {
var r = Math.sqrt((A[0]*A[0]) + (A[1]*A[1]))
var theta = Math.atan2(A[1],A[0])
console.log("r is ")
console.log("theta is ", theta)
var res_re = Math.pow(r,x) * Math.cos(x*theta)
var res_im = Math.pow(r,x) * Math.sin(x*theta)
var C = new Array()
C.push(res_re)
C.push(res_im)
return C
}
function addCmplxNum(A,B){
C = new Array()
C.push(A[0] + B[0])
C.push(A[1] + B[1])
return C
}
function subtrCmplxNum(A,B){
C = new Array()
C.push(A[0] - B[0])
C.push(A[1] - B[1])
return C
}
function multCmplxNum(A,B){
C = new Array()
C.push((A[0]*B[0]) - (A[1]*B[1]))
C.push((A[0]*B[1]) + (A[1]*B[0]))
return C
}
function divCmplxNum(A,B){
C = new Array()
var magn = (B[0] * B[0]) + (B[1] * B[1])
C = multCmplxNum(A,conjCmplxNum(B))
C[0] = C[0]/magn
C[1] = C[1]/magn
return C
}
function genCmplxFromString(str) {
var NumCmplx = new Array()
NumCmplx.push(0.0)
NumCmplx.push(0.0)
var isCmplex = str.includes("i")
var scaleFac = +1
if (isCmplex) {
var idx_i = str.indexOf("i")
var ii = idx_i
var sgn_idx = 0
var sgn_found = false
for (ii = idx_i; ii >= 0; ii--) {
if ((str[ii] == "+" || (str[ii] == "-" && str[ii-1]!= "e")) && (sgn_found == false)) {
if (str[ii] == "-") {
scaleFac = -1
}
sgn_idx = ii
sgn_found = true
}
}
var real_str = str.substring(0,sgn_idx)
var imag_i_str = str.substring(sgn_idx + 1, str.length)
var imag_str = imag_i_str.replace("i","")
var imag_str = imag_str.replace(/ +/g, ' ');
NumCmplx[0] = Number(real_str)
NumCmplx[1] = scaleFac * Number(imag_str)
console.log("Scale fac ",scaleFac)
if (imag_str == ' ' || imag_str == '') {
NumCmplx[1] = scaleFac * 1
}
if (sgn_found == false) {
NumCmplx[0] = 0
NumCmplx[1] = Number(str.replace("i",""))
// Corner case if we have just "i"
if (str.replace("i","").replace(/ +/g, ' ')== "") {
NumCmplx[1] = 1
}
}
} else {
NumCmplx[0] = Number(str)
NumCmplx[1] = 0
}
console.log("Real ",NumCmplx[0])
console.log("Imag ",NumCmplx[1])
return NumCmplx
}
/* The calculate function called when the Perform LU Decomposition button is pressed
*/
function calc() {
var dp = Number(document.getElementById("prec").value)
var inpstr = document.getElementById("InpBox").value;
var check_params = new Array()
check_params = validate_matrix(inpstr)
if (check_params[0] == 0) {
alert("All rows should have the same number of entries!")
}
var isSquare = check_params[3]
if (isSquare == 0 && check_params[0] == 1) {
alert("Matrix needs to be square!")
}
var dbg_str = check_params[1]
var M = new Array()
M = check_params[2]
var M_str = gen_printable_string_from_matrix(M,dp,true)
var Mt = new Array()
Mt = transpose_matrix(M)
console.log("Stage 0")
var Mt_str = gen_printable_string_from_matrix(Mt,dp,true)
console.log("Stage 1")
// The LU factorisation proper
res_lu = lu_factorisation_partial_pivot(M)
console.log("Stage 2")
var U = res_lu[0]
var L = res_lu[1]
var P = res_lu[2]
var num_swaps = res_lu[3]
var det_res = res_lu[4]
console.log("Stage 3")
// Stringify so as to put results in the textareas
var U_str = gen_printable_string_from_matrix(U,dp,true)
console.log("Stage 3a")
var L_str = gen_printable_string_from_matrix(L,dp,true)
console.log("Stage 3b")
var P_str = gen_printable_real_string_from_matrix(P,0,true)
console.log("Stage 3c")
var det_res = res_lu[4]
console.log("Stage 4")
// Calculate the inverse matrix fron the LU factorisation
var dim = U.length
var inv_res = new Array()
inv_res = create_const_matrix(dim,dim,0)
var C_inv = new Array()
for (kk=0; kk < dim; kk++) {
var ei = new Array()
ei = create_const_matrix(dim,1,0);
var Z = new Array()
Z.push(1.0)
Z.push(0.0)
ei[kk][0] = Z;
var ri = new Array()
ri = create_const_matrix(dim,1,0);
ri = back_subst(U,fwd_subst(L,ei));
for (mm=0; mm < dim; mm++) {
inv_res[mm][kk] = ri[mm][0];
}
}
C_inv = mult_matrices(inv_res,P)
//C_inv = inv_res
var C_str = gen_printable_string_from_matrix(C_inv,dp,true)
// Append matrix determinant
dbg_str += "<br>" + "Determinant of matrix is " + genStringFromCmplx(det_res,dp)
document.getElementById("p1").innerHTML = dbg_str;
document.getElementById("U").innerHTML = U_str;
document.getElementById("L").innerHTML = L_str;
document.getElementById("P").innerHTML = P_str;
document.getElementById("I").innerHTML = C_str;
/*
document.getElementById("L").innerHTML = M_str;
document.getElementById("U").innerHTML = Mt_str;
*/
}
</script>
Alijah Ahmedhttp://www.blogger.com/profile/01151589526613233918noreply@blogger.comtag:blogger.com,1999:blog-8441309013858171051.post-24809050130878363632020-05-11T06:40:00.002-07:002020-05-15T12:55:20.782-07:00Matrix Utilities: QR factorisation using Householder reflections for Real Matrix<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-csv/0.8.3/jquery.csv.min.js"></script>
<script>
$(document).ready(function() {
if(isAPIAvailable()) {
$('#files').bind('change', handleFileSelect);
}
});
function isAPIAvailable() {
// Check for the various File API support.
if (window.File && window.FileReader && window.FileList && window.Blob) {
// Great success! All the File APIs are supported.
return true;
} else {
// source: File API availability - http://caniuse.com/#feat=fileapi
// source: <output> availability - http://html5doctor.com/the-output-element/
document.writeln('The HTML5 APIs used in this form are only available in the following browsers:<br />');
// 6.0 File API & 13.0 <output>
document.writeln(' - Google Chrome: 13.0 or later<br />');
// 3.6 File API & 6.0 <output>
document.writeln(' - Mozilla Firefox: 6.0 or later<br />');
// 10.0 File API & 10.0 <output>
document.writeln(' - Internet Explorer: Not supported (partial support expected in 10.0)<br />');
// ? File API & 5.1 <output>
document.writeln(' - Safari: Not supported<br />');
// ? File API & 9.2 <output>
document.writeln(' - Opera: Not supported');
return false;
}
}
function handleFileSelect(evt) {
var files = evt.target.files; // FileList object
var file = files[0];
// read the file metadata
var output = ''
output += '<span style="font-weight:bold;">' + escape(file.name) + '</span><br />\n';
output += ' - FileType: ' + (file.type || 'n/a') + '<br />\n';
output += ' - FileSize: ' + file.size + ' bytes<br />\n';
output += ' - LastModified: ' + (file.lastModifiedDate ? file.lastModifiedDate.toLocaleDateString() : 'n/a') + '<br />\n';
// read the file contents
printTextArea(file);
// post the results
$('#list').append(output);
}
function printTextArea(file) {
var reader = new FileReader();
reader.readAsText(file);
reader.onload = function(event){
var csv = event.target.result;
var data = $.csv.toArrays(csv);
var mystr = "";
for(var row in data) {
for (ii=0; ii < data[row].length; ii++) {
mystr += data[row][ii].toString()
if (ii < data[row].length-1) {
mystr += ","
}
}
mystr += "\n"
}
mystr = mystr.substring(0, mystr.length - 1)
document.getElementById("InpBox").value = mystr;
};
reader.onerror = function(){ alert('Unable to read ' + file.fileName); };
}
</script>
<body>
<script>
$(document).ready(function(){
$(".special").css("background-color", "white");
$(".special").css("width", "600px");
$(".special").css("height", "150px");
$(".special").css("font-size", "0.9em");
$(".special").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".special").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".special").css("font-weight","normal");
$(".special").css("border","1px solid black");
$(".precision").css("font-size", "1.0em");
$(".results").css("background-color", "pink");
$(".results").css("width", "600px");
$(".results").css("height", "150px");
$(".results").css("font-size", "0.9em");
$(".results").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".results").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".results").css("font-weight","normal");
$(".results").css("border","1px solid black");
$(".invres").css("background-color", "yellow");
$(".invres").css("width", "600px");
$(".invres").css("height", "100px");
$(".invres").css("font-size", "0.9em");
$(".invres").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".invres").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".invres").css("font-weight","normal");
$(".invres").css("border","1px solid black");
});
</script>
<p style="text-align:justify">This blog post implements the QR factorisation of a square matrix, using Householder reflections.</p>
<p style="text-align:justify">
For the (square) matrix entry, comma separated (the separation can also be a single whitespace) numbers need to be entered into the Input box, with no newline after the last line. A comma at the end of each line can be added optionally for comma separated numbers. Alternatively, you can load a CSV file into the Input box, by clicking on the Choose File button.</p>
<p style="text-align:justify">The precision of the results in decimal places can be specified in the text box headed "Precision (decimal places)". To perform the calculation, simply press the button labelled "Perform QR factorisation" further down this page. Two matrices will be printed in coloured textareas: the Q matrix which is orthogonal, and the upper-triangular R matrix. In addition, intermediate values of the Q and R matrices will be displayed at the bottom.</p>
<br>
Precision (decimal places)
<br>
<textarea class="precision" name="textarea" rows="2" cols="10" id ="prec">
</textarea>
<br>
<br>
<div id=inputs class=clearfix>
<input type=file id=files name=files[] single />
</div>
<br>
<br>
<input name="b1" onclick="clear_input()" style="margin-left: 0px;" type="Button" value="Clear Input" />
<br>
<br>
Input<br>
<textarea class="special" name="textarea" rows="20" cols="50" id ="InpBox">
</textarea>
<br>
<input name="b1" onclick="calc()" style="margin-left: 0px;" type="Button" value="Perform QR factorisation" />
<br>
<br>
Q Matrix
<br>
<textarea class="results" rows="20" cols="50" id ="Q_hr">
</textarea>
<br>
R Matrix
<br>
<textarea class="results" rows="20" cols="50" id ="R_hr">
</textarea>
<br>
<br>
<p id="p1">Householder intermediates...</p>
<br>
<br>
</body>
</html>
<script language="JavaScript">
window.onload = set_inputs();
function set_inputs(){
var str = "1,2,4" + "\n" + "3,4,6" + "\n" + "8,6,7"
document.getElementById("InpBox").value = str;
document.getElementById("prec").value = "5";
}
function clear_input() {
document.getElementById("InpBox").value = "";
}
function validate_matrix(inpstr) {
// The Matrix proper
var M = new Array()
var inp_arr = new Array()
inp_arr = inpstr.split("\n");
var num_rows = inp_arr.length
if (inp_arr[num_rows - 1] == "")
num_rows = num_rows - 1
var splitchar = ","
var tmpStr = inp_arr[0]
if (tmpStr.indexOf(',') == -1) {
splitchar = " "
}
var row_arr_filtered = inp_arr[0].split(splitchar).filter(function (el) {
return el != "";
});
var num_cols
var num_cols = row_arr_filtered.length
var dbg_str = ""
var cols_match = 1
// Check if all rows have same number of elements
for (ii = 0; ii < num_rows; ii++) {
var vec = inp_arr[ii].split(splitchar).filter(function (el) {
return el != "";
});
var num_vec = new Array()
for (kk=0; kk < vec.length; kk++) {
num_vec.push(Number(vec[kk]))
}
M.push(num_vec)
if (ii == 0){
num_cols = vec.length
} else {
if (num_cols != vec.length) {
cols_match = 0
}
}
}
var dbg_str = ""
dbg_str += "Number of rows is " + num_rows.toString()
dbg_str += ", number of columns is " + num_cols.toString()
var isSquare = 1
if (num_rows != num_cols) {
isSquare = 0
}
return [cols_match, dbg_str,M, isSquare]
}
function gen_printable_string_from_matrix(M,dp,isTextArea)
{
var Nr = M.length
var Nc = M[0].length
var res_str = ""
for (i=0; i < Nr; i++)
{
for (j=0; j < Nc;j++)
{
res_str += M[i][j].toFixed(dp)
if (j < Nc-1)
res_str += ", "
}
if (isTextArea) {
res_str += "\n"
} else {
res_str += "<br>"
}
}
return res_str
}
function create_const_matrix(m,n,val)
{
var M_res = new Array
for (i=0; i < m; i++)
{
var vec = new Array
for (j=0; j < n;j++)
{
vec.push(val)
}
M_res.push(vec)
}
return M_res
}
function transpose_matrix(M)
{
var nr = M.length
var nc = M[0].length
var Mres = create_const_matrix(nc,nr,0);
for (i=0; i < nc; i++)
{
for (j=0; j < nr;j++)
{
Mres[i][j]=M[j][i]
}
}
return Mres
}
function get_max_idx(V,offs) {
var idx = 0
var maxVal = 0
for (k=0; k < V.length; k++) {
if (k == 0) {
maxVal = V[k]
} else {
if (Math.abs(V[k])>Math.abs(maxVal)) {
maxVal = V[k]
idx = k
}
}
}
return idx+offs
}
function swap_rows(Minp,i,j) {
var M_res = Minp
var nr = Minp.length
var nc = Minp[0].length
var tmpVal = 0
for (k=0; k < nc; k++) {
tmpVal = M_res[i][k]
M_res[i][k] = M_res[j][k]
M_res[j][k] = tmpVal
}
return M_res
}
function lu_factorisation_partial_pivot(M) {
var U = M
var nr = U.length
var L = create_const_matrix(nr,nr,0);
var P = create_const_matrix(nr,nr,0);
var num_swaps = 0
var k = 0
var ii = 0
var jj = 0
for (k=0; k < nr; k++) {
L[k][k] = 1.0
P[k][k] = 1.0
}
for (ii=0; ii< nr-1; ii++) {
var vec = new Array()
for (jj=ii; jj < nr; jj++) {
vec.push(U[jj][ii])
}
var elem = 0.0
var idx = get_max_idx(vec,ii)
var el_tmp = 0.0
for (jj =ii; jj < nr; jj++ ) {
el_tmp = U[ii][jj]
U[ii][jj] = U[idx][jj]
U[idx][jj] = el_tmp
}
for (jj =0; jj < ii; jj++ ) {
el_tmp = L[ii][jj]
L[ii][jj] = L[idx][jj]
L[idx][jj] = el_tmp
}
if (ii != idx) {
num_swaps += 1
}
Pnew = swap_rows(P,ii,idx)
P = Pnew
for (jj = ii + 1; jj < nr; jj++) {
if (Math.abs(U[jj][ii])<1e-16) {
L[jj][ii] = 0.0
} else {
L[jj][ii] = U[jj][ii]/U[ii][ii]
}
for (xx=ii;xx < nr; xx++) {
U[jj][xx] = U[jj][xx] - (L[jj][ii] * U[ii][xx])
}
}
}
var det_res = 1.0
for (vv=0; vv < nr; vv++) {
det_res = det_res * (U[vv][vv] * L[vv][vv])
}
det_res = det_res * ((-1)**num_swaps)
return [U,L,P,num_swaps,det_res]
}
function mult_matrices(A,B) {
var nra = A.length
var nca = A[0].length
var nrb = B.length
var ncb = B[0].length
if (nca != nrb) {
alert("Cannot multiply these matrics!")
}
var C = new Array()
C = create_const_matrix(nra,ncb,0)
for (aa = 0; aa < nra; aa++) {
for (bb = 0; bb < ncb; bb++) {
var tmp_res = 0.0
for (cc = 0; cc < nca; cc++) {
tmp_res = tmp_res + (A[aa][cc] * B[cc][bb])
}
C[aa][bb] = tmp_res
}
}
return C
}
function back_subst(U,d) {
var nr = d.length
var x = new Array()
x = create_const_matrix(nr,1,0)
x[nr - 1][0] = d[nr-1][0]/(U[nr-1][nr-1]);
for (i=nr-2; i >= 0; i--) {
var sum_ux = 0.0;
for (j=i+1; j < nr; j++) {
sum_ux += (U[i][j]*x[j][0]);
}
x[i][0] = (1/U[i][i]) * (d[i][0] - sum_ux);
}
return x;
}
function fwd_subst(L,b) {
var d = new Array()
var nr = b.length
d = create_const_matrix(nr,1,0)
d[0][0] = b[0][0]
for (i=1; i < nr; i++) {
var sum_ld = 0.0;
for (j=0; j < i; j++) {
sum_ld += (L[i][j] * d[j][0]);
}
d[i][0] = b[i][0] - sum_ld;
}
return d;
}
function extract_column(M,k) {
var R = new Array()
var dim = M.length
R = create_const_matrix(dim,1,0)
for (ii = 0; ii < dim; ii++) {
R[ii][0] = M[ii][k]
}
return R
}
function calc_norm(M) {
var nr = M.length
var nc = M[0].length
var sum_sq = 0.0
for (ii = 0; ii < nr; ii++) {
for (jj=0; jj < nc; jj++) {
sum_sq = sum_sq + (M[ii][jj] * M[ii][jj])
}
}
return Math.sqrt(sum_sq)
}
function mult_matrix_by_scalar(M,k) {
var nr = M.length
var nc = M[0].length
var R = create_const_matrix(nr,nc,0)
for (ii = 0; ii < nr; ii++) {
for (jj=0; jj < nc; jj++) {
R[ii][jj] = k * M[ii][jj]
}
}
return R
}
function dot_product(a,b) {
var vec_len = a[0].length
var res = 0.0
for (ii=0; ii < vec_len; ii++) {
res = res + (a[0][ii] * b[ii][0])
}
return res
}
function add_matrices(A,B) {
var C = new Array()
var nr = A.length
var nc = A[0].length
C = create_const_matrix(nr,nc,0)
for (ii=0; ii<nr; ii++) {
for (jj=0; jj<nc; jj++) {
C[ii][jj] = A[ii][jj] + B[ii][jj]
}
}
return C
}
function subtr_matrices(A,B) {
var C = new Array()
var nr = A.length
var nc = A[0].length
C = create_const_matrix(nr,nc,0)
for (ii=0; ii<nr; ii++) {
for (jj=0; jj<nc; jj++) {
C[ii][jj] = A[ii][jj] - B[ii][jj]
}
}
return C
}
function gram_schmidt(A) {
var dim = A.length
var U = new Array()
var E = new Array()
var u = new Array()
var e = new Array()
var a = new Array()
var dbg_info = "Debug Info"
U = create_const_matrix(dim,dim,0)
E = create_const_matrix(dim,dim,0)
u = create_const_matrix(dim,1,0)
e = create_const_matrix(dim,1,0)
a = create_const_matrix(dim,1,0)
dbg_info += " Dimension is " + dim.toString() + "<br>"
var jj = 0
var kk = 0
var mm = 0
var ii = 0
for (jj = 0; jj < dim; jj++) {
dbg_info += ("Index " + jj.toString() + "<br>")
if (jj == 0) {
u = extract_column(A,jj)
dbg_info += ("u<br>" + gen_printable_string_from_matrix(u,4,false) + "<br>")
e = mult_matrix_by_scalar(u,(1.0/calc_norm(u)))
for (ii = 0; ii < dim; ii++) {
E[ii][jj] = e[ii][0]
}
} else {
a = extract_column(A,jj)
u = a
dbg_info += ("u<br>" + gen_printable_string_from_matrix(u,5,false) + "<br>")
for (kk=0; kk < jj; kk++) {
dbg_info += ("**a column:<br>" + gen_printable_string_from_matrix(a,5,false) + "<br>")
dbg_info += ("**Ekk column:<br>" + gen_printable_string_from_matrix(extract_column(E,kk),5,false) + "<br>")
var inner_prod = dot_product(transpose_matrix(a),extract_column(E,kk))
dbg_info += ("**Inner prod: " + inner_prod.toString() + "<br>")
u = subtr_matrices(u,mult_matrix_by_scalar(extract_column(E,kk),inner_prod))
}
dbg_info += ("Final u<br>" + gen_printable_string_from_matrix(u,4,false) + "<br>")
//dbg_info += ("Inner prod<br>" + inner_prod.toString() + "<br>")
e = mult_matrix_by_scalar(u,(1.0/calc_norm(u)))
for (mm = 0; mm < dim; mm++) {
E[mm][jj] = e[mm][0]
}
}
}
// Create the R matrix
var R = new Array()
R = create_const_matrix(dim,dim,0)
for (aa=0; aa<dim; aa++) {
for (bb=aa; bb<dim; bb++) {
R[aa][bb] = dot_product(transpose_matrix(extract_column(A,bb)),extract_column(E,aa))
}
}
return [E,R,dbg_info]
}
function householder_qr_2(A) {
var m = A.length
var n = A[0].length
var Qh_arr = new Array()
var Rh_arr = new Array()
var Rh = []
Rh = create_const_matrix(m,n,0)
for (aa=0; aa < m; aa++) {
for (bb=0; bb < n; bb++) {
Rh[aa][bb] = A[aa][bb]
}
}
msg_str = ""
var Qh = []
Qh = create_const_matrix(m,m,0)
var ii = 0
for (ii=0; ii<m; ii++) {
Qh[ii][ii] = 1.0
}
Qh_arr.push(Qh)
Rh_arr.push(Rh)
msg_str += "<br>Iteration 0<br>"
msg_str += "Q matrix " + "<br>"
msg_str += gen_printable_string_from_matrix(Qh,5,false)
msg_str += "<br>" + "R matrix " + "<br>"
msg_str += gen_printable_string_from_matrix(Rh,5,false)
msg_str += "<br>"
console.log("Qh ",gen_printable_string_from_matrix(Qh,5,false))
console.log("Rh ",gen_printable_string_from_matrix(Rh,5,false))
var jj = 0
var mm = 0
var nn = 0
for (jj=0; jj < n; jj++) {
var R_j_end_j = new Array()
R_j_end_j = create_const_matrix(n-jj,1,0)
var R_j_end_a = new Array()
R_j_end_a = create_const_matrix(n-jj,n,0)
// Assign from R
for (mm=jj; mm < n; mm++) {
R_j_end_j[mm-jj][0] = Rh[mm][jj]
for (nn=0; nn < n; nn++) {
R_j_end_a[mm-jj][nn] = Rh[mm][nn]
}
}
// Deal with the Q matrix
var Q_a_j_end = new Array()
Q_a_j_end = create_const_matrix(m,m-jj,0)
// Assign from Q
for (mm=jj; mm < m; mm++) {
for (nn=0; nn < m; nn++) {
Q_a_j_end[nn][mm-jj] = Qh[nn][mm]
}
}
var normx = calc_norm(R_j_end_j)
var s = -Math.sign(Rh[jj][jj])
var u1 = Rh[jj][jj] - (s * normx)
// The original v
var u = new Array()
var e1 = new Array()
e1 = create_const_matrix(R_j_end_j.length,1,0)
e1[0][0] = 1
u = subtr_matrices(R_j_end_j,mult_matrix_by_scalar(e1,normx))
var v = new Array()
if (Math.abs(calc_norm(u)) < 1e-16) {
v = mult_matrix_by_scalar(u,0.0)
} else {
v = mult_matrix_by_scalar(u,1.0/calc_norm(u))
}
var w = new Array()
w = mult_matrix_by_scalar(R_j_end_j,1.0/u1)
w[0][0] = 1.0
var tau = -s*(u1/normx)
var tau_mult_w = new Array()
tau_mult_w = mult_matrix_by_scalar(w,tau)
console.log("Stage 1")
var vt_mult_R_j_end_a = mult_matrices(transpose_matrix(v),R_j_end_a)
console.log("Stage 2")
R_j_end_a = subtr_matrices(R_j_end_a, mult_matrix_by_scalar(mult_matrices(v,vt_mult_R_j_end_a),2))
console.log("Stage 3")
// Assign back to R
for (mm=jj; mm < n; mm++) {
for (nn=0; nn < n; nn++) {
Rh[mm][nn] = R_j_end_a[mm-jj][nn]
}
}
// deal with the Q matrix
var Q_a_j_end_mult_v = new Array()
console.log("Stage 4")
Q_a_j_end_mult_v = mult_matrices(Q_a_j_end,v)
console.log("Stage 5")
Q_a_j_end = subtr_matrices(Q_a_j_end, mult_matrix_by_scalar(mult_matrices(Q_a_j_end_mult_v,transpose_matrix(v)),2))
console.log("Stage 6")
// Assign back to Q
for (mm=jj; mm < m; mm++) {
for (nn=0; nn < m; nn++) {
Qh[nn][mm] = Q_a_j_end[nn][mm-jj]
}
}
Qh_arr.push(Qh)
Rh_arr.push(Rh)
var iterno_p_1 = jj + 1
msg_str += "<br>Iteration " + iterno_p_1.toString() + "<br>"
msg_str += "Q matrix " + "<br>"
msg_str += gen_printable_string_from_matrix(Qh,5,false)
msg_str += "<br>" + "R matrix " + "<br>"
msg_str += gen_printable_string_from_matrix(Rh,5,false)
msg_str += "<br>"
console.log("Qh ",gen_printable_string_from_matrix(Qh,5,false))
console.log("Rh ",gen_printable_string_from_matrix(Rh,5,false))
}
console.log("Length of Qh_arr is ",Qh_arr.length)
console.log("Length of Rh_arr is ",Rh_arr.length)
return [Qh,Rh,Qh_arr,Rh_arr, msg_str]
}
/* The calculate function called when the Perform LU Decomposition button is pressed
*/
function calc() {
var dp = Number(document.getElementById("prec").value)
var inpstr = document.getElementById("InpBox").value;
var check_params = new Array()
check_params = validate_matrix(inpstr)
if (check_params[0] == 0) {
alert("All rows should have the same number of entries!")
}
var isSquare = check_params[3]
if (isSquare == 0 && check_params[0] == 1) {
alert("Matrix needs to be square!")
}
var dbg_str = check_params[1]
var M = new Array()
M = check_params[2]
var M_str = gen_printable_string_from_matrix(M,dp,true)
var house_res = householder_qr_2(M)
Q_hr_mat = house_res[0]
R_hr_mat = house_res[1]
var Q_hr_mat_str = gen_printable_string_from_matrix(Q_hr_mat,dp,true)
var R_hr_mat_str = gen_printable_string_from_matrix(R_hr_mat,dp,true)
Q_hr_mat_arr = house_res[2]
R_hr_mat_arr = house_res[3]
var house_dbg_str = house_res[4]
console.log("M matrix", M_str)
document.getElementById("Q_hr").innerHTML = Q_hr_mat_str;
document.getElementById("R_hr").innerHTML = R_hr_mat_str;
document.getElementById("p1").innerHTML = house_dbg_str;
}
</script>
Alijah Ahmedhttp://www.blogger.com/profile/01151589526613233918noreply@blogger.comtag:blogger.com,1999:blog-8441309013858171051.post-28114985984611300862020-05-09T03:06:00.003-07:002020-05-15T12:55:37.009-07:00Matrix Utilities: Cholesky Decomposition for Real Matrix<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-csv/0.8.3/jquery.csv.min.js"></script>
<script>
$(document).ready(function() {
if(isAPIAvailable()) {
$('#files').bind('change', handleFileSelect);
}
});
function isAPIAvailable() {
// Check for the various File API support.
if (window.File && window.FileReader && window.FileList && window.Blob) {
// Great success! All the File APIs are supported.
return true;
} else {
// source: File API availability - http://caniuse.com/#feat=fileapi
// source: <output> availability - http://html5doctor.com/the-output-element/
document.writeln('The HTML5 APIs used in this form are only available in the following browsers:<br />');
// 6.0 File API & 13.0 <output>
document.writeln(' - Google Chrome: 13.0 or later<br />');
// 3.6 File API & 6.0 <output>
document.writeln(' - Mozilla Firefox: 6.0 or later<br />');
// 10.0 File API & 10.0 <output>
document.writeln(' - Internet Explorer: Not supported (partial support expected in 10.0)<br />');
// ? File API & 5.1 <output>
document.writeln(' - Safari: Not supported<br />');
// ? File API & 9.2 <output>
document.writeln(' - Opera: Not supported');
return false;
}
}
function handleFileSelect(evt) {
var files = evt.target.files; // FileList object
var file = files[0];
// read the file metadata
var output = ''
output += '<span style="font-weight:bold;">' + escape(file.name) + '</span><br />\n';
output += ' - FileType: ' + (file.type || 'n/a') + '<br />\n';
output += ' - FileSize: ' + file.size + ' bytes<br />\n';
output += ' - LastModified: ' + (file.lastModifiedDate ? file.lastModifiedDate.toLocaleDateString() : 'n/a') + '<br />\n';
// read the file contents
printTextArea(file);
// post the results
$('#list').append(output);
}
function printTextArea(file) {
var reader = new FileReader();
reader.readAsText(file);
reader.onload = function(event){
var csv = event.target.result;
var data = $.csv.toArrays(csv);
var mystr = "";
for(var row in data) {
for (ii=0; ii < data[row].length; ii++) {
mystr += data[row][ii].toString()
if (ii < data[row].length-1) {
mystr += ","
}
}
mystr += "\n"
}
mystr = mystr.substring(0, mystr.length - 1)
document.getElementById("InpBox").value = mystr;
};
reader.onerror = function(){ alert('Unable to read ' + file.fileName); };
}
</script>
<body>
<script>
$(document).ready(function(){
$(".special").css("background-color", "white");
$(".special").css("width", "600px");
$(".special").css("height", "200px");
$(".special").css("font-size", "0.9em");
$(".special").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".special").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".special").css("font-weight","normal");
$(".special").css("border","1px solid black");
$(".precision").css("font-size", "1.0em");
$(".results").css("background-color", "pink");
$(".results").css("width", "600px");
$(".results").css("height", "200px");
$(".results").css("font-size", "0.9em");
$(".results").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".results").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".results").css("font-weight","normal");
$(".results").css("border","1px solid black");
$(".invres").css("background-color", "yellow");
$(".invres").css("width", "600px");
$(".invres").css("height", "100px");
$(".invres").css("font-size", "0.9em");
$(".invres").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".invres").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".invres").css("font-weight","normal");
$(".invres").css("border","1px solid black");
});
</script>
<p style="text-align:justify">This blog post implements the Cholesky decomposition of a square matrix, as well as matrix inversion based on forward and backward substitution on the decomposition matrices. For a Cholesky decomposition to be feasible, the matrix must be positive definite and Hermitian. As the calculator here deals with matrices with only real number entries, the matrix will thus need to be symmetric (as well as positive definite).</p>
<p style="text-align:justify">
For the (square) matrix entry, comma separated (the separation can also be a single whitespace) numbers need to be entered into the Input box, with no newline after the last line. A comma at the end of each line can be added optionally for comma separated numbers. Alternatively, you can load a CSV file into the Input box, by clicking on the Choose File button.</p>
<p style="text-align:justify">The precision of the results in decimal places can be specified in the text box headed "Precision (decimal places)". To perform the calculation, simply press the button labelled "Perform Cholesky decomposition" further down this page. The resulting Cholesky matrices displayed (in the pink boxes) will be the lower and upper triangular matrices. Finally, the matrix inverse will be displayed in the yellow box.</p>
<br>
Precision (decimal places)
<br>
<textarea class="precision" name="textarea" rows="2" cols="10" id ="prec">
</textarea>
<br>
<br>
<div id=inputs class=clearfix>
<input type=file id=files name=files[] single />
</div>
<br>
<br>
<input name="b1" onclick="clear_input()" style="margin-left: 0px;" type="Button" value="Clear Input" />
<br>
<br>
Input<br>
<textarea class="special" name="textarea" rows="20" cols="50" id ="InpBox">
</textarea>
<br>
<input name="b1" onclick="calc()" style="margin-left: 0px;" type="Button" value="Perform Cholesky decomposition" />
<br>
<p id="p1">Results pending...</p>
<br>
Cholesky Matrix: Lower Triangular
<br>
<textarea class="results" rows="20" cols="50" id ="L">
</textarea>
<br>
<br>
Cholesky Matrix: Upper Triangular
<br>
<textarea class="results" rows="20" cols="50" id ="U">
</textarea>
<br>
<br>
Matrix Inverse
<br>
<textarea class="invres" rows="20" cols="50" id ="I">
</textarea>
<br>
</body>
</html>
<script language="JavaScript">
window.onload = set_inputs();
function set_inputs(){
var str = "138,32,40" + "\n" + "32,12,14" + "\n" + "40,14,19"
document.getElementById("InpBox").value = str;
document.getElementById("prec").value = "5";
}
function clear_input() {
document.getElementById("InpBox").value = "";
}
function validate_matrix(inpstr) {
// The Matrix proper
var M = new Array()
var inp_arr = new Array()
inp_arr = inpstr.split("\n");
var num_rows = inp_arr.length
if (inp_arr[num_rows - 1] == "")
num_rows = num_rows - 1
var splitchar = ","
var tmpStr = inp_arr[0]
if (tmpStr.indexOf(',') == -1) {
splitchar = " "
}
var row_arr_filtered = inp_arr[0].split(splitchar).filter(function (el) {
return el != "";
});
var num_cols
var num_cols = row_arr_filtered.length
var dbg_str = ""
var cols_match = 1
// Check if all rows have same number of elements
for (ii = 0; ii < num_rows; ii++) {
var vec = inp_arr[ii].split(splitchar).filter(function (el) {
return el != "";
});
var num_vec = new Array()
for (kk=0; kk < vec.length; kk++) {
num_vec.push(Number(vec[kk]))
}
M.push(num_vec)
if (ii == 0){
num_cols = vec.length
} else {
if (num_cols != vec.length) {
cols_match = 0
}
}
}
var dbg_str = ""
dbg_str += "Number of rows is " + num_rows.toString()
dbg_str += ", number of columns is " + num_cols.toString()
var isSquare = 1
if (num_rows != num_cols) {
isSquare = 0
}
return [cols_match, dbg_str,M, isSquare]
}
function gen_printable_string_from_matrix(M,dp,isTextArea)
{
var Nr = M.length
var Nc = M[0].length
var res_str = ""
for (i=0; i < Nr; i++)
{
for (j=0; j < Nc;j++)
{
res_str += M[i][j].toFixed(dp)
if (j < Nc-1)
res_str += ", "
}
if (isTextArea) {
res_str += "\n"
} else {
res_str += "<br>"
}
}
return res_str
}
function create_const_matrix(m,n,val)
{
var M_res = new Array
for (i=0; i < m; i++)
{
var vec = new Array
for (j=0; j < n;j++)
{
vec.push(val)
}
M_res.push(vec)
}
return M_res
}
function transpose_matrix(M)
{
var nr = M.length
var nc = M[0].length
var Mres = create_const_matrix(nc,nr,0);
for (i=0; i < nc; i++)
{
for (j=0; j < nr;j++)
{
Mres[i][j]=M[j][i]
}
}
return Mres
}
function get_max_idx(V,offs) {
var idx = 0
var maxVal = 0
for (k=0; k < V.length; k++) {
if (k == 0) {
maxVal = V[k]
} else {
if (Math.abs(V[k])>Math.abs(maxVal)) {
maxVal = V[k]
idx = k
}
}
}
return idx+offs
}
function swap_rows(Minp,i,j) {
var M_res = Minp
var nr = Minp.length
var nc = Minp[0].length
var tmpVal = 0
for (k=0; k < nc; k++) {
tmpVal = M_res[i][k]
M_res[i][k] = M_res[j][k]
M_res[j][k] = tmpVal
}
return M_res
}
function cholesky_decomposition(M) {
var nr = M.length
var nc = M[0].length
if (nr != nc) {
alert("Matrix must be square!")
}
// var Lres = new Array()
var Lres = create_const_matrix(nr,nr,0)
var s = 0.0
var ii = 0
var jj = 0
var kk = 0
for (ii = 0; ii < nr; ii++) {
for (jj = 0; jj < (ii+1); jj++) {
s = 0.0
for (kk = 0; kk < jj; kk++) {
s += (Lres[ii][kk] * Lres[jj][kk]);
}
console.log("s variable", s)
if (ii == jj) {
Lres[ii][jj] = Math.sqrt(M[ii][ii]-s)
console.log("M[ii][ii]", M[ii][ii])
// Lres[ii][jj] = M[ii][ii]-s
} else {
Lres[ii][jj] = (1.0/Lres[jj][jj]) * (M[ii][jj] - s)
}
}
}
return Lres
}
function lu_factorisation_partial_pivot(M) {
var U = M
var nr = U.length
var L = create_const_matrix(nr,nr,0);
var P = create_const_matrix(nr,nr,0);
var num_swaps = 0
for (k=0; k < nr; k++) {
L[k][k] = 1.0
P[k][k] = 1.0
}
for (ii=0; ii< nr-1; ii++) {
var vec = new Array()
for (jj=ii; jj < nr; jj++) {
vec.push(U[jj][ii])
}
var elem = 0.0
var idx = get_max_idx(vec,ii)
var el_tmp = 0.0
for (jj =ii; jj < nr; jj++ ) {
el_tmp = U[ii][jj]
U[ii][jj] = U[idx][jj]
U[idx][jj] = el_tmp
}
for (jj =0; jj < ii; jj++ ) {
el_tmp = L[ii][jj]
L[ii][jj] = L[idx][jj]
L[idx][jj] = el_tmp
}
if (ii != idx) {
num_swaps += 1
}
Pnew = swap_rows(P,ii,idx)
P = Pnew
for (jj = ii + 1; jj < nr; jj++) {
if (Math.abs(U[jj][ii])<1e-16) {
L[jj][ii] = 0.0
} else {
L[jj][ii] = U[jj][ii]/U[ii][ii]
}
for (xx=ii;xx < nr; xx++) {
U[jj][xx] = U[jj][xx] - (L[jj][ii] * U[ii][xx])
}
}
}
var det_res = 1.0
for (vv=0; vv < nr; vv++) {
det_res = det_res * (U[vv][vv] * L[vv][vv])
}
det_res = det_res * ((-1)**num_swaps)
return [U,L,P,num_swaps,det_res]
}
function mult_matrices(A,B) {
var nra = A.length
var nca = A[0].length
var nrb = B.length
var ncb = B[0].length
if (nca != nrb) {
alert("Cannot multiply these matrics!")
}
var C = new Array()
C = create_const_matrix(nra,ncb,0)
for (aa = 0; aa < nra; aa++) {
for (bb = 0; bb < ncb; bb++) {
var tmp_res = 0.0
for (cc = 0; cc < nca; cc++) {
tmp_res = tmp_res + (A[aa][cc] * B[cc][bb])
}
C[aa][bb] = tmp_res
}
}
return C
}
function back_subst(U,d) {
var nr = d.length
var x = new Array()
x = create_const_matrix(nr,1,0)
x[nr - 1][0] = d[nr-1][0]/(U[nr-1][nr-1]);
for (i=nr-2; i >= 0; i--) {
var sum_ux = 0.0;
for (j=i+1; j < nr; j++) {
sum_ux += (U[i][j]*x[j][0]);
}
x[i][0] = (1/U[i][i]) * (d[i][0] - sum_ux);
}
return x;
}
function fwd_subst(L,b) {
var d = new Array()
var nr = b.length
d = create_const_matrix(nr,1,0)
d[0][0] = b[0][0]/L[0][0]
for (i=1; i < nr; i++) {
var sum_ld = 0.0;
for (j=0; j < i; j++) {
sum_ld += (L[i][j] * d[j][0]);
}
d[i][0] = (1/L[i][i]) * (b[i][0] - sum_ld);
}
return d;
}
/* The calculate function called when the Perform LU Decomposition button is pressed
*/
function calc() {
var dp = Number(document.getElementById("prec").value)
var inpstr = document.getElementById("InpBox").value;
var check_params = new Array()
check_params = validate_matrix(inpstr)
if (check_params[0] == 0) {
alert("All rows should have the same number of entries!")
}
var isSquare = check_params[3]
if (isSquare == 0 && check_params[0] == 1) {
alert("Matrix needs to be square!")
}
var dbg_str = check_params[1]
var M = new Array()
M = check_params[2]
var M_str = gen_printable_string_from_matrix(M,dp,true)
var Lchol = new Array()
Lchol = cholesky_decomposition(M)
var Lchol_str = gen_printable_string_from_matrix(Lchol,dp,true)
var Lchol_t = new Array()
Lchol_t = transpose_matrix(Lchol)
var Lchol_t_str = gen_printable_string_from_matrix(Lchol_t,dp,true)
// Calculate the inverse matrix fron the Cholesky factorisation
var dim = Lchol.length
var inv_res = new Array()
inv_res = create_const_matrix(dim,dim,0)
var det_res = 1.0
for (vv = 0; vv < dim; vv++) {
det_res = det_res * (Lchol[vv][vv] * Lchol_t[vv][vv])
}
for (kk=0; kk < dim; kk++) {
var ei = new Array()
ei = create_const_matrix(dim,1,0);
ei[kk][0] = 1.0;
var ri = new Array()
ri = create_const_matrix(dim,1,0);
ri = back_subst(Lchol_t,fwd_subst(Lchol,ei));
for (mm=0; mm < dim; mm++) {
inv_res[mm][kk] = ri[mm][0];
}
}
var inv_res_str = gen_printable_string_from_matrix(inv_res,dp,true)
// Append matrix determinant
dbg_str += "<br>" + "Determinant of matrix is " + det_res.toFixed(dp)
document.getElementById("p1").innerHTML = dbg_str;
document.getElementById("L").innerHTML = Lchol_str;
document.getElementById("U").innerHTML = Lchol_t_str;
document.getElementById("I").innerHTML = inv_res_str;
}
</script>
Alijah Ahmedhttp://www.blogger.com/profile/01151589526613233918noreply@blogger.comtag:blogger.com,1999:blog-8441309013858171051.post-25526139840642980612020-05-07T11:39:00.002-07:002020-05-15T12:58:39.157-07:00Matrix Utilities: Binary Operators for Real Matrices<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-csv/0.8.3/jquery.csv.min.js"></script>
<script>
</script>
<body>
<script>
$(document).ready(function(){
$(".special").css("background-color", "white");
$(".special").css("width", "600px");
$(".special").css("height", "150px");
$(".special").css("font-size", "0.9em");
$(".special").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".special").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".special").css("font-weight","normal");
$(".special").css("border","1px solid black");
$(".precision").css("font-size", "1.0em");
$(".results").css("background-color", "yellow");
$(".results").css("width", "600px");
$(".results").css("height", "150px");
$(".results").css("font-size", "0.9em");
$(".results").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".results").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".results").css("font-weight","normal");
$(".results").css("border","1px solid black");
$(".invres").css("background-color", "yellow");
$(".invres").css("width", "600px");
$(".invres").css("height", "100px");
$(".invres").css("font-size", "0.9em");
$(".invres").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".invres").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".invres").css("font-weight","normal");
$(".invres").css("border","1px solid black");
});
</script>
<p style="text-align:justify">Binary operations on two matrices are implemented in this blog post. The operations include the following (two types of operations for non-commutative operations, a single for commutative ones):
<ul>
<li>Addition : A + B</li>
<li>Subtraction : A - B</li>
<li>Subtraction : B - A</li>
<li>Multiplication : A * B</li>
<li>Multiplication : B * A</li>
<li>Hadamard product: A .* B</li>
<li>Kronecker product: A kron B</li>
<li>Kronecker product: B kron A</li>
</ul>
</p>
<p style="text-align:justify">
For the matrix entries in Input A and Input B, comma separated (the separation can also be a single whitespace) real numbers need to be entered into the Input boxes, with no newline after the last line. A comma at the end of each line can be added optionally for comma separated numbers.</p>
<p style="text-align:justify">
Note that it is possible to transpose the matrices in the textareas for Input A and Input B.
</p>
<p style="text-align:justify">
Alternatively, you can load a CSV file into each of the Input boxes, by clicking on the Choose File button. There is a select drop down menu which allows you to specify into which of the Input boxes (A or B) should the CSV file be loaded. The drop down menu needs to be set appropriately before clicking on the "Chose File" button. The CSV files that need to be loaded into the two boxes must have a different name, even if the contents are the same (due to a limitation of how the CSV loading is implemented).</p>
<p style="text-align:justify">The precision of the results in decimal places can be specified in the text box headed "Precision (decimal places)".</p>
<p style="text-align:justify">To perform the calculation, simply press the button labelled "Perform matrix operation" further down this page. Before clicking on this button, you need to specify which binary operation to perform with another select drop down menu. The result will be displayed in the yellow textarea.</p>
<br>
Precision (decimal places)
<br>
<br>
<textarea class="precision" name="textarea" rows="2" cols="10" id ="prec">
</textarea>
<br>
<br>
<select id="SelectFile">
<option value="Input_A">Load CSV file into Input A</option>
<option value="Input_B">Load CSV file into Input B</option>
</select>
<div id=inputs class=clearfix>
<input type=file id=files name=files[] single />
</div>
<br>
<br>
<input name="b1" onclick="clear_inputA()" style="margin-left: 0px;" type="Button" value="Clear Input A" />
<input name="b1b" onclick="transpose_A()" style="margin-left: 0px;" type="Button" value="Transpose A" />
<br>
Input A<br>
<textarea class="special" name="textarea" rows="20" cols="50" id ="InpABox">
</textarea>
<input name="b2" onclick="clear_inputB()" style="margin-left: 0px;" type="Button" value="Clear Input B" />
<input name="b2b" onclick="transpose_B()" style="margin-left: 0px;" type="Button" value="Transpose B" />
<br>
Input B<br>
<textarea class="special" name="textarea" rows="20" cols="50" id ="InpBBox">
</textarea>
<br>
<br>
<select id="SelectOperation">
<option value="A + B">A + B</option>
<option value="A - B">A - B</option>
<option value="B - A">B - A</option>
<option value="A * B">A * B</option>
<option value="B * A">B * A</option>
<option value="A .* B">A .* B</option>
<option value="A kron B">A kron B</option>
<option value="B kron A">B kron A</option>
</select>
<br>
<input name="b1" onclick="calc()" style="margin-left: 0px;" type="Button" value="Perform Matrix Operation" />
<br>
<p id="p3">A + B</p>
<textarea class="results" rows="20" cols="50" id ="C">
</textarea>
<br>
<br>
</body>
</html>
<script language="JavaScript">
window.onload = set_inputs();
function set_inputs(){
var str = "1,2,4" + "\n" + "3,4,6" + "\n" + "8,6,7"
document.getElementById("InpABox").value = str;
document.getElementById("InpBBox").value = str;
document.getElementById("prec").value = "5";
}
function clear_inputA() {
document.getElementById("InpABox").value = "";
}
function validate_matrix(inpstr) {
// The Matrix proper
var M = new Array()
var inp_arr = new Array()
inp_arr = inpstr.split("\n");
var num_rows = inp_arr.length
if (inp_arr[num_rows - 1] == "")
num_rows = num_rows - 1
var splitchar = ","
var tmpStr = inp_arr[0]
if (tmpStr.indexOf(',') == -1) {
splitchar = " "
}
var row_arr_filtered = inp_arr[0].split(splitchar).filter(function (el) {
return el != "";
});
var num_cols
var num_cols = row_arr_filtered.length
var dbg_str = ""
var cols_match = 1
// Check if all rows have same number of elements
for (ii = 0; ii < num_rows; ii++) {
var vec = inp_arr[ii].split(splitchar).filter(function (el) {
return el != "";
});
var num_vec = new Array()
for (kk=0; kk < vec.length; kk++) {
num_vec.push(Number(vec[kk]))
}
M.push(num_vec)
if (ii == 0){
num_cols = vec.length
} else {
if (num_cols != vec.length) {
cols_match = 0
}
}
}
var dbg_str = ""
dbg_str += "Number of rows is " + num_rows.toString()
dbg_str += ", number of columns is " + num_cols.toString()
var isSquare = 1
if (num_rows != num_cols) {
isSquare = 0
}
return [cols_match, dbg_str,M, isSquare]
}
function transpose_string_matrix(inpstr) {
// The Matrix proper
var M = new Array()
// Transpose
var Mt = new Array()
var inp_arr = new Array()
inp_arr = inpstr.split("\n");
var num_rows = inp_arr.length
if (inp_arr[num_rows - 1] == "")
num_rows = num_rows - 1
var splitchar = ","
var tmpStr = inp_arr[0]
if (tmpStr.indexOf(',') == -1) {
splitchar = " "
}
var row_arr_filtered = inp_arr[0].split(splitchar).filter(function (el) {
return el != "";
});
var num_cols = row_arr_filtered.length
var dbg_str = ""
var cols_match = 1
// Check if all rows have same number of elements
for (ii = 0; ii < num_rows; ii++) {
var vec = inp_arr[ii].split(splitchar).filter(function (el) {
return el != "";
});
var num_vec = new Array()
for (kk=0; kk < vec.length; kk++) {
num_vec.push(Number(vec[kk]))
}
M.push(num_vec)
if (ii == 0){
num_cols = vec.length
} else {
if (num_cols != vec.length) {
cols_match = 0
}
}
}
var aa = 0
var bb = 0
for (aa = 0; aa < num_cols; aa++) {
var vec_t = new Array()
for (bb = 0; bb < num_rows; bb++) {
vec_t.push(M[bb][aa])
}
Mt.push(vec_t)
}
return Mt
}
function transpose_B() {
var inpstr = document.getElementById("InpBBox").value;
var Mt = new Array()
Mt = transpose_string_matrix(inpstr)
var mystr = ""
for (ii = 0; ii < Mt.length; ii++) {
for (kk=0; kk < Mt[0].length; kk++) {
mystr += Mt[ii][kk]
if (kk < Mt[0].length - 1) {
mystr += ","
}
}
mystr += "\n"
}
document.getElementById("InpBBox").value = mystr;
}
function transpose_A() {
var inpstr = document.getElementById("InpABox").value;
var Mt = new Array()
Mt = transpose_string_matrix(inpstr)
var mystr = ""
for (ii = 0; ii < Mt.length; ii++) {
for (kk=0; kk < Mt[0].length; kk++) {
mystr += Mt[ii][kk]
if (kk < Mt[0].length - 1) {
mystr += ","
}
}
mystr += "\n"
}
document.getElementById("InpABox").value = mystr;
}
function clear_inputB() {
document.getElementById("InpBBox").value = "";
}
function gen_printable_string_from_matrix(M,dp,isTextArea)
{
var Nr = M.length
var Nc = M[0].length
var res_str = ""
for (i=0; i < Nr; i++)
{
for (j=0; j < Nc;j++)
{
res_str += M[i][j].toFixed(dp)
if (j < Nc-1)
res_str += ", "
}
if (isTextArea) {
res_str += "\n"
} else {
res_str += "<br>"
}
}
return res_str
}
function create_const_matrix(m,n,val)
{
var M_res = new Array
for (i=0; i < m; i++)
{
var vec = new Array
for (j=0; j < n;j++)
{
vec.push(val)
}
M_res.push(vec)
}
return M_res
}
function transpose_matrix(M)
{
var nr = M.length
var nc = M[0].length
var Mres = create_const_matrix(nc,nr,0);
for (i=0; i < nc; i++)
{
for (j=0; j < nr;j++)
{
Mres[i][j]=M[j][i]
}
}
return Mres
}
function get_max_idx(V,offs) {
var idx = 0
var maxVal = 0
for (k=0; k < V.length; k++) {
if (k == 0) {
maxVal = V[k]
} else {
if (Math.abs(V[k])>Math.abs(maxVal)) {
maxVal = V[k]
idx = k
}
}
}
return idx+offs
}
function swap_rows(Minp,i,j) {
var M_res = Minp
var nr = Minp.length
var nc = Minp[0].length
var tmpVal = 0
for (k=0; k < nc; k++) {
tmpVal = M_res[i][k]
M_res[i][k] = M_res[j][k]
M_res[j][k] = tmpVal
}
return M_res
}
function lu_factorisation_partial_pivot(M) {
var U = M
var nr = U.length
var L = create_const_matrix(nr,nr,0);
var P = create_const_matrix(nr,nr,0);
var num_swaps = 0
for (k=0; k < nr; k++) {
L[k][k] = 1.0
P[k][k] = 1.0
}
for (ii=0; ii< nr-1; ii++) {
var vec = new Array()
for (jj=ii; jj < nr; jj++) {
vec.push(U[jj][ii])
}
var elem = 0.0
var idx = get_max_idx(vec,ii)
var el_tmp = 0.0
for (jj =ii; jj < nr; jj++ ) {
el_tmp = U[ii][jj]
U[ii][jj] = U[idx][jj]
U[idx][jj] = el_tmp
}
for (jj =0; jj < ii; jj++ ) {
el_tmp = L[ii][jj]
L[ii][jj] = L[idx][jj]
L[idx][jj] = el_tmp
}
if (ii != idx) {
num_swaps += 1
}
Pnew = swap_rows(P,ii,idx)
P = Pnew
for (jj = ii + 1; jj < nr; jj++) {
if (Math.abs(U[jj][ii])<1e-16) {
L[jj][ii] = 0.0
} else {
L[jj][ii] = U[jj][ii]/U[ii][ii]
}
for (xx=ii;xx < nr; xx++) {
U[jj][xx] = U[jj][xx] - (L[jj][ii] * U[ii][xx])
}
}
}
var det_res = 1.0
for (vv=0; vv < nr; vv++) {
det_res = det_res * (U[vv][vv] * L[vv][vv])
}
det_res = det_res * ((-1)**num_swaps)
return [U,L,P,num_swaps,det_res]
}
function mult_matrices(A,B) {
var nra = A.length
var nca = A[0].length
var nrb = B.length
var ncb = B[0].length
var valid_result = true
if (nca != nrb) {
// alert("Cannot multiply these matrics!")
valid_result = false
}
var C = new Array()
C = create_const_matrix(nra,ncb,0)
for (aa = 0; aa < nra; aa++) {
for (bb = 0; bb < ncb; bb++) {
var tmp_res = 0.0
for (cc = 0; cc < nca; cc++) {
tmp_res = tmp_res + (A[aa][cc] * B[cc][bb])
}
C[aa][bb] = tmp_res
}
}
return [C,valid_result]
}
function back_subst(U,d) {
var nr = d.length
var x = new Array()
x = create_const_matrix(nr,1,0)
x[nr - 1][0] = d[nr-1][0]/(U[nr-1][nr-1]);
for (i=nr-2; i >= 0; i--) {
var sum_ux = 0.0;
for (j=i+1; j < nr; j++) {
sum_ux += (U[i][j]*x[j][0]);
}
x[i][0] = (1/U[i][i]) * (d[i][0] - sum_ux);
}
return x;
}
function fwd_subst(L,b) {
var d = new Array()
var nr = b.length
d = create_const_matrix(nr,1,0)
d[0][0] = b[0][0]
for (i=1; i < nr; i++) {
var sum_ld = 0.0;
for (j=0; j < i; j++) {
sum_ld += (L[i][j] * d[j][0]);
}
d[i][0] = b[i][0] - sum_ld;
}
return d;
}
function extract_column(M,k) {
var R = new Array()
var dim = M.length
R = create_const_matrix(dim,1,0)
for (ii = 0; ii < dim; ii++) {
R[ii][0] = M[ii][k]
}
return R
}
function calc_norm(M) {
var nr = M.length
var nc = M[0].length
var sum_sq = 0.0
for (ii = 0; ii < nr; ii++) {
for (jj=0; jj < nc; jj++) {
sum_sq = sum_sq + (M[ii][jj] * M[ii][jj])
}
}
return Math.sqrt(sum_sq)
}
function mult_matrix_by_scalar(M,k) {
var nr = M.length
var nc = M[0].length
var R = create_const_matrix(nr,nc,0)
for (ii = 0; ii < nr; ii++) {
for (jj=0; jj < nc; jj++) {
R[ii][jj] = k * M[ii][jj]
}
}
return R
}
function dot_product(a,b) {
var vec_len = a[0].length
var res = 0.0
for (ii=0; ii < vec_len; ii++) {
res = res + (a[0][ii] * b[ii][0])
}
return res
}
function add_matrices(A,B) {
var C = new Array()
var nr = A.length
var nc = A[0].length
var nrb = B.length
var ncb = B[0].length
C = create_const_matrix(nr,nc,0)
var res_valid = true
if (nr != nrb || nc != ncb) {
res_valid = false
}
for (ii=0; ii<nr; ii++) {
for (jj=0; jj<nc; jj++) {
C[ii][jj] = A[ii][jj] + B[ii][jj]
}
}
return [C,res_valid]
}
function mult_elem_matrices(A,B) {
var C = new Array()
var nr = A.length
var nc = A[0].length
var nrb = B.length
var ncb = B[0].length
C = create_const_matrix(nr,nc,0)
var res_valid = true
if (nr != nrb || nc != ncb) {
res_valid = false
}
for (ii=0; ii<nr; ii++) {
for (jj=0; jj<nc; jj++) {
C[ii][jj] = A[ii][jj] * B[ii][jj]
}
}
return [C,res_valid]
}
function kronecker(A,B) {
var C = new Array()
var nr = A.length
var nc = A[0].length
var nrb = B.length
var ncb = B[0].length
C = create_const_matrix(nr*nrb,nc*ncb,0)
var ii = 0
var jj = 0
var kk = 0
var mm = 0
for (ii=0; ii<nr; ii++) {
for (jj=0; jj<nc; jj++) {
var Mtmp = new Array()
Mtmp = mult_matrix_by_scalar(B,A[ii][jj])
for (kk=0; kk<nrb; kk++) {
for (mm=0; mm<ncb; mm++) {
C[(ii*nrb)+kk][(jj*ncb)+mm] = Mtmp[kk][mm]
}
}
}
}
return [C,true]
}
function subtr_matrices(A,B) {
var C = new Array()
var nr = A.length
var nc = A[0].length
var nrb = B.length
var ncb = B[0].length
C = create_const_matrix(nr,nc,0)
var res_valid = true
if (nr != nrb|| nc != ncb) {
res_valid = false
}
for (ii=0; ii<nr; ii++) {
for (jj=0; jj<nc; jj++) {
C[ii][jj] = A[ii][jj] - B[ii][jj]
}
}
return [C,res_valid]
}
function gram_schmidt(A) {
var dim = A.length
var U = new Array()
var E = new Array()
var u = new Array()
var e = new Array()
var a = new Array()
var dbg_info = "Debug Info"
U = create_const_matrix(dim,dim,0)
E = create_const_matrix(dim,dim,0)
u = create_const_matrix(dim,1,0)
e = create_const_matrix(dim,1,0)
a = create_const_matrix(dim,1,0)
dbg_info += " Dimension is " + dim.toString() + "<br>"
var jj = 0
var kk = 0
var mm = 0
var ii = 0
for (jj = 0; jj < dim; jj++) {
dbg_info += ("Index " + jj.toString() + "<br>")
if (jj == 0) {
u = extract_column(A,jj)
dbg_info += ("u<br>" + gen_printable_string_from_matrix(u,4,false) + "<br>")
e = mult_matrix_by_scalar(u,(1.0/calc_norm(u)))
for (ii = 0; ii < dim; ii++) {
E[ii][jj] = e[ii][0]
}
} else {
a = extract_column(A,jj)
u = a
dbg_info += ("u<br>" + gen_printable_string_from_matrix(u,5,false) + "<br>")
for (kk=0; kk < jj; kk++) {
dbg_info += ("**a column:<br>" + gen_printable_string_from_matrix(a,5,false) + "<br>")
dbg_info += ("**Ekk column:<br>" + gen_printable_string_from_matrix(extract_column(E,kk),5,false) + "<br>")
var inner_prod = dot_product(transpose_matrix(a),extract_column(E,kk))
dbg_info += ("**Inner prod: " + inner_prod.toString() + "<br>")
u = subtr_matrices(u,mult_matrix_by_scalar(extract_column(E,kk),inner_prod))
}
dbg_info += ("Final u<br>" + gen_printable_string_from_matrix(u,4,false) + "<br>")
//dbg_info += ("Inner prod<br>" + inner_prod.toString() + "<br>")
e = mult_matrix_by_scalar(u,(1.0/calc_norm(u)))
for (mm = 0; mm < dim; mm++) {
E[mm][jj] = e[mm][0]
}
}
}
// Create the R matrix
var R = new Array()
R = create_const_matrix(dim,dim,0)
for (aa=0; aa<dim; aa++) {
for (bb=aa; bb<dim; bb++) {
R[aa][bb] = dot_product(transpose_matrix(extract_column(A,bb)),extract_column(E,aa))
}
}
return [E,R,dbg_info]
}
var select = document.getElementById("SelectFile");
select.onchange = function(){
selectedString = select.options[select.selectedIndex].value
document.getElementById("p2").innerHTML = selectedString
}
$(document).ready(function() {
if(isAPIAvailable()) {
$('#files').bind('change', handleFileSelect);
}
});
function isAPIAvailable() {
// Check for the various File API support.
if (window.File && window.FileReader && window.FileList && window.Blob) {
// Great success! All the File APIs are supported.
return true;
} else {
// source: File API availability - http://caniuse.com/#feat=fileapi
// source: <output> availability - http://html5doctor.com/the-output-element/
document.writeln('The HTML5 APIs used in this form are only available in the following browsers:<br />');
// 6.0 File API & 13.0 <output>
document.writeln(' - Google Chrome: 13.0 or later<br />');
// 3.6 File API & 6.0 <output>
document.writeln(' - Mozilla Firefox: 6.0 or later<br />');
// 10.0 File API & 10.0 <output>
document.writeln(' - Internet Explorer: Not supported (partial support expected in 10.0)<br />');
// ? File API & 5.1 <output>
document.writeln(' - Safari: Not supported<br />');
// ? File API & 9.2 <output>
document.writeln(' - Opera: Not supported');
return false;
}
}
function handleFileSelect(evt) {
var files = evt.target.files; // FileList object
var file = files[0];
// read the file metadata
var output = ''
output += '<span style="font-weight:bold;">' + escape(file.name) + '</span><br />\n';
output += ' - FileType: ' + (file.type || 'n/a') + '<br />\n';
output += ' - FileSize: ' + file.size + ' bytes<br />\n';
output += ' - LastModified: ' + (file.lastModifiedDate ? file.lastModifiedDate.toLocaleDateString() : 'n/a') + '<br />\n';
// read the file contents
printTextArea(file);
// post the results
$('#list').append(output);
}
var selectedString = "Input_A"
var select = document.getElementById("SelectFile");
select.onchange = function(){
selectedString = select.options[select.selectedIndex].value
document.getElementById("p2").innerHTML = selectedString
}
var selectedString2 = "A_plus_B"
var select2 = document.getElementById("SelectOperation");
select2.onchange = function(){
selectedString2 = select2.options[select2.selectedIndex].value
document.getElementById("p3").innerHTML = selectedString2
}
var Res_mat = new Array()
function printTextArea(file) {
var reader = new FileReader();
reader.readAsText(file);
reader.onload = function(event){
var csv = event.target.result;
var data = $.csv.toArrays(csv);
var mystr = "";
for(var row in data) {
for (ii=0; ii < data[row].length; ii++) {
mystr += data[row][ii].toString()
if (ii < data[row].length-1) {
mystr += ","
}
}
mystr += "\n"
}
mystr = mystr.substring(0, mystr.length - 1)
if (selectedString == "Input_A") {
document.getElementById("InpABox").value = mystr;
} else {
document.getElementById("InpBBox").value = mystr;
}
};
reader.onerror = function(){ alert('Unable to read ' + file.fileName); };
}
/* The calculate function called when the Perform LU Decomposition button is pressed
*/
function calc() {
var dp = Number(document.getElementById("prec").value)
var inpstr = document.getElementById("InpABox").value;
var check_params = new Array()
check_params = validate_matrix(inpstr)
var inpstrB = document.getElementById("InpBBox").value;
var check_paramsB = new Array()
check_paramsB = validate_matrix(inpstrB)
if (check_params[0] == 0) {
alert("All rows should have the same number of entries for InputA!")
}
if (check_paramsB[0] == 0) {
alert("All rows should have the same number of entries for InputB!")
}
var A = new Array()
A = check_params[2]
var B = new Array()
B = check_paramsB[2]
var res_mult = new Array()
var res_add = new Array()
var res_sub = new Array()
var Res_mat = new Array()
if (selectedString2 == "A + B") {
res_add = add_matrices(A,B)
Res_mat = res_add[0]
} else if (selectedString2 == "A - B") {
res_sub = subtr_matrices(A,B)
Res_mat = res_sub[0]
} else if (selectedString2 == "B - A") {
res_sub = subtr_matrices(B,A)
Res_mat = res_sub[0]
} else if (selectedString2 == "A * B") {
res_mult = mult_matrices(A,B)
Res_mat = res_mult[0]
} else if (selectedString2 == "B * A") {
res_mult = mult_matrices(B,A)
Res_mat = res_mult[0]
} else if (selectedString2 == "A .* B") {
res_mult = mult_elem_matrices(A,B)
Res_mat = res_mult[0]
} else if (selectedString2 == "A kron B") {
res_mult = kronecker(A,B)
Res_mat = res_mult[0]
} else if (selectedString2 == "B kron A") {
res_mult = kronecker(B,A)
Res_mat = res_mult[0]
} else {
res_add = add_matrices(A,B)
Res_mat = res_add[0]
}
var Res_mat_str = ""
Res_mat_str = gen_printable_string_from_matrix(Res_mat,dp,true)
if (res_add[1] == false || res_sub[1] == false || res_mult[1] == false) {
Res_mat_str = "Invalid Result, matrices have incompatible sizes!"
}
document.getElementById("C").innerHTML = Res_mat_str;
}
</script>
Alijah Ahmedhttp://www.blogger.com/profile/01151589526613233918noreply@blogger.comtag:blogger.com,1999:blog-8441309013858171051.post-30074992822830859522020-05-06T01:34:00.002-07:002020-05-15T12:43:55.570-07:00Matrix Utilities: QR factorisation using Gram-Schmidt for Real Matrix<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-csv/0.8.3/jquery.csv.min.js"></script>
<script>
$(document).ready(function() {
if(isAPIAvailable()) {
$('#files').bind('change', handleFileSelect);
}
});
function isAPIAvailable() {
// Check for the various File API support.
if (window.File && window.FileReader && window.FileList && window.Blob) {
// Great success! All the File APIs are supported.
return true;
} else {
// source: File API availability - http://caniuse.com/#feat=fileapi
// source: <output> availability - http://html5doctor.com/the-output-element/
document.writeln('The HTML5 APIs used in this form are only available in the following browsers:<br />');
// 6.0 File API & 13.0 <output>
document.writeln(' - Google Chrome: 13.0 or later<br />');
// 3.6 File API & 6.0 <output>
document.writeln(' - Mozilla Firefox: 6.0 or later<br />');
// 10.0 File API & 10.0 <output>
document.writeln(' - Internet Explorer: Not supported (partial support expected in 10.0)<br />');
// ? File API & 5.1 <output>
document.writeln(' - Safari: Not supported<br />');
// ? File API & 9.2 <output>
document.writeln(' - Opera: Not supported');
return false;
}
}
function handleFileSelect(evt) {
var files = evt.target.files; // FileList object
var file = files[0];
// read the file metadata
var output = ''
output += '<span style="font-weight:bold;">' + escape(file.name) + '</span><br />\n';
output += ' - FileType: ' + (file.type || 'n/a') + '<br />\n';
output += ' - FileSize: ' + file.size + ' bytes<br />\n';
output += ' - LastModified: ' + (file.lastModifiedDate ? file.lastModifiedDate.toLocaleDateString() : 'n/a') + '<br />\n';
// read the file contents
printTextArea(file);
// post the results
$('#list').append(output);
}
function printTextArea(file) {
var reader = new FileReader();
reader.readAsText(file);
reader.onload = function(event){
var csv = event.target.result;
var data = $.csv.toArrays(csv);
var mystr = "";
for(var row in data) {
for (ii=0; ii < data[row].length; ii++) {
mystr += data[row][ii].toString()
if (ii < data[row].length-1) {
mystr += ","
}
}
mystr += "\n"
}
mystr = mystr.substring(0, mystr.length - 1)
document.getElementById("InpBox").value = mystr;
};
reader.onerror = function(){ alert('Unable to read ' + file.fileName); };
}
</script>
<body>
<script>
$(document).ready(function(){
$(".special").css("background-color", "white");
$(".special").css("width", "600px");
$(".special").css("height", "150px");
$(".special").css("font-size", "0.9em");
$(".special").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".special").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".special").css("font-weight","normal");
$(".special").css("border","1px solid black");
$(".precision").css("font-size", "1.0em");
$(".results").css("background-color", "pink");
$(".results").css("width", "600px");
$(".results").css("height", "150px");
$(".results").css("font-size", "0.9em");
$(".results").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".results").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".results").css("font-weight","normal");
$(".results").css("border","1px solid black");
$(".invres").css("background-color", "yellow");
$(".invres").css("width", "600px");
$(".invres").css("height", "100px");
$(".invres").css("font-size", "0.9em");
$(".invres").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".invres").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".invres").css("font-weight","normal");
$(".invres").css("border","1px solid black");
});
</script>
<p style="text-align:justify">This blog post implements the QR factorisation of a square matrix whose entries are real numbers. The Gram-Schmidt method is used to calculate the orthogonal Q matrix.</p>
<p style="text-align:justify">
For the (square) matrix entry, comma separated (the separation can also be a single whitespace) numbers need to be entered into the Input box, with no newline after the last line. A comma at the end of each line can be added optionally for comma separated numbers. Alternatively, you can load a CSV file into the Input box, by clicking on the Choose File button.</p>
<p style="text-align:justify">The precision of the results in decimal places can be specified in the text box headed "Precision (decimal places)". To perform the calculation, simply press the button labelled "Perform QR factorisation" further down this page. Two matrices will be printed in coloured textareas: the Q matrix which is orthogonal, and the upper-triangular R matrix. In addition, the matrix size and determinant will be printed.</p>
<br>
Precision (decimal places)
<br>
<textarea class="precision" name="textarea" rows="2" cols="10" id ="prec">
</textarea>
<br>
<br>
<div id=inputs class=clearfix>
<input type=file id=files name=files[] single />
</div>
<br>
<br>
<input name="b1" onclick="clear_input()" style="margin-left: 0px;" type="Button" value="Clear Input" />
<br>
<br>
Input<br>
<textarea class="special" name="textarea" rows="20" cols="50" id ="InpBox">
</textarea>
<br>
<input name="b1" onclick="calc()" style="margin-left: 0px;" type="Button" value="Perform QR factorisation" />
<br>
<p id="p1">Results pending...</p>
<br>
Q Matrix
<br>
<textarea class="results" rows="20" cols="50" id ="Q">
</textarea>
<br>
R Matrix
<br>
<textarea class="results" rows="20" cols="50" id ="R">
</textarea>
<br>
<br>
</body>
</html>
<script language="JavaScript">
window.onload = set_inputs();
function set_inputs(){
var str = "1,2,4" + "\n" + "3,4,6" + "\n" + "8,6,7"
document.getElementById("InpBox").value = str;
document.getElementById("prec").value = "5";
}
function clear_input() {
document.getElementById("InpBox").value = "";
}
function validate_matrix(inpstr) {
// The Matrix proper
var M = new Array()
var inp_arr = new Array()
inp_arr = inpstr.split("\n");
var num_rows = inp_arr.length
if (inp_arr[num_rows - 1] == "")
num_rows = num_rows - 1
var splitchar = ","
var tmpStr = inp_arr[0]
if (tmpStr.indexOf(',') == -1) {
splitchar = " "
}
var row_arr_filtered = inp_arr[0].split(splitchar).filter(function (el) {
return el != "";
});
var num_cols
var num_cols = row_arr_filtered.length
var dbg_str = ""
var cols_match = 1
// Check if all rows have same number of elements
for (ii = 0; ii < num_rows; ii++) {
var vec = inp_arr[ii].split(splitchar).filter(function (el) {
return el != "";
});
var num_vec = new Array()
for (kk=0; kk < vec.length; kk++) {
num_vec.push(Number(vec[kk]))
}
M.push(num_vec)
if (ii == 0){
num_cols = vec.length
} else {
if (num_cols != vec.length) {
cols_match = 0
}
}
}
var dbg_str = ""
dbg_str += "Number of rows is " + num_rows.toString()
dbg_str += ", number of columns is " + num_cols.toString()
var isSquare = 1
if (num_rows != num_cols) {
isSquare = 0
}
return [cols_match, dbg_str,M, isSquare]
}
function gen_printable_string_from_matrix(M,dp,isTextArea)
{
var Nr = M.length
var Nc = M[0].length
var res_str = ""
for (i=0; i < Nr; i++)
{
for (j=0; j < Nc;j++)
{
res_str += M[i][j].toFixed(dp)
if (j < Nc-1)
res_str += ", "
}
if (isTextArea) {
res_str += "\n"
} else {
res_str += "<br>"
}
}
return res_str
}
function create_const_matrix(m,n,val)
{
var M_res = new Array
for (i=0; i < m; i++)
{
var vec = new Array
for (j=0; j < n;j++)
{
vec.push(val)
}
M_res.push(vec)
}
return M_res
}
function transpose_matrix(M)
{
var nr = M.length
var nc = M[0].length
var Mres = create_const_matrix(nc,nr,0);
for (i=0; i < nc; i++)
{
for (j=0; j < nr;j++)
{
Mres[i][j]=M[j][i]
}
}
return Mres
}
function get_max_idx(V,offs) {
var idx = 0
var maxVal = 0
for (k=0; k < V.length; k++) {
if (k == 0) {
maxVal = V[k]
} else {
if (Math.abs(V[k])>Math.abs(maxVal)) {
maxVal = V[k]
idx = k
}
}
}
return idx+offs
}
function swap_rows(Minp,i,j) {
var M_res = Minp
var nr = Minp.length
var nc = Minp[0].length
var tmpVal = 0
for (k=0; k < nc; k++) {
tmpVal = M_res[i][k]
M_res[i][k] = M_res[j][k]
M_res[j][k] = tmpVal
}
return M_res
}
function lu_factorisation_partial_pivot(M) {
var U = M
var nr = U.length
var L = create_const_matrix(nr,nr,0);
var P = create_const_matrix(nr,nr,0);
var num_swaps = 0
for (k=0; k < nr; k++) {
L[k][k] = 1.0
P[k][k] = 1.0
}
for (ii=0; ii< nr-1; ii++) {
var vec = new Array()
for (jj=ii; jj < nr; jj++) {
vec.push(U[jj][ii])
}
var elem = 0.0
var idx = get_max_idx(vec,ii)
var el_tmp = 0.0
for (jj =ii; jj < nr; jj++ ) {
el_tmp = U[ii][jj]
U[ii][jj] = U[idx][jj]
U[idx][jj] = el_tmp
}
for (jj =0; jj < ii; jj++ ) {
el_tmp = L[ii][jj]
L[ii][jj] = L[idx][jj]
L[idx][jj] = el_tmp
}
if (ii != idx) {
num_swaps += 1
}
Pnew = swap_rows(P,ii,idx)
P = Pnew
for (jj = ii + 1; jj < nr; jj++) {
if (Math.abs(U[jj][ii])<1e-16) {
L[jj][ii] = 0.0
} else {
L[jj][ii] = U[jj][ii]/U[ii][ii]
}
for (xx=ii;xx < nr; xx++) {
U[jj][xx] = U[jj][xx] - (L[jj][ii] * U[ii][xx])
}
}
}
var det_res = 1.0
for (vv=0; vv < nr; vv++) {
det_res = det_res * (U[vv][vv] * L[vv][vv])
}
det_res = det_res * ((-1)**num_swaps)
return [U,L,P,num_swaps,det_res]
}
function mult_matrices(A,B) {
var nra = A.length
var nca = A[0].length
var nrb = B.length
var ncb = B[0].length
if (nca != nrb) {
alert("Cannot multiply these matrics!")
}
var C = new Array()
C = create_const_matrix(nra,ncb,0)
for (aa = 0; aa < nra; aa++) {
for (bb = 0; bb < ncb; bb++) {
var tmp_res = 0.0
for (cc = 0; cc < nca; cc++) {
tmp_res = tmp_res + (A[aa][cc] * B[cc][bb])
}
C[aa][bb] = tmp_res
}
}
return C
}
function back_subst(U,d) {
var nr = d.length
var x = new Array()
x = create_const_matrix(nr,1,0)
x[nr - 1][0] = d[nr-1][0]/(U[nr-1][nr-1]);
for (i=nr-2; i >= 0; i--) {
var sum_ux = 0.0;
for (j=i+1; j < nr; j++) {
sum_ux += (U[i][j]*x[j][0]);
}
x[i][0] = (1/U[i][i]) * (d[i][0] - sum_ux);
}
return x;
}
function fwd_subst(L,b) {
var d = new Array()
var nr = b.length
d = create_const_matrix(nr,1,0)
d[0][0] = b[0][0]
for (i=1; i < nr; i++) {
var sum_ld = 0.0;
for (j=0; j < i; j++) {
sum_ld += (L[i][j] * d[j][0]);
}
d[i][0] = b[i][0] - sum_ld;
}
return d;
}
function extract_column(M,k) {
var R = new Array()
var dim = M.length
R = create_const_matrix(dim,1,0)
for (ii = 0; ii < dim; ii++) {
R[ii][0] = M[ii][k]
}
return R
}
function calc_norm(M) {
var nr = M.length
var nc = M[0].length
var sum_sq = 0.0
for (ii = 0; ii < nr; ii++) {
for (jj=0; jj < nc; jj++) {
sum_sq = sum_sq + (M[ii][jj] * M[ii][jj])
}
}
return Math.sqrt(sum_sq)
}
function mult_matrix_by_scalar(M,k) {
var nr = M.length
var nc = M[0].length
var R = create_const_matrix(nr,nc,0)
for (ii = 0; ii < nr; ii++) {
for (jj=0; jj < nc; jj++) {
R[ii][jj] = k * M[ii][jj]
}
}
return R
}
function dot_product(a,b) {
var vec_len = a[0].length
var res = 0.0
for (ii=0; ii < vec_len; ii++) {
res = res + (a[0][ii] * b[ii][0])
}
return res
}
function add_matrices(A,B) {
var C = new Array()
var nr = A.length
var nc = A[0].length
C = create_const_matrix(nr,nc,0)
for (ii=0; ii<nr; ii++) {
for (jj=0; jj<nc; jj++) {
C[ii][jj] = A[ii][jj] + B[ii][jj]
}
}
return C
}
function subtr_matrices(A,B) {
var C = new Array()
var nr = A.length
var nc = A[0].length
C = create_const_matrix(nr,nc,0)
for (ii=0; ii<nr; ii++) {
for (jj=0; jj<nc; jj++) {
C[ii][jj] = A[ii][jj] - B[ii][jj]
}
}
return C
}
function gram_schmidt(A) {
var dim = A.length
var U = new Array()
var E = new Array()
var u = new Array()
var e = new Array()
var a = new Array()
var dbg_info = "Debug Info"
U = create_const_matrix(dim,dim,0)
E = create_const_matrix(dim,dim,0)
u = create_const_matrix(dim,1,0)
e = create_const_matrix(dim,1,0)
a = create_const_matrix(dim,1,0)
dbg_info += " Dimension is " + dim.toString() + "<br>"
var jj = 0
var kk = 0
var mm = 0
var ii = 0
for (jj = 0; jj < dim; jj++) {
dbg_info += ("Index " + jj.toString() + "<br>")
if (jj == 0) {
u = extract_column(A,jj)
dbg_info += ("u<br>" + gen_printable_string_from_matrix(u,4,false) + "<br>")
e = mult_matrix_by_scalar(u,(1.0/calc_norm(u)))
for (ii = 0; ii < dim; ii++) {
E[ii][jj] = e[ii][0]
}
} else {
a = extract_column(A,jj)
u = a
dbg_info += ("u<br>" + gen_printable_string_from_matrix(u,5,false) + "<br>")
for (kk=0; kk < jj; kk++) {
dbg_info += ("**a column:<br>" + gen_printable_string_from_matrix(a,5,false) + "<br>")
dbg_info += ("**Ekk column:<br>" + gen_printable_string_from_matrix(extract_column(E,kk),5,false) + "<br>")
var inner_prod = dot_product(transpose_matrix(a),extract_column(E,kk))
dbg_info += ("**Inner prod: " + inner_prod.toString() + "<br>")
u = subtr_matrices(u,mult_matrix_by_scalar(extract_column(E,kk),inner_prod))
}
dbg_info += ("Final u<br>" + gen_printable_string_from_matrix(u,4,false) + "<br>")
//dbg_info += ("Inner prod<br>" + inner_prod.toString() + "<br>")
e = mult_matrix_by_scalar(u,(1.0/calc_norm(u)))
for (mm = 0; mm < dim; mm++) {
E[mm][jj] = e[mm][0]
}
}
}
// Create the R matrix
var R = new Array()
R = create_const_matrix(dim,dim,0)
for (aa=0; aa<dim; aa++) {
for (bb=aa; bb<dim; bb++) {
R[aa][bb] = dot_product(transpose_matrix(extract_column(A,bb)),extract_column(E,aa))
}
}
return [E,R,dbg_info]
}
/* The calculate function called when the Perform LU Decomposition button is pressed
*/
function calc() {
var dp = Number(document.getElementById("prec").value)
var inpstr = document.getElementById("InpBox").value;
var check_params = new Array()
check_params = validate_matrix(inpstr)
if (check_params[0] == 0) {
alert("All rows should have the same number of entries!")
}
var isSquare = check_params[3]
if (isSquare == 0 && check_params[0] == 1) {
alert("Matrix needs to be square!")
}
var dbg_str = check_params[1]
var M = new Array()
M = check_params[2]
var M_str = gen_printable_string_from_matrix(M,dp,true)
var E_mat = new Array()
gs_res = gram_schmidt(M)
E_mat = gs_res[0]
var E_mat_str = gen_printable_string_from_matrix(E_mat,dp,true)
R_mat = gs_res[1]
var R_mat_str = gen_printable_string_from_matrix(R_mat,dp,true)
var M_col_1 = new Array()
M_col_1 = extract_column(M,1)
var norm_M_col_1 = calc_norm(M_col_1)
var M_col_1_str = gen_printable_string_from_matrix(M_col_1,dp,false)
var M_sc = new Array()
M_sc = mult_matrix_by_scalar(M,1.0)
var M_sc_str = gen_printable_string_from_matrix(M_sc,dp,false)
var Mt = new Array()
Mt = transpose_matrix(M)
var Mt_str = gen_printable_string_from_matrix(Mt,dp,true)
// The LU factorisation proper
res_lu = lu_factorisation_partial_pivot(M)
var U = res_lu[0]
var L = res_lu[1]
var P = res_lu[2]
var num_swaps = res_lu[3]
var det_res = res_lu[4]
// Stringify so as to put results in the textareas
/*
var U_str = gen_printable_string_from_matrix(U,dp,true)
var L_str = gen_printable_string_from_matrix(L,dp,true)
var P_str = gen_printable_string_from_matrix(P,0,true)
*/
var det_res = res_lu[4]
// Calculate the inverse matrix fron the LU factorisation
var dim = U.length
var inv_res = new Array()
inv_res = create_const_matrix(dim,dim,0)
var C_inv = new Array()
for (kk=0; kk < dim; kk++) {
var ei = new Array()
ei = create_const_matrix(dim,1,0);
ei[kk][0] = 1.0;
var ri = new Array()
ri = create_const_matrix(dim,1,0);
ri = back_subst(U,fwd_subst(L,ei));
for (mm=0; mm < dim; mm++) {
inv_res[mm][kk] = ri[mm][0];
}
}
C_inv = mult_matrices(inv_res,P)
var C_str = gen_printable_string_from_matrix(C_inv,dp,true)
// Append matrix determinant
dbg_str += "<br>" + "Determinant of matrix is " + det_res.toFixed(dp)
/*
dbg_str += "<br>"
dbg_str += M_col_1_str
dbg_str += "<br>"
dbg_str += "Norm is " + norm_M_col_1.toString()
dbg_str += "<br>"
dbg_str += "Scaled matrix is " + "<br>"
dbg_str += M_sc_str
dbg_str += "<br>" + "Gram-Schmidt Q matrix is " + "<br>"
dbg_str += E_mat_str
dbg_str += "<br>" + "Gram-Schmidt R matrix is " + "<br>"
dbg_str += R_mat_str
dbg_str += "Debug for Gram-Schmidt " + "<br>"
dbg_str += gs_res[2]
*/
document.getElementById("p1").innerHTML = dbg_str;
document.getElementById("Q").innerHTML = E_mat_str;
document.getElementById("R").innerHTML = R_mat_str;
}
</script>
Alijah Ahmedhttp://www.blogger.com/profile/01151589526613233918noreply@blogger.comtag:blogger.com,1999:blog-8441309013858171051.post-68854948548651658162020-05-04T03:22:00.000-07:002020-05-15T01:58:14.232-07:00Matrix Utilities: LU factorisation and Inverse of Real Matrix<!DOCTYPE html>
<html>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-csv/0.8.3/jquery.csv.min.js"></script>
<script>
$(document).ready(function() {
if(isAPIAvailable()) {
$('#files').bind('change', handleFileSelect);
}
});
function isAPIAvailable() {
// Check for the various File API support.
if (window.File && window.FileReader && window.FileList && window.Blob) {
// Great success! All the File APIs are supported.
return true;
} else {
// source: File API availability - http://caniuse.com/#feat=fileapi
// source: <output> availability - http://html5doctor.com/the-output-element/
document.writeln('The HTML5 APIs used in this form are only available in the following browsers:<br />');
// 6.0 File API & 13.0 <output>
document.writeln(' - Google Chrome: 13.0 or later<br />');
// 3.6 File API & 6.0 <output>
document.writeln(' - Mozilla Firefox: 6.0 or later<br />');
// 10.0 File API & 10.0 <output>
document.writeln(' - Internet Explorer: Not supported (partial support expected in 10.0)<br />');
// ? File API & 5.1 <output>
document.writeln(' - Safari: Not supported<br />');
// ? File API & 9.2 <output>
document.writeln(' - Opera: Not supported');
return false;
}
}
function handleFileSelect(evt) {
var files = evt.target.files; // FileList object
var file = files[0];
// read the file metadata
var output = ''
output += '<span style="font-weight:bold;">' + escape(file.name) + '</span><br />\n';
output += ' - FileType: ' + (file.type || 'n/a') + '<br />\n';
output += ' - FileSize: ' + file.size + ' bytes<br />\n';
output += ' - LastModified: ' + (file.lastModifiedDate ? file.lastModifiedDate.toLocaleDateString() : 'n/a') + '<br />\n';
// read the file contents
printTextArea(file);
// post the results
$('#list').append(output);
}
function printTextArea(file) {
var reader = new FileReader();
reader.readAsText(file);
reader.onload = function(event){
var csv = event.target.result;
var data = $.csv.toArrays(csv);
var mystr = "";
for(var row in data) {
for (ii=0; ii < data[row].length; ii++) {
mystr += data[row][ii].toString()
if (ii < data[row].length-1) {
mystr += ","
}
}
mystr += "\n"
}
mystr = mystr.substring(0, mystr.length - 1)
document.getElementById("InpBox").value = mystr;
};
reader.onerror = function(){ alert('Unable to read ' + file.fileName); };
}
</script>
<body>
<script>
$(document).ready(function(){
$(".special").css("background-color", "white");
$(".special").css("width", "600px");
$(".special").css("height", "100px");
$(".special").css("font-size", "0.9em");
$(".special").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".special").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".special").css("font-weight","normal");
$(".special").css("border","1px solid black");
$(".precision").css("font-size", "1.0em");
$(".results").css("background-color", "pink");
$(".results").css("width", "600px");
$(".results").css("height", "100px");
$(".results").css("font-size", "0.9em");
$(".results").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".results").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".results").css("font-weight","normal");
$(".results").css("border","1px solid black");
$(".invres").css("background-color", "yellow");
$(".invres").css("width", "600px");
$(".invres").css("height", "100px");
$(".invres").css("font-size", "0.9em");
$(".invres").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".invres").css("font-family","Verdana, Arial, Helvetica, sans-serif");
$(".invres").css("font-weight","normal");
$(".invres").css("border","1px solid black");
});
</script>
<p style="text-align:justify">This blog post implements the LU factorisation and inverse calculation of a square matrix for real numbers. A partial pivot method is used for the LU factorisation. The inverse matrix is calculated from the results of the LU factorisation, using backward and forward substitution.</p>
<p style="text-align:justify">
For the (square) matrix entry, comma separated (the separation can also be a single whitespace) numbers need to be entered into the Input box, with no newline after the last line. A comma at the end of each line can be added optionally for comma separated numbers. Alternatively, you can load a CSV file into the Input box, by clicking on the Choose File button.</p>
<p style="text-align:justify">The precision of the results in decimal places can be specified in the text box headed "Precision (decimal places)". To perform the calculation, simply press the button labelled "Perform LU factorisation and Inverse" further down this page. Four matrices will be printed in coloured textareas: the Lower Triangular, Upper Triangular, Permutation and Inverse. In addition, the matrix size and determinant will be printed.</p>
<br>
Precision (decimal places)
<br>
<textarea class="precision" name="textarea" rows="2" cols="10" id ="prec">
</textarea>
<br>
<br>
<div id=inputs class=clearfix>
<input type=file id=files name=files[] single />
</div>
<br>
<br>
<input name="b1" onclick="clear_input()" style="margin-left: 0px;" type="Button" value="Clear Input" />
<br>
<br>
Input<br>
<textarea class="special" name="textarea" rows="20" cols="50" id ="InpBox">
</textarea>
<br>
<input name="b1" onclick="calc()" style="margin-left: 0px;" type="Button" value="Perform LU factorisation and Inverse" />
<br>
<p id="p1">Results pending...</p>
<br>
Lower Triangular Matrix
<br>
<textarea class="results" rows="20" cols="50" id ="L">
</textarea>
<br>
Upper Triangular Matrix
<br>
<textarea class="results" rows="20" cols="50" id ="U">
</textarea>
<br>
Permutation Matrix
<br>
<textarea class="results" rows="10" cols="30" id ="P">
</textarea>
<br>
Inverse Matrix
<br>
<textarea class="invres" rows="20" cols="30" id ="I">
</textarea>
<br>
</body>
</html>
<script language="JavaScript">
window.onload = set_inputs();
function set_inputs(){
var str = "1,2,4" + "\n" + "3,4,6" + "\n" + "8,6,7"
document.getElementById("InpBox").value = str;
document.getElementById("prec").value = "5";
}
function clear_input() {
document.getElementById("InpBox").value = "";
}
function validate_matrix(inpstr) {
// The Matrix proper
var M = new Array()
var inp_arr = new Array()
inp_arr = inpstr.split("\n");
var num_rows = inp_arr.length
if (inp_arr[num_rows - 1] == "")
num_rows = num_rows - 1
var splitchar = ","
var tmpStr = inp_arr[0]
if (tmpStr.indexOf(',') == -1) {
splitchar = " "
}
var row_arr_filtered = inp_arr[0].split(splitchar).filter(function (el) {
return el != "";
});
var num_cols
var num_cols = row_arr_filtered.length
var dbg_str = ""
var cols_match = 1
// Check if all rows have same number of elements
for (ii = 0; ii < num_rows; ii++) {
var vec = inp_arr[ii].split(splitchar).filter(function (el) {
return el != "";
});
var num_vec = new Array()
for (kk=0; kk < vec.length; kk++) {
num_vec.push(Number(vec[kk]))
}
M.push(num_vec)
if (ii == 0){
num_cols = vec.length
} else {
if (num_cols != vec.length) {
cols_match = 0
}
}
}
var dbg_str = ""
dbg_str += "Number of rows is " + num_rows.toString()
dbg_str += ", number of columns is " + num_cols.toString()
var isSquare = 1
if (num_rows != num_cols) {
isSquare = 0
}
return [cols_match, dbg_str,M, isSquare]
}
function gen_printable_string_from_matrix(M,dp,isTextArea)
{
var Nr = M.length
var Nc = M[0].length
var res_str = ""
for (i=0; i < Nr; i++)
{
for (j=0; j < Nc;j++)
{
res_str += M[i][j].toFixed(dp)
if (j < Nc-1)
res_str += ", "
}
if (isTextArea) {
res_str += "\n"
} else {
res_str += "<br>"
}
}
return res_str
}
function create_const_matrix(m,n,val)
{
var M_res = new Array
for (i=0; i < m; i++)
{
var vec = new Array
for (j=0; j < n;j++)
{
vec.push(val)
}
M_res.push(vec)
}
return M_res
}
function transpose_matrix(M)
{
var nr = M.length
var nc = M[0].length
var Mres = create_const_matrix(nc,nr,0);
for (i=0; i < nc; i++)
{
for (j=0; j < nr;j++)
{
Mres[i][j]=M[j][i]
}
}
return Mres
}
function get_max_idx(V,offs) {
var idx = 0
var maxVal = 0
for (k=0; k < V.length; k++) {
if (k == 0) {
maxVal = V[k]
} else {
if (Math.abs(V[k])>Math.abs(maxVal)) {
maxVal = V[k]
idx = k
}
}
}
return idx+offs
}
function swap_rows(Minp,i,j) {
var M_res = Minp
var nr = Minp.length
var nc = Minp[0].length
var tmpVal = 0
for (k=0; k < nc; k++) {
tmpVal = M_res[i][k]
M_res[i][k] = M_res[j][k]
M_res[j][k] = tmpVal
}
return M_res
}
function lu_factorisation_partial_pivot(M) {
var U = M
var nr = U.length
var L = create_const_matrix(nr,nr,0);
var P = create_const_matrix(nr,nr,0);
var num_swaps = 0
for (k=0; k < nr; k++) {
L[k][k] = 1.0
P[k][k] = 1.0
}
for (ii=0; ii< nr-1; ii++) {
var vec = new Array()
for (jj=ii; jj < nr; jj++) {
vec.push(U[jj][ii])
}
var elem = 0.0
var idx = get_max_idx(vec,ii)
var el_tmp = 0.0
for (jj =ii; jj < nr; jj++ ) {
el_tmp = U[ii][jj]
U[ii][jj] = U[idx][jj]
U[idx][jj] = el_tmp
}
for (jj =0; jj < ii; jj++ ) {
el_tmp = L[ii][jj]
L[ii][jj] = L[idx][jj]
L[idx][jj] = el_tmp
}
if (ii != idx) {
num_swaps += 1
}
Pnew = swap_rows(P,ii,idx)
P = Pnew
for (jj = ii + 1; jj < nr; jj++) {
if (Math.abs(U[jj][ii])<1e-16) {
L[jj][ii] = 0.0
} else {
L[jj][ii] = U[jj][ii]/U[ii][ii]
}
for (xx=ii;xx < nr; xx++) {
U[jj][xx] = U[jj][xx] - (L[jj][ii] * U[ii][xx])
}
}
}
var det_res = 1.0
for (vv=0; vv < nr; vv++) {
det_res = det_res * (U[vv][vv] * L[vv][vv])
}
det_res = det_res * ((-1)**num_swaps)
return [U,L,P,num_swaps,det_res]
}
function mult_matrices(A,B) {
var nra = A.length
var nca = A[0].length
var nrb = B.length
var ncb = B[0].length
if (nca != nrb) {
alert("Cannot multiply these matrics!")
}
var C = new Array()
C = create_const_matrix(nra,ncb,0)
for (aa = 0; aa < nra; aa++) {
for (bb = 0; bb < ncb; bb++) {
var tmp_res = 0.0
for (cc = 0; cc < nca; cc++) {
tmp_res = tmp_res + (A[aa][cc] * B[cc][bb])
}
C[aa][bb] = tmp_res
}
}
return C
}
function back_subst(U,d) {
var nr = d.length
var x = new Array()
x = create_const_matrix(nr,1,0)
x[nr - 1][0] = d[nr-1][0]/(U[nr-1][nr-1]);
for (i=nr-2; i >= 0; i--) {
var sum_ux = 0.0;
for (j=i+1; j < nr; j++) {
sum_ux += (U[i][j]*x[j][0]);
}
x[i][0] = (1/U[i][i]) * (d[i][0] - sum_ux);
}
return x;
}
function fwd_subst(L,b) {
var d = new Array()
var nr = b.length
d = create_const_matrix(nr,1,0)
d[0][0] = b[0][0]
for (i=1; i < nr; i++) {
var sum_ld = 0.0;
for (j=0; j < i; j++) {
sum_ld += (L[i][j] * d[j][0]);
}
d[i][0] = b[i][0] - sum_ld;
}
return d;
}
/* The calculate function called when the Perform LU Decomposition button is pressed
*/
function calc() {
var dp = Number(document.getElementById("prec").value)
var inpstr = document.getElementById("InpBox").value;
var check_params = new Array()
check_params = validate_matrix(inpstr)
if (check_params[0] == 0) {
alert("All rows should have the same number of entries!")
}
var isSquare = check_params[3]
if (isSquare == 0 && check_params[0] == 1) {
alert("Matrix needs to be square!")
}
var dbg_str = check_params[1]
var M = new Array()
M = check_params[2]
var M_str = gen_printable_string_from_matrix(M,dp,true)
var Mt = new Array()
Mt = transpose_matrix(M)
var Mt_str = gen_printable_string_from_matrix(Mt,dp,true)
// The LU factorisation proper
res_lu = lu_factorisation_partial_pivot(M)
var U = res_lu[0]
var L = res_lu[1]
var P = res_lu[2]
var num_swaps = res_lu[3]
var det_res = res_lu[4]
// Stringify so as to put results in the textareas
var U_str = gen_printable_string_from_matrix(U,dp,true)
var L_str = gen_printable_string_from_matrix(L,dp,true)
var P_str = gen_printable_string_from_matrix(P,0,true)
var det_res = res_lu[4]
// Calculate the inverse matrix fron the LU factorisation
var dim = U.length
var inv_res = new Array()
inv_res = create_const_matrix(dim,dim,0)
var C_inv = new Array()
for (kk=0; kk < dim; kk++) {
var ei = new Array()
ei = create_const_matrix(dim,1,0);
ei[kk][0] = 1.0;
var ri = new Array()
ri = create_const_matrix(dim,1,0);
ri = back_subst(U,fwd_subst(L,ei));
for (mm=0; mm < dim; mm++) {
inv_res[mm][kk] = ri[mm][0];
}
}
C_inv = mult_matrices(inv_res,P)
var C_str = gen_printable_string_from_matrix(C_inv,dp,true)
// Append matrix determinant
dbg_str += "<br>" + "Determinant of matrix is " + det_res.toFixed(dp)
document.getElementById("p1").innerHTML = dbg_str;
document.getElementById("U").innerHTML = U_str;
document.getElementById("L").innerHTML = L_str;
document.getElementById("P").innerHTML = P_str;
document.getElementById("I").innerHTML = C_str;
}
</script>
Alijah Ahmedhttp://www.blogger.com/profile/01151589526613233918noreply@blogger.comtag:blogger.com,1999:blog-8441309013858171051.post-71316226256428334862017-12-09T03:44:00.000-08:002020-04-12T06:57:12.846-07:00Two-way ANOVA without replication<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script>
$(document).ready(function() {
if(isAPIAvailable()) {
$('#files').bind('change', handleFileSelect);
}
});
function isAPIAvailable() {
// Check for the various File API support.
if (window.File && window.FileReader && window.FileList && window.Blob) {
// Great success! All the File APIs are supported.
return true;
} else {
// source: File API availability - http://caniuse.com/#feat=fileapi
// source: <output> availability - http://html5doctor.com/the-output-element/
document.writeln('The HTML5 APIs used in this form are only available in the following browsers:<br />');
// 6.0 File API & 13.0 <output>
document.writeln(' - Google Chrome: 13.0 or later<br />');
// 3.6 File API & 6.0 <output>
document.writeln(' - Mozilla Firefox: 6.0 or later<br />');
// 10.0 File API & 10.0 <output>
document.writeln(' - Internet Explorer: Not supported (partial support expected in 10.0)<br />');
// ? File API & 5.1 <output>
document.writeln(' - Safari: Not supported<br />');
// ? File API & 9.2 <output>
document.writeln(' - Opera: Not supported');
return false;
}
}
function handleFileSelect(evt) {
var files = evt.target.files; // FileList object
var file = files[0];
// read the file metadata
var output = ''
output += '<span style="font-weight:bold;">' + escape(file.name) + '</span><br />\n';
output += ' - FileType: ' + (file.type || 'n/a') + '<br />\n';
output += ' - FileSize: ' + file.size + ' bytes<br />\n';
output += ' - LastModified: ' + (file.lastModifiedDate ? file.lastModifiedDate.toLocaleDateString() : 'n/a') + '<br />\n';
// read the file contents
printTextArea(file);
// post the results
$('#list').append(output);
}
function printTextArea(file) {
var reader = new FileReader();
reader.readAsText(file);
reader.onload = function(event){
var csv = event.target.result;
var data = $.csv.toArrays(csv);
var mystr = "";
for(var row in data) {
mystr = mystr.concat(data[row]);
mystr = mystr.concat("\n");
}
mystr = mystr.substring(0, mystr.length - 1)
document.getElementById("Data_inp").value = mystr;
};
reader.onerror = function(){ alert('Unable to read ' + file.fileName); };
}
</script>
<p style="text-align:justify">This blog post implements the two-way ANOVA test without replication. Simply enter comma separated numbers as in the format shown - the rows correspond to the blocks, whilst the columns correspond to the treatment- and press the calculate button. Note that there must be no trailing comma at the end of each line, and no newline after the last line.</p>
<textarea rows="10" cols="25" id ="Data_inp">
</textarea>
<br>
<div style="width:350px;float: left;">
<input name="b1" onclick="clear_textbox()" style="margin-left: 0px;" type="Button" value="Clear input" />
</div>
<br>
<br>
<input name="b2" onclick="calc()" style="margin-left: 0px;" type="Button" value="Calculate" />
<br>
<p id="p1">Results pending...</p>
<br>
<script language="JavaScript">
window.onload = set_inputs();
var x = new Array();
var y = new Array();
function set_inputs() {
var strtmp = "1,2,3,4" + "\n" + "5,6,7,8" + "\n" + "9,10,11,12" + "\n" + "13,14,15,16";
document.getElementById("Data_inp").value = strtmp;
}
function clear_textbox() {
document.getElementById("Data_inp").value = "";
}
function calc_mean(inp) {
var N = inp.length
var sum = 0.0
for (ii = 0; ii < N; ii++) {
sum = sum + inp[ii]
}
var mu = sum/(N+0.0)
return mu
}
function calc_var(inp) {
var N = inp.length
var mu = calc_mean(inp)
var sum_sq = 0.0
for (ii = 0; ii < N; ii++) {
sum_sq = sum_sq + ((inp[ii] - mu)*(inp[ii] - mu))
}
var res = sum_sq/(N-1.0)
return res
}
function calc() {
var inpdat = document.getElementById("Data_inp").value;
var datalines = new Array();
datalines = inpdat.split("\n");
var k = datalines.length;
var num_in_line = new Array();
var dmat = new Array();
var lines_match = 1;
var n;
for (ii = 0; ii < k; ii++) {
if (ii==0) {
num_in_line[ii] = datalines[ii].split(",").length;
n = num_in_line[ii];
} else {
num_in_line[ii] = datalines[ii].split(",").length;
if (num_in_line[ii] != num_in_line[0]) {
alert("All lines must have the same number of samples (i.e. treatments)")
lines_match = 0
}
}
var line_no = ii+1;
}
var total_sum = 0.0;
var total_sum_sq = 0.0;
var rowsums = new Array()
var colsums = new Array();
var block_means = new Array()
var block_vars = new Array()
var treat_means = new Array()
var treat_vars = new Array()
var sstr = 0.0;
var str = "There are " + n + " treatments and " + k + " blocks" + "<br>";
if (lines_match == 1) {
for (ii = 0; ii < k; ii++) {
dmat[ii] = [];
for (jj = 0; jj < n; jj++) {
dmat[ii].push(0.0);
}
}
for (ii = 0; ii < k; ii++) {
var numRows = new Array()
numRows = datalines[ii].split(",")
rowsums[ii] = 0.0;
for (jj = 0; jj < n; jj++) {
dmat[ii][jj] = Number(numRows[jj])
if (ii == 0) {
colsums[jj] = dmat[ii][jj];
} else {
colsums[jj] = colsums[jj] + dmat[ii][jj];
}
rowsums[ii] = rowsums[ii] + dmat[ii][jj];
total_sum = total_sum + dmat[ii][jj];
total_sum_sq = total_sum_sq + (dmat[ii][jj] * dmat[ii][jj])
}
}
}
for (jj=0; jj < k; jj++) {
var tmp_vec = new Array()
for (mm = 0; mm < n; mm++) {
tmp_vec.push(dmat[jj][mm])
}
block_means.push(calc_mean(tmp_vec))
block_vars.push(calc_var(tmp_vec))
}
for (jj=0; jj < n; jj++) {
var tmp_vec = new Array()
for (mm = 0; mm < k; mm++) {
tmp_vec.push(dmat[mm][jj])
}
treat_means.push(calc_mean(tmp_vec))
treat_vars.push(calc_var(tmp_vec))
}
var sst = 0.0;
var sstr = 0.0;
var ssbl = 0.0;
var sse = 0.0;
var mu = ((total_sum * total_sum)/(k*n))
sst = total_sum_sq - mu
for (ii =0 ; ii < colsums.length; ii++) {
sstr = sstr + (colsums[ii] * colsums[ii])
}
sstr = (sstr/k) - mu;
for (ii =0 ; ii < rowsums.length; ii++) {
ssbl = ssbl + (rowsums[ii] * rowsums[ii])
}
ssbl = (ssbl/n) - mu;
sse = sst - sstr - ssbl;
var mstr = sstr/(n-1)
var msbl = ssbl/(k-1)
var mse = sse/((n-1)*(k-1));
var f_tr = mstr/mse
var f_bl = msbl/mse
var p_f_tr = 1 - f_cdf(f_tr,n-1,(n-1)*(k-1))
var p_f_bl = 1 - f_cdf(f_bl,k-1,(n-1)*(k-1))
var p_f_tr_str
var p_f_bl_str
if (Math.log10(p_f_tr) < -5)
p_f_tr_str = p_f_tr.toExp()
else
p_f_tr_str = p_f_tr.toFixed(5)
if (Math.log10(p_f_bl) < -5)
p_f_bl_str = p_f_bl.toExp()
else
p_f_bl_str = p_f_bl.toFixed(5)
var sstr_percent = (sstr/sst)*100.0;
var ssbl_percent = (ssbl/sst)*100.0;
var sse_percent = (sse/sst)*100.0;
str = str.concat("<table style=\"width:100%\">")
str = str.concat("<tbody>")
str = str.concat("<tr>")
str = str.concat("<th>")
str = str.concat("Source of variation")
str = str.concat("</th>")
str = str.concat("<th>")
str = str.concat("Sum Square")
str = str.concat("</th>")
str = str.concat("<th>")
str = str.concat("% of total var")
str = str.concat("</th>")
str = str.concat("<th>")
str = str.concat("DF")
str = str.concat("</th>")
str = str.concat("<th>")
str = str.concat("Mean Square")
str = str.concat("</th>")
str = str.concat("<th>")
str = str.concat("F")
str = str.concat("</th>")
str = str.concat("<th>")
str = str.concat("p-value")
str = str.concat("</th>")
str = str.concat("</tr>")
str = str.concat("<tr>")
str = str.concat("<td>")
str = str.concat("Treatment")
str = str.concat("</td>")
str = str.concat("<td>")
str = str.concat(sstr.toFixed(3))
str = str.concat("</td>")
str = str.concat("<td>")
str = str.concat(sstr_percent.toFixed(3))
str = str.concat("</td>")
str = str.concat("<td>")
str = str.concat(k-1)
str = str.concat("</td>")
str = str.concat("<td>")
str = str.concat(mstr.toFixed(3))
str = str.concat("</td>")
str = str.concat("<td>")
str = str.concat(f_tr.toFixed(3))
str = str.concat("</td>")
str = str.concat("<td>")
str = str.concat(p_f_tr_str)
str = str.concat("</td>")
str = str.concat("</tr>")
str = str.concat("<tr>")
str = str.concat("<td>")
str = str.concat("Blocks")
str = str.concat("</td>")
str = str.concat("<td>")
str = str.concat(ssbl.toFixed(3))
str = str.concat("</td>")
str = str.concat("<td>")
str = str.concat(ssbl_percent.toFixed(3))
str = str.concat("</td>")
str = str.concat("<td>")
str = str.concat(n-1)
str = str.concat("</td>")
str = str.concat("<td>")
str = str.concat(msbl.toFixed(3))
str = str.concat("</td>")
str = str.concat("<td>")
str = str.concat(f_bl.toFixed(3))
str = str.concat("</td>")
str = str.concat("<td>")
str = str.concat(p_f_bl_str)
str = str.concat("</td>")
str = str.concat("</tr>")
str = str.concat("<tr>")
str = str.concat("<td>")
str = str.concat("Error")
str = str.concat("</td>")
str = str.concat("<td>")
str = str.concat(sse.toFixed(3))
str = str.concat("</td>")
str = str.concat("<td>")
str = str.concat(sse_percent.toFixed(3))
str = str.concat("</td>")
str = str.concat("<td>")
str = str.concat((n-1)*(k-1))
str = str.concat("</td>")
str = str.concat("<td>")
str = str.concat(mse.toFixed(3))
str = str.concat("</td>")
str = str.concat("<td>")
str = str.concat(" ")
str = str.concat("</td>")
str = str.concat("<td>")
str = str.concat(" ")
str = str.concat("</td>")
str = str.concat("</tr>")
str = str.concat("</tbody>")
str = str.concat("</table>")
for (ii = 0; ii < n; ii++) {
str = str.concat("<br>")
str = str.concat("Treatment " + (ii+1) + " has mean " + treat_means[ii].toFixed(3) + " and variance " + treat_vars[ii].toFixed(3))
}
var var_treat_means = calc_var(treat_means)
str = str.concat("<br>")
str = str.concat("Variance of treatment means is " + var_treat_means.toFixed(3))
str = str.concat("<br>")
for (ii = 0; ii < k; ii++) {
str = str.concat("<br>")
str = str.concat("Block " + (ii+1) + " has mean " + block_means[ii].toFixed(3) + " and variance " + block_vars[ii].toFixed(3))
}
str = str.concat("<br>")
var var_block_means = calc_var(block_means)
str = str.concat("Variance of block means is " + var_block_means.toFixed(3))
str = str.fontcolor("blue")
document.getElementById("p1").innerHTML = str;
}
function f_cdf(inp,p1,p2) {
var res;
res = betainc_num((p1*inp)/((p1*inp)+p2), p1/2, p2/2);
return res;
}
function betainc_num(x, a, b) {
var bt;
var g_ab;
var g_a;
var g_b;
if ((a + b) >= 171)
g_ab = land_lgamma_stirling(a+b);
else
g_ab = Math.log(land_ios_gamma(a+b));
if (a >= 171)
g_a = land_lgamma_stirling(a);
else
g_a = Math.log(land_ios_gamma(a));
if (b >= 171)
g_b = land_lgamma_stirling(b);
else
g_b = Math.log(land_ios_gamma(b));
bt = Math.exp(g_ab - g_a - g_b + a*Math.log(x)+b*Math.log(1.0-x));
if (x == 0)
bt = 0;
if (x < ((a + 1.0)/(a + b + 2.0)))
return bt*betacf(a,b,x)/a;
else
return 1 - (bt*betacf(b,a,1.0-x)/b);
}
function betacf(a, b, x)
{
var maxit = 100;
var eps = 3e-16;
var fpmin = 1e-30;
var aa;
var c;
var d;
var del;
var h;
var qab;
var qam;
var qap;
qab = a + b;
qap = a + 1;
qam = a - 1;
c = 1.0;
d = 1.0 - qab*x/qap;
if (Math.abs(d)<fpmin)
d = fpmin;
d = 1.0/d;
h = d;
var m2;
for (m = 1; m < maxit; m++)
{
m2 = 2*m;
aa = m*(b-m)*x/((qam + m2)*(a + m2));
d = 1.0 + aa*d;
if (Math.abs(d)<fpmin)
d = fpmin;
c = 1.0 + aa/c;
if (Math.abs(c)<fpmin)
c = fpmin;
d = 1.0/d;
h = h*d*c;
aa = -(a + m)*(qab + m)*x/((a+m2)*(qap+m2));
d = 1.0 + aa*d;
if (Math.abs(d)<fpmin)
d = fpmin;
c = 1.0 + aa/c;
if (Math.abs(c)<fpmin)
c = fpmin;
d = 1.0/d;
del = d*c;
h = h*del;
if (Math.abs(del-1.0)< eps)
{
// std::cout << "Breaking out at iter " << m << std::endl;
break;
}
}
// std::cout << " h is " << h << std::endl;
return h;
}
function land_ios_gamma(x)
{
var g = 7;
var y;
var t;
var res_fr;
var p = new Array()
p[0] = 0.99999999999980993;
p[1] = 676.5203681218851;
p[2] = -1259.1392167224028;
p[3] = 771.32342877765313;
p[4] = -176.61502916214059;
p[5] = 12.507343278686905;
p[6] = -0.13857109526572012;
p[7] = 9.9843695780195716e-6;
p[8] = 1.5056327351493116e-7;
if (Math.abs(x - Math.floor(x)) < 1e-16)
{
if ( x > 1)
return sFact(x - 1);
else if (x == 1)
return 1;
else
return 1/0.0;
}
else
{
x -= 1;
y = p[0];
for (i=1; i < g+2; i++)
{
y = y + p[i]/(x + i);
}
t = x + g + 0.5;
res_fr = Math.sqrt(2*Math.PI) * Math.exp(((x+0.5)*Math.log(t))-t)*y;
return res_fr;
}
}
function land_lgamma_stirling(x)
{
var t = 0.5*Math.log(2*Math.PI) - 0.5*Math.log(x) + x*(Math.log(x))-x;
var x2 = x * x;
var x3 = x2 * x;
var x4 = x3 * x;
var err_term = Math.log(1 + (1.0/(12*x)) + (1.0/(288*x2)) - (139.0/(51840*x3))
- (571.0/(2488320*x4)));
var res = t + err_term;
return res;
}
function sFact(num)
{
var rval=1;
for (var i = 2; i <= num; i++)
rval = rval * i;
return rval;
}
</script>Alijah Ahmedhttp://www.blogger.com/profile/01151589526613233918noreply@blogger.com189tag:blogger.com,1999:blog-8441309013858171051.post-21638009508222179582015-11-21T07:20:00.001-08:002024-01-21T05:11:44.089-08:00Cosine Similarity Calculator <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
<script language="Javascript">
/**
* jQuery-csv (jQuery Plugin)
*
* This document is licensed as free software under the terms of the
* MIT License: http://www.opensource.org/licenses/mit-license.php
*
* Acknowledgements:
* The original design and influence to implement this library as a jquery
* plugin is influenced by jquery-json (http://code.google.com/p/jquery-json/).
* If you're looking to use native JSON.Stringify but want additional backwards
* compatibility for browsers that don't support it, I highly recommend you
* check it out.
*
* A special thanks goes out to rwk@acm.org for providing a lot of valuable
* feedback to the project including the core for the new FSM
* (Finite State Machine) parsers. If you're looking for a stable TSV parser
* be sure to take a look at jquery-tsv (http://code.google.com/p/jquery-tsv/).
* For legal purposes I'll include the "NO WARRANTY EXPRESSED OR IMPLIED.
* USE AT YOUR OWN RISK.". Which, in 'layman's terms' means, by using this
* library you are accepting responsibility if it breaks your code.
*
* Legal jargon aside, I will do my best to provide a useful and stable core
* that can effectively be built on.
*
* Copyrighted 2012 by Evan Plaice.
*/
RegExp.escape= function(s) {
return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
};
(function (undefined) {
'use strict';
var $;
// to keep backwards compatibility
if (typeof jQuery !== 'undefined' && jQuery) {
$ = jQuery;
} else {
$ = {};
}
/**
* jQuery.csv.defaults
* Encapsulates the method paramater defaults for the CSV plugin module.
*/
$.csv = {
defaults: {
separator:',',
delimiter:'"',
headers:true
},
hooks: {
castToScalar: function(value, state) {
var hasDot = /\./;
if (isNaN(value)) {
return value;
} else {
if (hasDot.test(value)) {
return parseFloat(value);
} else {
var integer = parseInt(value);
if(isNaN(integer)) {
return null;
} else {
return integer;
}
}
}
}
},
parsers: {
parse: function(csv, options) {
// cache settings
var separator = options.separator;
var delimiter = options.delimiter;
// set initial state if it's missing
if(!options.state.rowNum) {
options.state.rowNum = 1;
}
if(!options.state.colNum) {
options.state.colNum = 1;
}
// clear initial state
var data = [];
var entry = [];
var state = 0;
var value = '';
var exit = false;
function endOfEntry() {
// reset the state
state = 0;
value = '';
// if 'start' hasn't been met, don't output
if(options.start && options.state.rowNum < options.start) {
// update global state
entry = [];
options.state.rowNum++;
options.state.colNum = 1;
return;
}
if(options.onParseEntry === undefined) {
// onParseEntry hook not set
data.push(entry);
} else {
var hookVal = options.onParseEntry(entry, options.state); // onParseEntry Hook
// false skips the row, configurable through a hook
if(hookVal !== false) {
data.push(hookVal);
}
}
//console.log('entry:' + entry);
// cleanup
entry = [];
// if 'end' is met, stop parsing
if(options.end && options.state.rowNum >= options.end) {
exit = true;
}
// update global state
options.state.rowNum++;
options.state.colNum = 1;
}
function endOfValue() {
if(options.onParseValue === undefined) {
// onParseValue hook not set
entry.push(value);
} else {
var hook = options.onParseValue(value, options.state); // onParseValue Hook
// false skips the row, configurable through a hook
if(hook !== false) {
entry.push(hook);
}
}
//console.log('value:' + value);
// reset the state
value = '';
state = 0;
// update global state
options.state.colNum++;
}
// escape regex-specific control chars
var escSeparator = RegExp.escape(separator);
var escDelimiter = RegExp.escape(delimiter);
// compile the regEx str using the custom delimiter/separator
var match = /(D|S|\r\n|\n|\r|[^DS\r\n]+)/;
var matchSrc = match.source;
matchSrc = matchSrc.replace(/S/g, escSeparator);
matchSrc = matchSrc.replace(/D/g, escDelimiter);
match = new RegExp(matchSrc, 'gm');
// put on your fancy pants...
// process control chars individually, use look-ahead on non-control chars
csv.replace(match, function (m0) {
if(exit) {
return;
}
switch (state) {
// the start of a value
case 0:
// null last value
if (m0 === separator) {
value += '';
endOfValue();
break;
}
// opening delimiter
if (m0 === delimiter) {
state = 1;
break;
}
// null last value
if (/^(\r\n|\n|\r)$/.test(m0)) {
endOfValue();
endOfEntry();
break;
}
// un-delimited value
value += m0;
state = 3;
break;
// delimited input
case 1:
// second delimiter? check further
if (m0 === delimiter) {
state = 2;
break;
}
// delimited data
value += m0;
state = 1;
break;
// delimiter found in delimited input
case 2:
// escaped delimiter?
if (m0 === delimiter) {
value += m0;
state = 1;
break;
}
// null value
if (m0 === separator) {
endOfValue();
break;
}
// end of entry
if (/^(\r\n|\n|\r)$/.test(m0)) {
endOfValue();
endOfEntry();
break;
}
// broken paser?
throw new Error('CSVDataError: Illegal State [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
// un-delimited input
case 3:
// null last value
if (m0 === separator) {
endOfValue();
break;
}
// end of entry
if (/^(\r\n|\n|\r)$/.test(m0)) {
endOfValue();
endOfEntry();
break;
}
if (m0 === delimiter) {
// non-compliant data
throw new Error('CSVDataError: Illegal Quote [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
}
// broken parser?
throw new Error('CSVDataError: Illegal Data [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
default:
// shenanigans
throw new Error('CSVDataError: Unknown State [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
}
//console.log('val:' + m0 + ' state:' + state);
});
// submit the last entry
// ignore null last line
if(entry.length !== 0) {
endOfValue();
endOfEntry();
}
return data;
},
// a csv-specific line splitter
splitLines: function(csv, options) {
// cache settings
var separator = options.separator;
var delimiter = options.delimiter;
// set initial state if it's missing
if(!options.state.rowNum) {
options.state.rowNum = 1;
}
// clear initial state
var entries = [];
var state = 0;
var entry = '';
var exit = false;
function endOfLine() {
// reset the state
state = 0;
// if 'start' hasn't been met, don't output
if(options.start && options.state.rowNum < options.start) {
// update global state
entry = '';
options.state.rowNum++;
return;
}
if(options.onParseEntry === undefined) {
// onParseEntry hook not set
entries.push(entry);
} else {
var hookVal = options.onParseEntry(entry, options.state); // onParseEntry Hook
// false skips the row, configurable through a hook
if(hookVal !== false) {
entries.push(hookVal);
}
}
// cleanup
entry = '';
// if 'end' is met, stop parsing
if(options.end && options.state.rowNum >= options.end) {
exit = true;
}
// update global state
options.state.rowNum++;
}
// escape regex-specific control chars
var escSeparator = RegExp.escape(separator);
var escDelimiter = RegExp.escape(delimiter);
// compile the regEx str using the custom delimiter/separator
var match = /(D|S|\n|\r|[^DS\r\n]+)/;
var matchSrc = match.source;
matchSrc = matchSrc.replace(/S/g, escSeparator);
matchSrc = matchSrc.replace(/D/g, escDelimiter);
match = new RegExp(matchSrc, 'gm');
// put on your fancy pants...
// process control chars individually, use look-ahead on non-control chars
csv.replace(match, function (m0) {
if(exit) {
return;
}
switch (state) {
// the start of a value/entry
case 0:
// null value
if (m0 === separator) {
entry += m0;
state = 0;
break;
}
// opening delimiter
if (m0 === delimiter) {
entry += m0;
state = 1;
break;
}
// end of line
if (m0 === '\n') {
endOfLine();
break;
}
// phantom carriage return
if (/^\r$/.test(m0)) {
break;
}
// un-delimit value
entry += m0;
state = 3;
break;
// delimited input
case 1:
// second delimiter? check further
if (m0 === delimiter) {
entry += m0;
state = 2;
break;
}
// delimited data
entry += m0;
state = 1;
break;
// delimiter found in delimited input
case 2:
// escaped delimiter?
var prevChar = entry.substr(entry.length - 1);
if (m0 === delimiter && prevChar === delimiter) {
entry += m0;
state = 1;
break;
}
// end of value
if (m0 === separator) {
entry += m0;
state = 0;
break;
}
// end of line
if (m0 === '\n') {
endOfLine();
break;
}
// phantom carriage return
if (m0 === '\r') {
break;
}
// broken paser?
throw new Error('CSVDataError: Illegal state [Row:' + options.state.rowNum + ']');
// un-delimited input
case 3:
// null value
if (m0 === separator) {
entry += m0;
state = 0;
break;
}
// end of line
if (m0 === '\n') {
endOfLine();
break;
}
// phantom carriage return
if (m0 === '\r') {
break;
}
// non-compliant data
if (m0 === delimiter) {
throw new Error('CSVDataError: Illegal quote [Row:' + options.state.rowNum + ']');
}
// broken parser?
throw new Error('CSVDataError: Illegal state [Row:' + options.state.rowNum + ']');
default:
// shenanigans
throw new Error('CSVDataError: Unknown state [Row:' + options.state.rowNum + ']');
}
//console.log('val:' + m0 + ' state:' + state);
});
// submit the last entry
// ignore null last line
if(entry !== '') {
endOfLine();
}
return entries;
},
// a csv entry parser
parseEntry: function(csv, options) {
// cache settings
var separator = options.separator;
var delimiter = options.delimiter;
// set initial state if it's missing
if(!options.state.rowNum) {
options.state.rowNum = 1;
}
if(!options.state.colNum) {
options.state.colNum = 1;
}
// clear initial state
var entry = [];
var state = 0;
var value = '';
function endOfValue() {
if(options.onParseValue === undefined) {
// onParseValue hook not set
entry.push(value);
} else {
var hook = options.onParseValue(value, options.state); // onParseValue Hook
// false skips the value, configurable through a hook
if(hook !== false) {
entry.push(hook);
}
}
// reset the state
value = '';
state = 0;
// update global state
options.state.colNum++;
}
// checked for a cached regEx first
if(!options.match) {
// escape regex-specific control chars
var escSeparator = RegExp.escape(separator);
var escDelimiter = RegExp.escape(delimiter);
// compile the regEx str using the custom delimiter/separator
var match = /(D|S|\n|\r|[^DS\r\n]+)/;
var matchSrc = match.source;
matchSrc = matchSrc.replace(/S/g, escSeparator);
matchSrc = matchSrc.replace(/D/g, escDelimiter);
options.match = new RegExp(matchSrc, 'gm');
}
// put on your fancy pants...
// process control chars individually, use look-ahead on non-control chars
csv.replace(options.match, function (m0) {
switch (state) {
// the start of a value
case 0:
// null last value
if (m0 === separator) {
value += '';
endOfValue();
break;
}
// opening delimiter
if (m0 === delimiter) {
state = 1;
break;
}
// skip un-delimited new-lines
if (m0 === '\n' || m0 === '\r') {
break;
}
// un-delimited value
value += m0;
state = 3;
break;
// delimited input
case 1:
// second delimiter? check further
if (m0 === delimiter) {
state = 2;
break;
}
// delimited data
value += m0;
state = 1;
break;
// delimiter found in delimited input
case 2:
// escaped delimiter?
if (m0 === delimiter) {
value += m0;
state = 1;
break;
}
// null value
if (m0 === separator) {
endOfValue();
break;
}
// skip un-delimited new-lines
if (m0 === '\n' || m0 === '\r') {
break;
}
// broken paser?
throw new Error('CSVDataError: Illegal State [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
// un-delimited input
case 3:
// null last value
if (m0 === separator) {
endOfValue();
break;
}
// skip un-delimited new-lines
if (m0 === '\n' || m0 === '\r') {
break;
}
// non-compliant data
if (m0 === delimiter) {
throw new Error('CSVDataError: Illegal Quote [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
}
// broken parser?
throw new Error('CSVDataError: Illegal Data [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
default:
// shenanigans
throw new Error('CSVDataError: Unknown State [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
}
//console.log('val:' + m0 + ' state:' + state);
});
// submit the last value
endOfValue();
return entry;
}
},
helpers: {
/**
* $.csv.helpers.collectPropertyNames(objectsArray)
* Collects all unique property names from all passed objects.
*
* @param {Array} objects Objects to collect properties from.
*
* Returns an array of property names (array will be empty,
* if objects have no own properties).
*/
collectPropertyNames: function (objects) {
var o, propName, props = [];
for (o in objects) {
for (propName in objects[o]) {
if ((objects[o].hasOwnProperty(propName)) &&
(props.indexOf(propName) < 0) &&
(typeof objects[o][propName] !== 'function')) {
props.push(propName);
}
}
}
return props;
}
},
/**
* $.csv.toArray(csv)
* Converts a CSV entry string to a javascript array.
*
* @param {Array} csv The string containing the CSV data.
* @param {Object} [options] An object containing user-defined options.
* @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
* @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
*
* This method deals with simple CSV strings only. It's useful if you only
* need to parse a single entry. If you need to parse more than one line,
* use $.csv2Array instead.
*/
toArray: function(csv, options, callback) {
options = (options !== undefined ? options : {});
var config = {};
config.callback = ((callback !== undefined && typeof(callback) === 'function') ? callback : false);
config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator;
config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter;
var state = (options.state !== undefined ? options.state : {});
// setup
options = {
delimiter: config.delimiter,
separator: config.separator,
onParseEntry: options.onParseEntry,
onParseValue: options.onParseValue,
state: state
};
var entry = $.csv.parsers.parseEntry(csv, options);
// push the value to a callback if one is defined
if(!config.callback) {
return entry;
} else {
config.callback('', entry);
}
},
/**
* $.csv.toArrays(csv)
* Converts a CSV string to a javascript array.
*
* @param {String} csv The string containing the raw CSV data.
* @param {Object} [options] An object containing user-defined options.
* @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
* @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
*
* This method deals with multi-line CSV. The breakdown is simple. The first
* dimension of the array represents the line (or entry/row) while the second
* dimension contains the values (or values/columns).
*/
toArrays: function(csv, options, callback) {
options = (options !== undefined ? options : {});
var config = {};
config.callback = ((callback !== undefined && typeof(callback) === 'function') ? callback : false);
config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator;
config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter;
// setup
var data = [];
options = {
delimiter: config.delimiter,
separator: config.separator,
onPreParse: options.onPreParse,
onParseEntry: options.onParseEntry,
onParseValue: options.onParseValue,
onPostParse: options.onPostParse,
start: options.start,
end: options.end,
state: {
rowNum: 1,
colNum: 1
}
};
// onPreParse hook
if(options.onPreParse !== undefined) {
options.onPreParse(csv, options.state);
}
// parse the data
data = $.csv.parsers.parse(csv, options);
// onPostParse hook
if(options.onPostParse !== undefined) {
options.onPostParse(data, options.state);
}
// push the value to a callback if one is defined
if(!config.callback) {
return data;
} else {
config.callback('', data);
}
},
/**
* $.csv.toObjects(csv)
* Converts a CSV string to a javascript object.
* @param {String} csv The string containing the raw CSV data.
* @param {Object} [options] An object containing user-defined options.
* @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
* @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
* @param {Boolean} [headers] Indicates whether the data contains a header line. Defaults to true.
*
* This method deals with multi-line CSV strings. Where the headers line is
* used as the key for each value per entry.
*/
toObjects: function(csv, options, callback) {
options = (options !== undefined ? options : {});
var config = {};
config.callback = ((callback !== undefined && typeof(callback) === 'function') ? callback : false);
config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator;
config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter;
config.headers = 'headers' in options ? options.headers : $.csv.defaults.headers;
options.start = 'start' in options ? options.start : 1;
// account for headers
if(config.headers) {
options.start++;
}
if(options.end && config.headers) {
options.end++;
}
// setup
var lines = [];
var data = [];
options = {
delimiter: config.delimiter,
separator: config.separator,
onPreParse: options.onPreParse,
onParseEntry: options.onParseEntry,
onParseValue: options.onParseValue,
onPostParse: options.onPostParse,
start: options.start,
end: options.end,
state: {
rowNum: 1,
colNum: 1
},
match: false,
transform: options.transform
};
// fetch the headers
var headerOptions = {
delimiter: config.delimiter,
separator: config.separator,
start: 1,
end: 1,
state: {
rowNum:1,
colNum:1
}
};
// onPreParse hook
if(options.onPreParse !== undefined) {
options.onPreParse(csv, options.state);
}
// parse the csv
var headerLine = $.csv.parsers.splitLines(csv, headerOptions);
var headers = $.csv.toArray(headerLine[0], options);
// fetch the data
lines = $.csv.parsers.splitLines(csv, options);
// reset the state for re-use
options.state.colNum = 1;
if(headers){
options.state.rowNum = 2;
} else {
options.state.rowNum = 1;
}
// convert data to objects
for(var i=0, len=lines.length; i<len; i++) {
var entry = $.csv.toArray(lines[i], options);
var object = {};
for(var j=0; j <headers.length; j++) {
object[headers[j]] = entry[j];
}
if (options.transform !== undefined) {
data.push(options.transform.call(undefined, object));
} else {
data.push(object);
}
// update row state
options.state.rowNum++;
}
// onPostParse hook
if(options.onPostParse !== undefined) {
options.onPostParse(data, options.state);
}
// push the value to a callback if one is defined
if(!config.callback) {
return data;
} else {
config.callback('', data);
}
},
/**
* $.csv.fromArrays(arrays)
* Converts a javascript array to a CSV String.
*
* @param {Array} arrays An array containing an array of CSV entries.
* @param {Object} [options] An object containing user-defined options.
* @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
* @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
*
* This method generates a CSV file from an array of arrays (representing entries).
*/
fromArrays: function(arrays, options, callback) {
options = (options !== undefined ? options : {});
var config = {};
config.callback = ((callback !== undefined && typeof(callback) === 'function') ? callback : false);
config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator;
config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter;
var output = '',
line,
lineValues,
i, j;
for (i = 0; i < arrays.length; i++) {
line = arrays[i];
lineValues = [];
for (j = 0; j < line.length; j++) {
var strValue = (line[j] === undefined || line[j] === null) ? '' : line[j].toString();
if (strValue.indexOf(config.delimiter) > -1) {
strValue = strValue.replace(config.delimiter, config.delimiter + config.delimiter);
}
var escMatcher = '\n|\r|S|D';
escMatcher = escMatcher.replace('S', config.separator);
escMatcher = escMatcher.replace('D', config.delimiter);
if (strValue.search(escMatcher) > -1) {
strValue = config.delimiter + strValue + config.delimiter;
}
lineValues.push(strValue);
}
output += lineValues.join(config.separator) + '\r\n';
}
// push the value to a callback if one is defined
if(!config.callback) {
return output;
} else {
config.callback('', output);
}
},
/**
* $.csv.fromObjects(objects)
* Converts a javascript dictionary to a CSV string.
*
* @param {Object} objects An array of objects containing the data.
* @param {Object} [options] An object containing user-defined options.
* @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
* @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
* @param {Character} [sortOrder] Sort order of columns (named after
* object properties). Use 'alpha' for alphabetic. Default is 'declare',
* which means, that properties will _probably_ appear in order they were
* declared for the object. But without any guarantee.
* @param {Character or Array} [manualOrder] Manually order columns. May be
* a strin in a same csv format as an output or an array of header names
* (array items won't be parsed). All the properties, not present in
* `manualOrder` will be appended to the end in accordance with `sortOrder`
* option. So the `manualOrder` always takes preference, if present.
*
* This method generates a CSV file from an array of objects (name:value pairs).
* It starts by detecting the headers and adding them as the first line of
* the CSV file, followed by a structured dump of the data.
*/
fromObjects: function(objects, options, callback) {
options = (options !== undefined ? options : {});
var config = {};
config.callback = ((callback !== undefined && typeof(callback) === 'function') ? callback : false);
config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator;
config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter;
config.headers = 'headers' in options ? options.headers : $.csv.defaults.headers;
config.sortOrder = 'sortOrder' in options ? options.sortOrder : 'declare';
config.manualOrder = 'manualOrder' in options ? options.manualOrder : [];
config.transform = options.transform;
if (typeof config.manualOrder === 'string') {
config.manualOrder = $.csv.toArray(config.manualOrder, config);
}
if (config.transform !== undefined) {
var origObjects = objects;
objects = [];
var i;
for (i = 0; i < origObjects.length; i++) {
objects.push(config.transform.call(undefined, origObjects[i]));
}
}
var props = $.csv.helpers.collectPropertyNames(objects);
if (config.sortOrder === 'alpha') {
props.sort();
} // else {} - nothing to do for 'declare' order
if (config.manualOrder.length > 0) {
var propsManual = [].concat(config.manualOrder);
var p;
for (p = 0; p < props.length; p++) {
if (propsManual.indexOf( props[p] ) < 0) {
propsManual.push( props[p] );
}
}
props = propsManual;
}
var o, p, line, output = [], propName;
if (config.headers) {
output.push(props);
}
for (o = 0; o < objects.length; o++) {
line = [];
for (p = 0; p < props.length; p++) {
propName = props[p];
if (propName in objects[o] && typeof objects[o][propName] !== 'function') {
line.push(objects[o][propName]);
} else {
line.push('');
}
}
output.push(line);
}
// push the value to a callback if one is defined
return $.csv.fromArrays(output, options, config.callback);
}
};
// Maintenance code to maintain backward-compatibility
// Will be removed in release 1.0
$.csvEntry2Array = $.csv.toArray;
$.csv2Array = $.csv.toArrays;
$.csv2Dictionary = $.csv.toObjects;
// CommonJS module is defined
if (typeof module !== 'undefined' && module.exports) {
module.exports = $.csv;
}
}).call( this );
</script>
<script>
$(document).ready(function() {
if(isAPIAvailable()) {
$('#files').bind('change', handleFileSelect);
}
});
function isAPIAvailable() {
// Check for the various File API support.
if (window.File && window.FileReader && window.FileList && window.Blob) {
// Great success! All the File APIs are supported.
return true;
} else {
// source: File API availability - http://caniuse.com/#feat=fileapi
// source: <output> availability - http://html5doctor.com/the-output-element/
document.writeln('The HTML5 APIs used in this form are only available in the following browsers:<br />');
// 6.0 File API & 13.0 <output>
document.writeln(' - Google Chrome: 13.0 or later<br />');
// 3.6 File API & 6.0 <output>
document.writeln(' - Mozilla Firefox: 6.0 or later<br />');
// 10.0 File API & 10.0 <output>
document.writeln(' - Internet Explorer: Not supported (partial support expected in 10.0)<br />');
// ? File API & 5.1 <output>
document.writeln(' - Safari: Not supported<br />');
// ? File API & 9.2 <output>
document.writeln(' - Opera: Not supported');
return false;
}
}
function handleFileSelect(evt) {
var files = evt.target.files; // FileList object
var file = files[0];
// read the file metadata
var output = ''
output += '<span style="font-weight:bold;">' + escape(file.name) + '</span><br />\n';
output += ' - FileType: ' + (file.type || 'n/a') + '<br />\n';
output += ' - FileSize: ' + file.size + ' bytes<br />\n';
output += ' - LastModified: ' + (file.lastModifiedDate ? file.lastModifiedDate.toLocaleDateString() : 'n/a') + '<br />\n';
// read the file contents
printTextArea(file);
// post the results
$('#list').append(output);
}
function printTextArea(file) {
var container = document.getElementById("area");
var inps = container.getElementsByTagName("input");
var nvec = inps.length;
var idx_selected = 0;
for (k=0; k < nvec; k++)
{
if (inps.item(k).value == "")
{
idx_selected = k;
break;
}
}
var reader = new FileReader();
reader.readAsText(file);
reader.onload = function(event){
var csv = event.target.result;
var data = $.csv.toArrays(csv);
var multi_col = 0;
var mystr = new Array();
for (k=0;k<nvec;k++)
mystr[k] = '';
for(var row in data) {
if (!isNaN(Number(data[row][0])))
{
if (data[row].length > 1)
{
multi_col = 1;
for (k=0; k < nvec; k++)
{
mystr[k] = (mystr[k]).concat((Number(data[row][k]).toFixed(6)).toString());
mystr[k] = (mystr[k]).concat(",");
}
}
else
{
mystr[0] = (mystr[0]).concat((Number(data[row][0]).toFixed(6)).toString());
mystr[0] = (mystr[0]).concat(",");
}
}
}
if (multi_col == 0)
{
mystr[0] = (mystr[0]).substring(0, mystr[0].length - 1)
inps.item(idx_selected).value = mystr[0];
}
else
{
for (k=0; k < nvec; k++)
{
mystr[k] = (mystr[k]).substring(0, mystr[k].length - 1)
inps.item(k).value = mystr[k];
}
}
};
reader.onerror = function(){ alert('Unable to read ' + file.fileName); };
}
</script>
<!--[if IE ]>
<script type="text/javascript" src="https://firebasestorage.googleapis.com/v0/b/js-static-assets.appspot.com/o/excanvas_min.js?alt=media&token=9dd78ad2-f09b-45dd-8a09-8db2b2546d1d"></script>
<![endif]-->
<script type="text/javascript" src="https://firebasestorage.googleapis.com/v0/b/js-static-assets.appspot.com/o/jquery_flot.js?alt=media&token=bde69a50-3df9-4794-b7d8-dd4e840472cd
"></script>
</head>
<body>
<!-- Just call function 'addTextBox()' to add textbox -->
<a onclick='addTextBox()' href='#'>Please click to add a row.</a>
<br/>
<br/>
<!-- Textbox will be added in followng DIV -->
<div id='area'></div>
<div id=inputs class=clearfix>
<input type=file id=files name=files[] multiple />
</div>
<p style="text-align:justify"> This blog post calculates the pairwise Cosine similarity for a user-specifiable number of vectors. All vectors must comprise the same number of elements.</p>
<p style="text-align:justify"> Simply click on the link near the top to add text boxes. Each text box stores a single vector and needs to be filled in with comma separated numbers. All rows need to have the same number of samples.</p>
<p style="text-align:justify">Alternatively, you can choose two file entry methods:-
<ol>
<li>Select multiple single column CSV files to populate the text boxes by repeatedly pressing the Choose File button - there must be one distinct (and differently named) file for each text box i.e. one file per group. Each file can have a different number of samples.</li>
<li>Select a single multi-column CSV file by pressing the Choose File button once, where the number of columns equals the number of vectors.</li>
</ol>
</p>
<input name="b1" onclick="calculate_cosine_similarity()" style="margin-left: 0px;" type="Button" value="Calculate.." />
<p id="pdbg">Results pending...</p>
<input name="b1" onclick="clearReset()" style="margin-left: 0px;" type="Button" value="Reset Results log" />
<br>
<br>
<!-- Post hoc Textbox will be added in followng DIV -->
<div id='areaph'></div>
</body>
</html>
<script language="javascript">
var inival=0; // Initialise starting element number
var phval = 0;
function calculate_cosine_similarity() {
phval = 0;
var dbg_str = '';
var tmp_str = '';
var dataSet;
var datavec = new Array();
var str = inival.toString();
var container = document.getElementById("area");
var inps = container.getElementsByTagName("input");
var nvec = inps.length;
tmp_str = "Number of vectors is " + nvec + "<br>";
// dbg_str = dbg_str.concat(tmp_str);
var dataSet;
var datavec = new Array();
var nc = 0;
var first_len;
var all_same_len = 1;
for (k=0; k < nvec; k++)
{
if (inps.item(k).value.split(',').length > nc)
nc = inps.item(k).value.split(',').length;
if (k == 0)
first_len = inps.item(k).value.split(',').length;
if (inps.item(k).value.split(',').length != first_len)
all_same_len = 0;
}
if (all_same_len == 0)
{
alert("All rows must have same number of samples.");
return;
}
var dmat = [];
var numel = first_len;
// dbg_str = dbg_str.concat("Number of elements is " + numel + " <br>");
// Initialise the vector storage matrix
for (var i=0; i < nvec; i++)
{
dmat[i] = [];
for(var j=0; j< numel; j++) {
dmat[i][j] = 0;
}
}
for (var k=0; k < nvec; k++)
{
dataSet = inps.item(k).value;
datavec = dataSet.split(',');
tmp_str = "";
for (var m=0; m < datavec.length; m++)
{
dmat[k][m] = Number(datavec[m]);
tmp_str = tmp_str.concat(dmat[k][m].toFixed(6)+" ");
}
tmp_str = tmp_str.concat("<br>");
// dbg_str = dbg_str.concat(tmp_str);
}
// calculate the correlations
for (var k=0; k < nvec - 1; k++)
{
for (var m= k+1; m < nvec; m++)
{
var v1 = new Array();
var v2 = new Array();
for (p=0; p < numel; p++)
{
v1[p] = dmat[k][p];
v2[p] = dmat[m][p];
}
var res = calc_correl(v1,v2);
var phi = 0.0;
if (Math.abs(res - 1)<1e-16)
{
phi = 0;
}
else
{
phi = Math.acos(res);
}
var vv1 = k+1;
var vv2 = m+1;
dbg_str = dbg_str.concat("Cosine Similarity between vectors " + vv1 + " and " + vv2);
dbg_str = dbg_str.concat(" is " + res.toFixed(6) + ", cosine of " + phi.toFixed(6) + " radians <br>");
}
}
dbg_str = dbg_str.fontcolor("white");
document.getElementById("pdbg").innerHTML=dbg_str;
}
function calc_correl(a,b)
{
var Sab = 0;
var Saa = 0;
var Sbb = 0;
for (var i=0; i < a.length; i++)
{
Sab = Sab + (a[i] * b[i]);
Saa = Saa + (a[i] * a[i]);
Sbb = Sbb + (b[i] * b[i]);
}
var correl = Sab/(Math.sqrt(Saa) * Math.sqrt(Sbb));
return correl;
}
// Call this function to add textbox
function addTextBox()
{
phval = 0;
var newArea = add_New_Element();
var str = inival.toString();
var htcontents = "Row ";
htcontents = htcontents.concat(inival.toString());
htcontents = htcontents.concat(": <input type='textArea' style='width:300px;' name='textbx[]' />");
htcontents = htcontents.concat("<br><br>");
document.getElementById(newArea).innerHTML = htcontents; // You can any other elements in place of 'htcontents'
}
function add_New_Element() {
inival=inival+1; // Increment element number by 1
var ni = document.getElementById('area');
var newdiv = document.createElement('div'); // Create dynamic element
var divIdName = 'my'+inival+'Div';
newdiv.setAttribute('id',divIdName);
ni.appendChild(newdiv);
return divIdName;
}
// Post-hoc text boxes
// Call this function to add secondary textbox
function addTextBox2()
{
var newArea2 = add_New_Element2();
var str = phval.toString();
var htcontents = "Row ";
htcontents = htcontents.concat(phval.toString());
htcontents = htcontents.concat(": <input type='textArea' style='width:300px;' />");
htcontents = htcontents.concat("<br><br>");
document.getElementById(newArea2).innerHTML = htcontents; // You can any other elements in place of 'htcontents'
}
function add_New_Element2() {
phval=phval+1; // Increment element number by 1
var ni = document.getElementById('areaph');
var newdiv = document.createElement('div'); // Create dynamic element
var divIdName = 'moi'+phval+'Div';
newdiv.setAttribute('id',divIdName);
ni.appendChild(newdiv);
return divIdName;
}
function clearReset() {
document.getElementById("pdbg").innerHTML="Results pending..";
}
function compare(a,b) {
if (a[0] < b[0])
return -1;
if (a[0] > b[0])
return 1;
return
}
</script>Alijah Ahmedhttp://www.blogger.com/profile/01151589526613233918noreply@blogger.comtag:blogger.com,1999:blog-8441309013858171051.post-65747212415522363512014-01-24T05:36:00.007-08:002020-10-12T06:23:54.469-07:00k-means clustering calculator <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<script language="Javascript">
/**
* jQuery-csv (jQuery Plugin)
*
* This document is licensed as free software under the terms of the
* MIT License: http://www.opensource.org/licenses/mit-license.php
*
* Acknowledgements:
* The original design and influence to implement this library as a jquery
* plugin is influenced by jquery-json (http://code.google.com/p/jquery-json/).
* If you're looking to use native JSON.Stringify but want additional backwards
* compatibility for browsers that don't support it, I highly recommend you
* check it out.
*
* A special thanks goes out to rwk@acm.org for providing a lot of valuable
* feedback to the project including the core for the new FSM
* (Finite State Machine) parsers. If you're looking for a stable TSV parser
* be sure to take a look at jquery-tsv (http://code.google.com/p/jquery-tsv/).
* For legal purposes I'll include the "NO WARRANTY EXPRESSED OR IMPLIED.
* USE AT YOUR OWN RISK.". Which, in 'layman's terms' means, by using this
* library you are accepting responsibility if it breaks your code.
*
* Legal jargon aside, I will do my best to provide a useful and stable core
* that can effectively be built on.
*
* Copyrighted 2012 by Evan Plaice.
*/
RegExp.escape= function(s) {
return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
};
(function (undefined) {
'use strict';
var $;
// to keep backwards compatibility
if (typeof jQuery !== 'undefined' && jQuery) {
$ = jQuery;
} else {
$ = {};
}
/**
* jQuery.csv.defaults
* Encapsulates the method paramater defaults for the CSV plugin module.
*/
$.csv = {
defaults: {
separator:',',
delimiter:'"',
headers:true
},
hooks: {
castToScalar: function(value, state) {
var hasDot = /\./;
if (isNaN(value)) {
return value;
} else {
if (hasDot.test(value)) {
return parseFloat(value);
} else {
var integer = parseInt(value);
if(isNaN(integer)) {
return null;
} else {
return integer;
}
}
}
}
},
parsers: {
parse: function(csv, options) {
// cache settings
var separator = options.separator;
var delimiter = options.delimiter;
// set initial state if it's missing
if(!options.state.rowNum) {
options.state.rowNum = 1;
}
if(!options.state.colNum) {
options.state.colNum = 1;
}
// clear initial state
var data = [];
var entry = [];
var state = 0;
var value = '';
var exit = false;
function endOfEntry() {
// reset the state
state = 0;
value = '';
// if 'start' hasn't been met, don't output
if(options.start && options.state.rowNum < options.start) {
// update global state
entry = [];
options.state.rowNum++;
options.state.colNum = 1;
return;
}
if(options.onParseEntry === undefined) {
// onParseEntry hook not set
data.push(entry);
} else {
var hookVal = options.onParseEntry(entry, options.state); // onParseEntry Hook
// false skips the row, configurable through a hook
if(hookVal !== false) {
data.push(hookVal);
}
}
//console.log('entry:' + entry);
// cleanup
entry = [];
// if 'end' is met, stop parsing
if(options.end && options.state.rowNum >= options.end) {
exit = true;
}
// update global state
options.state.rowNum++;
options.state.colNum = 1;
}
function endOfValue() {
if(options.onParseValue === undefined) {
// onParseValue hook not set
entry.push(value);
} else {
var hook = options.onParseValue(value, options.state); // onParseValue Hook
// false skips the row, configurable through a hook
if(hook !== false) {
entry.push(hook);
}
}
//console.log('value:' + value);
// reset the state
value = '';
state = 0;
// update global state
options.state.colNum++;
}
// escape regex-specific control chars
var escSeparator = RegExp.escape(separator);
var escDelimiter = RegExp.escape(delimiter);
// compile the regEx str using the custom delimiter/separator
var match = /(D|S|\r\n|\n|\r|[^DS\r\n]+)/;
var matchSrc = match.source;
matchSrc = matchSrc.replace(/S/g, escSeparator);
matchSrc = matchSrc.replace(/D/g, escDelimiter);
match = new RegExp(matchSrc, 'gm');
// put on your fancy pants...
// process control chars individually, use look-ahead on non-control chars
csv.replace(match, function (m0) {
if(exit) {
return;
}
switch (state) {
// the start of a value
case 0:
// null last value
if (m0 === separator) {
value += '';
endOfValue();
break;
}
// opening delimiter
if (m0 === delimiter) {
state = 1;
break;
}
// null last value
if (/^(\r\n|\n|\r)$/.test(m0)) {
endOfValue();
endOfEntry();
break;
}
// un-delimited value
value += m0;
state = 3;
break;
// delimited input
case 1:
// second delimiter? check further
if (m0 === delimiter) {
state = 2;
break;
}
// delimited data
value += m0;
state = 1;
break;
// delimiter found in delimited input
case 2:
// escaped delimiter?
if (m0 === delimiter) {
value += m0;
state = 1;
break;
}
// null value
if (m0 === separator) {
endOfValue();
break;
}
// end of entry
if (/^(\r\n|\n|\r)$/.test(m0)) {
endOfValue();
endOfEntry();
break;
}
// broken paser?
throw new Error('CSVDataError: Illegal State [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
// un-delimited input
case 3:
// null last value
if (m0 === separator) {
endOfValue();
break;
}
// end of entry
if (/^(\r\n|\n|\r)$/.test(m0)) {
endOfValue();
endOfEntry();
break;
}
if (m0 === delimiter) {
// non-compliant data
throw new Error('CSVDataError: Illegal Quote [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
}
// broken parser?
throw new Error('CSVDataError: Illegal Data [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
default:
// shenanigans
throw new Error('CSVDataError: Unknown State [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
}
//console.log('val:' + m0 + ' state:' + state);
});
// submit the last entry
// ignore null last line
if(entry.length !== 0) {
endOfValue();
endOfEntry();
}
return data;
},
// a csv-specific line splitter
splitLines: function(csv, options) {
// cache settings
var separator = options.separator;
var delimiter = options.delimiter;
// set initial state if it's missing
if(!options.state.rowNum) {
options.state.rowNum = 1;
}
// clear initial state
var entries = [];
var state = 0;
var entry = '';
var exit = false;
function endOfLine() {
// reset the state
state = 0;
// if 'start' hasn't been met, don't output
if(options.start && options.state.rowNum < options.start) {
// update global state
entry = '';
options.state.rowNum++;
return;
}
if(options.onParseEntry === undefined) {
// onParseEntry hook not set
entries.push(entry);
} else {
var hookVal = options.onParseEntry(entry, options.state); // onParseEntry Hook
// false skips the row, configurable through a hook
if(hookVal !== false) {
entries.push(hookVal);
}
}
// cleanup
entry = '';
// if 'end' is met, stop parsing
if(options.end && options.state.rowNum >= options.end) {
exit = true;
}
// update global state
options.state.rowNum++;
}
// escape regex-specific control chars
var escSeparator = RegExp.escape(separator);
var escDelimiter = RegExp.escape(delimiter);
// compile the regEx str using the custom delimiter/separator
var match = /(D|S|\n|\r|[^DS\r\n]+)/;
var matchSrc = match.source;
matchSrc = matchSrc.replace(/S/g, escSeparator);
matchSrc = matchSrc.replace(/D/g, escDelimiter);
match = new RegExp(matchSrc, 'gm');
// put on your fancy pants...
// process control chars individually, use look-ahead on non-control chars
csv.replace(match, function (m0) {
if(exit) {
return;
}
switch (state) {
// the start of a value/entry
case 0:
// null value
if (m0 === separator) {
entry += m0;
state = 0;
break;
}
// opening delimiter
if (m0 === delimiter) {
entry += m0;
state = 1;
break;
}
// end of line
if (m0 === '\n') {
endOfLine();
break;
}
// phantom carriage return
if (/^\r$/.test(m0)) {
break;
}
// un-delimit value
entry += m0;
state = 3;
break;
// delimited input
case 1:
// second delimiter? check further
if (m0 === delimiter) {
entry += m0;
state = 2;
break;
}
// delimited data
entry += m0;
state = 1;
break;
// delimiter found in delimited input
case 2:
// escaped delimiter?
var prevChar = entry.substr(entry.length - 1);
if (m0 === delimiter && prevChar === delimiter) {
entry += m0;
state = 1;
break;
}
// end of value
if (m0 === separator) {
entry += m0;
state = 0;
break;
}
// end of line
if (m0 === '\n') {
endOfLine();
break;
}
// phantom carriage return
if (m0 === '\r') {
break;
}
// broken paser?
throw new Error('CSVDataError: Illegal state [Row:' + options.state.rowNum + ']');
// un-delimited input
case 3:
// null value
if (m0 === separator) {
entry += m0;
state = 0;
break;
}
// end of line
if (m0 === '\n') {
endOfLine();
break;
}
// phantom carriage return
if (m0 === '\r') {
break;
}
// non-compliant data
if (m0 === delimiter) {
throw new Error('CSVDataError: Illegal quote [Row:' + options.state.rowNum + ']');
}
// broken parser?
throw new Error('CSVDataError: Illegal state [Row:' + options.state.rowNum + ']');
default:
// shenanigans
throw new Error('CSVDataError: Unknown state [Row:' + options.state.rowNum + ']');
}
//console.log('val:' + m0 + ' state:' + state);
});
// submit the last entry
// ignore null last line
if(entry !== '') {
endOfLine();
}
return entries;
},
// a csv entry parser
parseEntry: function(csv, options) {
// cache settings
var separator = options.separator;
var delimiter = options.delimiter;
// set initial state if it's missing
if(!options.state.rowNum) {
options.state.rowNum = 1;
}
if(!options.state.colNum) {
options.state.colNum = 1;
}
// clear initial state
var entry = [];
var state = 0;
var value = '';
function endOfValue() {
if(options.onParseValue === undefined) {
// onParseValue hook not set
entry.push(value);
} else {
var hook = options.onParseValue(value, options.state); // onParseValue Hook
// false skips the value, configurable through a hook
if(hook !== false) {
entry.push(hook);
}
}
// reset the state
value = '';
state = 0;
// update global state
options.state.colNum++;
}
// checked for a cached regEx first
if(!options.match) {
// escape regex-specific control chars
var escSeparator = RegExp.escape(separator);
var escDelimiter = RegExp.escape(delimiter);
// compile the regEx str using the custom delimiter/separator
var match = /(D|S|\n|\r|[^DS\r\n]+)/;
var matchSrc = match.source;
matchSrc = matchSrc.replace(/S/g, escSeparator);
matchSrc = matchSrc.replace(/D/g, escDelimiter);
options.match = new RegExp(matchSrc, 'gm');
}
// put on your fancy pants...
// process control chars individually, use look-ahead on non-control chars
csv.replace(options.match, function (m0) {
switch (state) {
// the start of a value
case 0:
// null last value
if (m0 === separator) {
value += '';
endOfValue();
break;
}
// opening delimiter
if (m0 === delimiter) {
state = 1;
break;
}
// skip un-delimited new-lines
if (m0 === '\n' || m0 === '\r') {
break;
}
// un-delimited value
value += m0;
state = 3;
break;
// delimited input
case 1:
// second delimiter? check further
if (m0 === delimiter) {
state = 2;
break;
}
// delimited data
value += m0;
state = 1;
break;
// delimiter found in delimited input
case 2:
// escaped delimiter?
if (m0 === delimiter) {
value += m0;
state = 1;
break;
}
// null value
if (m0 === separator) {
endOfValue();
break;
}
// skip un-delimited new-lines
if (m0 === '\n' || m0 === '\r') {
break;
}
// broken paser?
throw new Error('CSVDataError: Illegal State [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
// un-delimited input
case 3:
// null last value
if (m0 === separator) {
endOfValue();
break;
}
// skip un-delimited new-lines
if (m0 === '\n' || m0 === '\r') {
break;
}
// non-compliant data
if (m0 === delimiter) {
throw new Error('CSVDataError: Illegal Quote [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
}
// broken parser?
throw new Error('CSVDataError: Illegal Data [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
default:
// shenanigans
throw new Error('CSVDataError: Unknown State [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
}
//console.log('val:' + m0 + ' state:' + state);
});
// submit the last value
endOfValue();
return entry;
}
},
helpers: {
/**
* $.csv.helpers.collectPropertyNames(objectsArray)
* Collects all unique property names from all passed objects.
*
* @param {Array} objects Objects to collect properties from.
*
* Returns an array of property names (array will be empty,
* if objects have no own properties).
*/
collectPropertyNames: function (objects) {
var o, propName, props = [];
for (o in objects) {
for (propName in objects[o]) {
if ((objects[o].hasOwnProperty(propName)) &&
(props.indexOf(propName) < 0) &&
(typeof objects[o][propName] !== 'function')) {
props.push(propName);
}
}
}
return props;
}
},
/**
* $.csv.toArray(csv)
* Converts a CSV entry string to a javascript array.
*
* @param {Array} csv The string containing the CSV data.
* @param {Object} [options] An object containing user-defined options.
* @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
* @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
*
* This method deals with simple CSV strings only. It's useful if you only
* need to parse a single entry. If you need to parse more than one line,
* use $.csv2Array instead.
*/
toArray: function(csv, options, callback) {
options = (options !== undefined ? options : {});
var config = {};
config.callback = ((callback !== undefined && typeof(callback) === 'function') ? callback : false);
config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator;
config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter;
var state = (options.state !== undefined ? options.state : {});
// setup
options = {
delimiter: config.delimiter,
separator: config.separator,
onParseEntry: options.onParseEntry,
onParseValue: options.onParseValue,
state: state
};
var entry = $.csv.parsers.parseEntry(csv, options);
// push the value to a callback if one is defined
if(!config.callback) {
return entry;
} else {
config.callback('', entry);
}
},
/**
* $.csv.toArrays(csv)
* Converts a CSV string to a javascript array.
*
* @param {String} csv The string containing the raw CSV data.
* @param {Object} [options] An object containing user-defined options.
* @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
* @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
*
* This method deals with multi-line CSV. The breakdown is simple. The first
* dimension of the array represents the line (or entry/row) while the second
* dimension contains the values (or values/columns).
*/
toArrays: function(csv, options, callback) {
options = (options !== undefined ? options : {});
var config = {};
config.callback = ((callback !== undefined && typeof(callback) === 'function') ? callback : false);
config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator;
config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter;
// setup
var data = [];
options = {
delimiter: config.delimiter,
separator: config.separator,
onPreParse: options.onPreParse,
onParseEntry: options.onParseEntry,
onParseValue: options.onParseValue,
onPostParse: options.onPostParse,
start: options.start,
end: options.end,
state: {
rowNum: 1,
colNum: 1
}
};
// onPreParse hook
if(options.onPreParse !== undefined) {
options.onPreParse(csv, options.state);
}
// parse the data
data = $.csv.parsers.parse(csv, options);
// onPostParse hook
if(options.onPostParse !== undefined) {
options.onPostParse(data, options.state);
}
// push the value to a callback if one is defined
if(!config.callback) {
return data;
} else {
config.callback('', data);
}
},
/**
* $.csv.toObjects(csv)
* Converts a CSV string to a javascript object.
* @param {String} csv The string containing the raw CSV data.
* @param {Object} [options] An object containing user-defined options.
* @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
* @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
* @param {Boolean} [headers] Indicates whether the data contains a header line. Defaults to true.
*
* This method deals with multi-line CSV strings. Where the headers line is
* used as the key for each value per entry.
*/
toObjects: function(csv, options, callback) {
options = (options !== undefined ? options : {});
var config = {};
config.callback = ((callback !== undefined && typeof(callback) === 'function') ? callback : false);
config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator;
config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter;
config.headers = 'headers' in options ? options.headers : $.csv.defaults.headers;
options.start = 'start' in options ? options.start : 1;
// account for headers
if(config.headers) {
options.start++;
}
if(options.end && config.headers) {
options.end++;
}
// setup
var lines = [];
var data = [];
options = {
delimiter: config.delimiter,
separator: config.separator,
onPreParse: options.onPreParse,
onParseEntry: options.onParseEntry,
onParseValue: options.onParseValue,
onPostParse: options.onPostParse,
start: options.start,
end: options.end,
state: {
rowNum: 1,
colNum: 1
},
match: false,
transform: options.transform
};
// fetch the headers
var headerOptions = {
delimiter: config.delimiter,
separator: config.separator,
start: 1,
end: 1,
state: {
rowNum:1,
colNum:1
}
};
// onPreParse hook
if(options.onPreParse !== undefined) {
options.onPreParse(csv, options.state);
}
// parse the csv
var headerLine = $.csv.parsers.splitLines(csv, headerOptions);
var headers = $.csv.toArray(headerLine[0], options);
// fetch the data
lines = $.csv.parsers.splitLines(csv, options);
// reset the state for re-use
options.state.colNum = 1;
if(headers){
options.state.rowNum = 2;
} else {
options.state.rowNum = 1;
}
// convert data to objects
for(var i=0, len=lines.length; i<len; i++) {
var entry = $.csv.toArray(lines[i], options);
var object = {};
for(var j=0; j <headers.length; j++) {
object[headers[j]] = entry[j];
}
if (options.transform !== undefined) {
data.push(options.transform.call(undefined, object));
} else {
data.push(object);
}
// update row state
options.state.rowNum++;
}
// onPostParse hook
if(options.onPostParse !== undefined) {
options.onPostParse(data, options.state);
}
// push the value to a callback if one is defined
if(!config.callback) {
return data;
} else {
config.callback('', data);
}
},
/**
* $.csv.fromArrays(arrays)
* Converts a javascript array to a CSV String.
*
* @param {Array} arrays An array containing an array of CSV entries.
* @param {Object} [options] An object containing user-defined options.
* @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
* @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
*
* This method generates a CSV file from an array of arrays (representing entries).
*/
fromArrays: function(arrays, options, callback) {
options = (options !== undefined ? options : {});
var config = {};
config.callback = ((callback !== undefined && typeof(callback) === 'function') ? callback : false);
config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator;
config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter;
var output = '',
line,
lineValues,
i, j;
for (i = 0; i < arrays.length; i++) {
line = arrays[i];
lineValues = [];
for (j = 0; j < line.length; j++) {
var strValue = (line[j] === undefined || line[j] === null) ? '' : line[j].toString();
if (strValue.indexOf(config.delimiter) > -1) {
strValue = strValue.replace(config.delimiter, config.delimiter + config.delimiter);
}
var escMatcher = '\n|\r|S|D';
escMatcher = escMatcher.replace('S', config.separator);
escMatcher = escMatcher.replace('D', config.delimiter);
if (strValue.search(escMatcher) > -1) {
strValue = config.delimiter + strValue + config.delimiter;
}
lineValues.push(strValue);
}
output += lineValues.join(config.separator) + '\r\n';
}
// push the value to a callback if one is defined
if(!config.callback) {
return output;
} else {
config.callback('', output);
}
},
/**
* $.csv.fromObjects(objects)
* Converts a javascript dictionary to a CSV string.
*
* @param {Object} objects An array of objects containing the data.
* @param {Object} [options] An object containing user-defined options.
* @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
* @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
* @param {Character} [sortOrder] Sort order of columns (named after
* object properties). Use 'alpha' for alphabetic. Default is 'declare',
* which means, that properties will _probably_ appear in order they were
* declared for the object. But without any guarantee.
* @param {Character or Array} [manualOrder] Manually order columns. May be
* a strin in a same csv format as an output or an array of header names
* (array items won't be parsed). All the properties, not present in
* `manualOrder` will be appended to the end in accordance with `sortOrder`
* option. So the `manualOrder` always takes preference, if present.
*
* This method generates a CSV file from an array of objects (name:value pairs).
* It starts by detecting the headers and adding them as the first line of
* the CSV file, followed by a structured dump of the data.
*/
fromObjects: function(objects, options, callback) {
options = (options !== undefined ? options : {});
var config = {};
config.callback = ((callback !== undefined && typeof(callback) === 'function') ? callback : false);
config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator;
config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter;
config.headers = 'headers' in options ? options.headers : $.csv.defaults.headers;
config.sortOrder = 'sortOrder' in options ? options.sortOrder : 'declare';
config.manualOrder = 'manualOrder' in options ? options.manualOrder : [];
config.transform = options.transform;
if (typeof config.manualOrder === 'string') {
config.manualOrder = $.csv.toArray(config.manualOrder, config);
}
if (config.transform !== undefined) {
var origObjects = objects;
objects = [];
var i;
for (i = 0; i < origObjects.length; i++) {
objects.push(config.transform.call(undefined, origObjects[i]));
}
}
var props = $.csv.helpers.collectPropertyNames(objects);
if (config.sortOrder === 'alpha') {
props.sort();
} // else {} - nothing to do for 'declare' order
if (config.manualOrder.length > 0) {
var propsManual = [].concat(config.manualOrder);
var p;
for (p = 0; p < props.length; p++) {
if (propsManual.indexOf( props[p] ) < 0) {
propsManual.push( props[p] );
}
}
props = propsManual;
}
var o, p, line, output = [], propName;
if (config.headers) {
output.push(props);
}
for (o = 0; o < objects.length; o++) {
line = [];
for (p = 0; p < props.length; p++) {
propName = props[p];
if (propName in objects[o] && typeof objects[o][propName] !== 'function') {
line.push(objects[o][propName]);
} else {
line.push('');
}
}
output.push(line);
}
// push the value to a callback if one is defined
return $.csv.fromArrays(output, options, config.callback);
}
};
// Maintenance code to maintain backward-compatibility
// Will be removed in release 1.0
$.csvEntry2Array = $.csv.toArray;
$.csv2Array = $.csv.toArrays;
$.csv2Dictionary = $.csv.toObjects;
// CommonJS module is defined
if (typeof module !== 'undefined' && module.exports) {
module.exports = $.csv;
}
}).call( this );
</script>
<script>
$(document).ready(function() {
if(isAPIAvailable()) {
$('#files').bind('change', handleFileSelect);
}
});
function isAPIAvailable() {
// Check for the various File API support.
if (window.File && window.FileReader && window.FileList && window.Blob) {
// Great success! All the File APIs are supported.
return true;
} else {
// source: File API availability - http://caniuse.com/#feat=fileapi
// source: <output> availability - http://html5doctor.com/the-output-element/
document.writeln('The HTML5 APIs used in this form are only available in the following browsers:<br />');
// 6.0 File API & 13.0 <output>
document.writeln(' - Google Chrome: 13.0 or later<br />');
// 3.6 File API & 6.0 <output>
document.writeln(' - Mozilla Firefox: 6.0 or later<br />');
// 10.0 File API & 10.0 <output>
document.writeln(' - Internet Explorer: Not supported (partial support expected in 10.0)<br />');
// ? File API & 5.1 <output>
document.writeln(' - Safari: Not supported<br />');
// ? File API & 9.2 <output>
document.writeln(' - Opera: Not supported');
return false;
}
}
function handleFileSelect(evt) {
var files = evt.target.files; // FileList object
var file = files[0];
// read the file metadata
var output = ''
output += '<span style="font-weight:bold;">' + escape(file.name) + '</span><br />\n';
output += ' - FileType: ' + (file.type || 'n/a') + '<br />\n';
output += ' - FileSize: ' + file.size + ' bytes<br />\n';
output += ' - LastModified: ' + (file.lastModifiedDate ? file.lastModifiedDate.toLocaleDateString() : 'n/a') + '<br />\n';
// read the file contents
printTextArea(file);
// post the results
$('#list').append(output);
}
function printTextArea(file) {
var reader = new FileReader();
reader.readAsText(file);
reader.onload = function(event){
var csv = event.target.result;
var data = $.csv.toArrays(csv);
var mystr = "";
for(var row in data) {
if (!isNaN(Number(data[row][0])))
{
mystr = mystr.concat((Number(data[row][0]).toFixed(3)).toString());
}
if (!isNaN(Number(data[row][1])))
{
mystr = mystr.concat(",");
mystr = mystr.concat((Number(data[row][1]).toFixed(3)).toString());
mystr = mystr.concat("\n");
}
else
{
mystr = mystr.concat("\n");
}
}
mystr = mystr.substring(0,mystr.length - 1);
document.getElementById("Real_inp").value = mystr;
};
reader.onerror = function(){ alert('Unable to read ' + file.fileName); };
}
</script>
<!--[if IE ]>
<script type="text/javascript" src="https://firebasestorage.googleapis.com/v0/b/js-static-assets.appspot.com/o/excanvas_min.js?alt=media&token=9dd78ad2-f09b-45dd-8a09-8db2b2546d1d"></script>
<![endif]-->
<script src="https://firebasestorage.googleapis.com/v0/b/js-static-assets.appspot.com/o/jquery_flot.js?alt=media&token=bde69a50-3df9-4794-b7d8-dd4e840472cd" type="text/javascript"></script>
<p style="text-align:justify">
This blog post implements a basic k-means clustering algorithm, which can be applied to either a scalar number or 2-d data (x and y component). Graphs of the clustered data and algorithm convergence (as measured by the changes in cluster membership of the data samples between consecutive iterations) are displayed below. <b>For a more general (and better performing) k-means calculator, you can see a much more recent blog post</b> <a href="https://scistatcalc.blogspot.com/2020/10/online-k-means-calculator-for-arbitrary.html"><b>here</b></a></p>
<p style="text-align:justify">
The cluster centres (or centroids) are initialised using the k-means++ algorithm as proposed by David Arthur and Sergei Vassilvitski in 2007.</p>
<p style="text-align:justify">
Please enter the numbers in the text areas below - either one number per line or two comma separated numbers per line. There must be no new line after the last number.</p>
<p style="text-align:justify">
Alternatively you can choose to load a CSV file, which must be either a single column of numbers (for a real only input) or two comma-separated columns of numbers - the first line can be a comment line, starting with the character #.</p>
<p style="text-align:justify">
To perform the k-means clustering, please enter the number of clusters and the number of iterations in the appropriate fields, then press the button labelled "Perform k-means clustering" below - the results will populate the textareas below labelled "Output" and "Centroid values". The "Output" textarea will list the sample values and the cluster/centroid index each sample belongs to, while the "Centroid values" textarea will list the centroid index and the value of the centroids (or cluster centres).</p>
<p style="text-align:justify">
Note that the k-means algorithm can converge to a local minimum, and also exhibit degeneracy, whereby one of the clusters has no members. Should these scenarios occur, simply re-run the algorithm, by clicking on the button labelled "Perform k-means clustering".</p>
<p style="text-align:justify">
An example two-dimensional dataset has been loaded, with three clusters of 200 samples - the number of iterations is set to ten. Pressing the "Perform k-means clustering" can result in a local minima being reached, which will be obvious to spot from the Cluster Visualisation display. In this case, simply re-run the algorithm.
</p>
<br />
<div class="clearfix" id="inputs">
<input id="files" name="files[]" single="" type="file" />
</div>
<br />
<div style="float: left; width: 200px;">
Input
</div>
<br />
<textarea cols="25" id="Real_inp" rows="10"></textarea>
<br />
<div style="float: left; width: 200px;">
<input name="b1" onclick="clear_real_inp()" style="margin-left: 0px;" type="Button" value="Clear Input" />
</div>
<br>
<br>
<form name="frmOne">
Enter number of clusters (k value):-<br>
<input name="txtKNumber" size="30" type="TextArea" value="" />
<br>
Enter number of iterations:-<br>
<input name="txtIterNumber" size="30" type="TextArea" value="" />
</form>
<br>
<br><br>
<input name="b1" onclick="calc_k_means()" style="margin-left: 0px;" type="Button" value="Perform k-means clustering" />
<br />
<br />
<div style="float: left; width: 200px;">
Output:-
</div>
<div style="float: left; width: 200px;">
Centroid values:-
</div>
<br>
<textarea cols="25" id="Real_out" rows="10"></textarea>
<textarea cols="25" id="centroid_out" rows="10"></textarea>
<br>
<br>
<table style="text-align: center;">
<tbody>
<tr>
<td></td>
<td>Cluster Visualisation</td>
<td></td>
</tr>
<tr>
<td>Value</td>
<td><div id='chart0' style='height:300px;width:500px;'>
</div>
</td>
<td></td>
</tr>
<tr>
<td></td>
<td>Samples</td>
<td></td>
</tr>
<tr>
<div><p id="legendDiv"></p></div>
</tr>
</tbody></table>
<table style="text-align: center;">
<tbody>
<tr>
<td></td>
<td>Algorithm convergence</td>
<td></td>
</tr>
<tr>
<td>Change</td>
<td><div id='chart1' style='height:300px;width:500px;'>
</div>
</td>
<td></td>
</tr>
<tr>
<td></td>
<td>Iteration number</td>
<td></td>
</tr>
</tbody></table>
<p id="pdbg">Summary statistics pending..</p>
<script language="JavaScript">
// Load an example dataset
window.onload = set_inputs();
function set_inputs() {
var sample_data = ""
sample_data = sample_data + "1.870,1.991\n"
sample_data = sample_data + "2.007,2.186\n"
sample_data = sample_data + "2.137,1.982\n"
sample_data = sample_data + "1.883,2.103\n"
sample_data = sample_data + "1.917,2.019\n"
sample_data = sample_data + "1.935,2.083\n"
sample_data = sample_data + "1.849,2.087\n"
sample_data = sample_data + "0.122,-0.223\n"
sample_data = sample_data + "2.003,2.031\n"
sample_data = sample_data + "0.055,0.029\n"
sample_data = sample_data + "0.054,0.107\n"
sample_data = sample_data + "0.827,0.981\n"
sample_data = sample_data + "0.980,0.887\n"
sample_data = sample_data + "1.222,0.883\n"
sample_data = sample_data + "1.056,0.921\n"
sample_data = sample_data + "2.080,1.996\n"
sample_data = sample_data + "1.021,1.066\n"
sample_data = sample_data + "-0.055,0.016\n"
sample_data = sample_data + "0.970,1.027\n"
sample_data = sample_data + "1.125,0.860\n"
sample_data = sample_data + "1.023,0.978\n"
sample_data = sample_data + "-0.036,-0.019\n"
sample_data = sample_data + "0.200,0.109\n"
sample_data = sample_data + "-0.163,-0.131\n"
sample_data = sample_data + "-0.176,-0.122\n"
sample_data = sample_data + "-0.023,-0.044\n"
sample_data = sample_data + "-0.029,0.028\n"
sample_data = sample_data + "1.920,1.991\n"
sample_data = sample_data + "-0.030,-0.031\n"
sample_data = sample_data + "1.940,1.967\n"
sample_data = sample_data + "1.021,1.141\n"
sample_data = sample_data + "1.025,1.097\n"
sample_data = sample_data + "1.997,2.081\n"
sample_data = sample_data + "1.921,2.026\n"
sample_data = sample_data + "-0.107,-0.042\n"
sample_data = sample_data + "0.911,1.093\n"
sample_data = sample_data + "1.027,1.151\n"
sample_data = sample_data + "1.992,2.079\n"
sample_data = sample_data + "1.088,0.979\n"
sample_data = sample_data + "-0.031,0.019\n"
sample_data = sample_data + "0.082,-0.033\n"
sample_data = sample_data + "-0.012,-0.034\n"
sample_data = sample_data + "2.110,1.828\n"
sample_data = sample_data + "1.960,1.887\n"
sample_data = sample_data + "0.892,0.854\n"
sample_data = sample_data + "1.900,2.054\n"
sample_data = sample_data + "0.073,0.103\n"
sample_data = sample_data + "2.142,1.770\n"
sample_data = sample_data + "0.034,0.069\n"
sample_data = sample_data + "0.109,0.037\n"
sample_data = sample_data + "1.011,0.808\n"
sample_data = sample_data + "1.976,1.800\n"
sample_data = sample_data + "2.050,2.045\n"
sample_data = sample_data + "-0.150,-0.015\n"
sample_data = sample_data + "2.145,2.127\n"
sample_data = sample_data + "2.085,2.116\n"
sample_data = sample_data + "2.090,1.974\n"
sample_data = sample_data + "0.106,-0.044\n"
sample_data = sample_data + "0.044,0.109\n"
sample_data = sample_data + "0.934,1.016\n"
sample_data = sample_data + "1.897,1.986\n"
sample_data = sample_data + "1.105,0.926\n"
sample_data = sample_data + "-0.056,0.092\n"
sample_data = sample_data + "0.993,1.006\n"
sample_data = sample_data + "-0.026,-0.031\n"
sample_data = sample_data + "0.984,1.079\n"
sample_data = sample_data + "1.027,1.033\n"
sample_data = sample_data + "0.225,-0.012\n"
sample_data = sample_data + "1.903,1.710\n"
sample_data = sample_data + "0.058,0.004\n"
sample_data = sample_data + "1.042,1.208\n"
sample_data = sample_data + "1.824,2.033\n"
sample_data = sample_data + "-0.082,-0.052\n"
sample_data = sample_data + "0.910,0.996\n"
sample_data = sample_data + "2.004,2.134\n"
sample_data = sample_data + "-0.082,0.153\n"
sample_data = sample_data + "-0.075,0.136\n"
sample_data = sample_data + "1.078,1.035\n"
sample_data = sample_data + "2.013,1.968\n"
sample_data = sample_data + "0.174,-0.006\n"
sample_data = sample_data + "-0.072,0.002\n"
sample_data = sample_data + "1.147,0.961\n"
sample_data = sample_data + "1.952,1.947\n"
sample_data = sample_data + "0.051,0.048\n"
sample_data = sample_data + "-0.087,-0.127\n"
sample_data = sample_data + "2.076,2.166\n"
sample_data = sample_data + "1.932,1.960\n"
sample_data = sample_data + "-0.015,-0.056\n"
sample_data = sample_data + "0.997,0.833\n"
sample_data = sample_data + "1.049,0.886\n"
sample_data = sample_data + "2.059,2.220\n"
sample_data = sample_data + "1.081,0.999\n"
sample_data = sample_data + "1.032,1.001\n"
sample_data = sample_data + "0.892,1.078\n"
sample_data = sample_data + "1.929,1.969\n"
sample_data = sample_data + "0.810,1.012\n"
sample_data = sample_data + "1.287,0.992\n"
sample_data = sample_data + "2.045,2.056\n"
sample_data = sample_data + "-0.009,-0.049\n"
sample_data = sample_data + "-0.136,0.046\n"
sample_data = sample_data + "0.934,1.020\n"
sample_data = sample_data + "0.973,0.963\n"
sample_data = sample_data + "0.118,-0.025\n"
sample_data = sample_data + "1.982,1.971\n"
sample_data = sample_data + "1.959,1.964\n"
sample_data = sample_data + "-0.014,0.017\n"
sample_data = sample_data + "0.017,0.122\n"
sample_data = sample_data + "0.916,1.063\n"
sample_data = sample_data + "-0.020,-0.087\n"
sample_data = sample_data + "0.020,-0.145\n"
sample_data = sample_data + "0.992,0.957\n"
sample_data = sample_data + "1.990,1.909\n"
sample_data = sample_data + "0.959,1.013\n"
sample_data = sample_data + "-0.008,0.038\n"
sample_data = sample_data + "0.985,0.986\n"
sample_data = sample_data + "0.042,-0.013\n"
sample_data = sample_data + "0.096,-0.013\n"
sample_data = sample_data + "0.069,-0.137\n"
sample_data = sample_data + "0.115,0.119\n"
sample_data = sample_data + "2.118,2.105\n"
sample_data = sample_data + "0.952,1.005\n"
sample_data = sample_data + "2.065,1.884\n"
sample_data = sample_data + "-0.056,-0.111\n"
sample_data = sample_data + "0.104,-0.026\n"
sample_data = sample_data + "1.901,1.851\n"
sample_data = sample_data + "-0.128,0.003\n"
sample_data = sample_data + "1.961,1.941\n"
sample_data = sample_data + "1.086,0.951\n"
sample_data = sample_data + "1.933,1.960\n"
sample_data = sample_data + "2.026,2.178\n"
sample_data = sample_data + "0.889,0.845\n"
sample_data = sample_data + "1.153,0.894\n"
sample_data = sample_data + "0.106,0.039\n"
sample_data = sample_data + "0.993,1.106\n"
sample_data = sample_data + "1.016,1.036\n"
sample_data = sample_data + "-0.248,-0.030\n"
sample_data = sample_data + "1.865,1.974\n"
sample_data = sample_data + "1.139,1.005\n"
sample_data = sample_data + "1.077,1.001\n"
sample_data = sample_data + "0.005,0.074\n"
sample_data = sample_data + "2.019,2.069\n"
sample_data = sample_data + "-0.032,0.007\n"
sample_data = sample_data + "0.692,1.134\n"
sample_data = sample_data + "1.909,1.964\n"
sample_data = sample_data + "2.194,1.919\n"
sample_data = sample_data + "2.095,1.964\n"
sample_data = sample_data + "1.114,1.074\n"
sample_data = sample_data + "0.936,1.066\n"
sample_data = sample_data + "0.996,0.968\n"
sample_data = sample_data + "-0.107,0.010\n"
sample_data = sample_data + "0.135,0.099\n"
sample_data = sample_data + "0.046,-0.128\n"
sample_data = sample_data + "1.076,0.962\n"
sample_data = sample_data + "-0.164,0.044\n"
sample_data = sample_data + "0.787,1.078\n"
sample_data = sample_data + "0.112,-0.110\n"
sample_data = sample_data + "0.046,0.053\n"
sample_data = sample_data + "0.747,0.848\n"
sample_data = sample_data + "0.021,0.099\n"
sample_data = sample_data + "0.146,0.042\n"
sample_data = sample_data + "1.844,1.819\n"
sample_data = sample_data + "1.066,0.987\n"
sample_data = sample_data + "0.855,0.922\n"
sample_data = sample_data + "-0.158,-0.033\n"
sample_data = sample_data + "2.008,1.980\n"
sample_data = sample_data + "1.029,0.987\n"
sample_data = sample_data + "-0.017,0.057\n"
sample_data = sample_data + "2.045,1.996\n"
sample_data = sample_data + "2.129,1.986\n"
sample_data = sample_data + "-0.046,0.076\n"
sample_data = sample_data + "1.030,0.959\n"
sample_data = sample_data + "-0.002,-0.208\n"
sample_data = sample_data + "1.028,1.076\n"
sample_data = sample_data + "-0.021,0.059\n"
sample_data = sample_data + "2.039,2.056\n"
sample_data = sample_data + "0.991,1.002\n"
sample_data = sample_data + "2.109,2.080\n"
sample_data = sample_data + "1.120,0.834\n"
sample_data = sample_data + "1.956,2.128\n"
sample_data = sample_data + "1.913,1.988\n"
sample_data = sample_data + "1.932,1.974\n"
sample_data = sample_data + "1.041,0.975\n"
sample_data = sample_data + "1.210,0.872\n"
sample_data = sample_data + "-0.003,-0.159\n"
sample_data = sample_data + "1.926,2.076\n"
sample_data = sample_data + "2.127,2.007\n"
sample_data = sample_data + "-0.010,0.234\n"
sample_data = sample_data + "2.094,1.938\n"
sample_data = sample_data + "-0.183,-0.136\n"
sample_data = sample_data + "1.989,2.211\n"
sample_data = sample_data + "-0.008,0.033\n"
sample_data = sample_data + "1.946,1.911\n"
sample_data = sample_data + "2.215,1.822\n"
sample_data = sample_data + "1.917,1.968\n"
sample_data = sample_data + "2.077,2.110\n"
sample_data = sample_data + "2.190,1.934\n"
sample_data = sample_data + "1.009,0.995\n"
sample_data = sample_data + "0.042,0.028\n"
sample_data = sample_data + "1.977,2.179\n"
sample_data = sample_data + "1.018,1.014"
document.getElementById("Real_inp").value = sample_data;
document.frmOne.txtKNumber.value = "3"
document.frmOne.txtIterNumber.value = "10"
}
var options = {legend:{position:"s",noColumns: 4,container: document.getElementById("legendDiv")},selection:{mode:"xy"}};
var series1 = [ [0,0],[1,0],[2,0],[3,0]];
var data = [ series1 ];
$.plot($("#chart0"), data);
$.plot($("#chart1"), data);
function clear_real_inp() {
document.getElementById("Real_inp").value = "";
}
function calc_k_means() {
var dat_inp = document.getElementById("Real_inp").value;
var dat_arr = new Array();
dat_arr = dat_inp.split("\n");
var nrows = dat_arr.length;
var ncols = dat_arr[0].split(",").length;
debug_str = "Number of rows is " + nrows + "\n";
tmp_str = "Number of cols is " + ncols + "\n";
debug_str = debug_str.concat(tmp_str);
var k = Number(document.frmOne.txtKNumber.value);
var niter = Number(document.frmOne.txtIterNumber.value);
tmp_str = "Number of clusters is " + k + "\n";
debug_str = debug_str.concat(tmp_str);
tmp_str = "Number of iterations is " + niter + "\n";
debug_str = debug_str.concat(tmp_str);
// Initialise centroids
var permArray = new Array();
permArray = randperm(nrows);
var cent_init = new Array();
var cent_vec = new Array();
if (ncols == 1)
{
for (var idx = 0; idx < k; idx++)
cent_init.push(Number(dat_arr[permArray[idx]]));
for (var idx = 0; idx < k; idx++)
{
tmp_str = cent_init[idx] + "\n";
debug_str = debug_str.concat(tmp_str);
}
}
else if (ncols == 2)
{
for (var idx = 0; idx < k; idx++)
{
var tmparr = new Array();
tmparr = dat_arr[permArray[idx]].split(",");
var tmpvar = [Number(tmparr[0]),Number(tmparr[1])];
cent_init.push(tmpvar);
}
for (var idx = 0; idx < k; idx++)
{
tmp_str = cent_init[idx] + "\n";
debug_str = debug_str.concat(tmp_str);
}
}
// End of Centroid initialisation
// Centroid initialisation based on k++ algorithm..
var cent_kpp_init = new Array();
var new_dat_arr = dat_arr;
cent_kpp_init = kpp_alg(new_dat_arr, k, ncols,10);
// alert("Init cluster centres " + cent_kpp_init);
var isz = check_labels_assigned(new_dat_arr,cent_kpp_init,ncols);
var dbg_msg = "";
if (isz == 1)
dbg_msg = "One or more labels are unassigned!";
else
dbg_msg = "All labels are assigned";
var retries = 0;
if (ncols == 1)
{
while ((check_labels_assigned(new_dat_arr,cent_kpp_init,ncols) != 0) && (retries < 200))
{
cent_kpp_init = kpp_alg(new_dat_arr, k, ncols, retries*10);
retries++;
}
}
var tmp_str = "\nNumber of retries is " + retries;
dbg_msg = dbg_msg.concat(tmp_str);
document.getElementById("pdbg").innerHTML=dbg_msg;
// cent_vec = cent_init;
cent_vec = cent_kpp_init;
// The k-means algorithm
var iter_diff = new Array();
var samp_k = new Array();
var samp2_prev = new Array();
for (var idx = 0; idx < nrows; idx++)
samp2_prev.push(0);
// Start of 1-d processing
if (ncols == 1)
{
// Create the sample and label two-column vector
for (var idx = 0; idx < nrows; idx++)
samp_k.push([Number(dat_arr[idx]),0]);
// One dimensional k-means algorithm
for (var m = 0; m < niter; m++)
{
for (var idx = 0; idx < nrows; idx++)
samp2_prev[idx] = samp_k[idx][1];
for (var idx = 0; idx < nrows; idx++)
{
var b = find_nearest_1d(samp_k[idx][0],cent_vec);
samp_k[idx][1] = b;
}
// Recalculate centroids
for (var idx = 0; idx < k; idx++)
{
var tot = 0;
var ns = 0;
for (var t=0; t < nrows; t++)
{
if (samp_k[t][1] == (idx + 1))
{
tot = tot + samp_k[t][0];
ns = ns + 1;
}
}
cent_vec[idx] = tot/ns;
}
// Find the difference in labels
var dfval = 0;
for (var idx = 0; idx < nrows; idx++)
dfval = dfval + Math.abs(samp2_prev[idx] - samp_k[idx][1]);
iter_diff.push(dfval);
}
// One dimensional k-means algorithm
}
// End of 1-d processing
// Start of 2-d processing
else if (ncols == 2)
{
// Create the sample and label three-column vector
for (var idx = 0; idx < nrows; idx++)
{
var tmparr = new Array();
tmparr = dat_arr[idx].split(",");
samp_k.push([Number(tmparr[0]),Number(tmparr[1]),0]);
}
// Two dimensional k-means algorithm
for (var m = 0; m < niter; m++)
{
for (var idx = 0; idx < nrows; idx++)
samp2_prev[idx] = samp_k[idx][2];
for (var idx = 0; idx < nrows; idx++)
{
var b = find_nearest_2d(samp_k[idx][0],samp_k[idx][1],cent_vec);
samp_k[idx][2] = b;
}
// Recalculate centroids
for (var idx = 0; idx < k; idx++)
{
var tot1 = 0;
var tot2 = 0;
var ns = 0;
for (var t=0; t < nrows; t++)
{
if (samp_k[t][2] == (idx + 1))
{
tot1 = tot1 + samp_k[t][0];
tot2 = tot2 + samp_k[t][1];
ns = ns + 1;
}
}
cent_vec[idx][0] = tot1/ns;
cent_vec[idx][1] = tot2/ns;
}
// Find the difference in labels
var dfval = 0;
for (var idx = 0; idx < nrows; idx++)
dfval = dfval + Math.abs(samp2_prev[idx] - samp_k[idx][2]);
iter_diff.push(dfval);
}
// Two dimensional k-means algorithm
}
// End of 2-d processing
tmp_str = "Iterations difference " + "\n";
debug_str = debug_str.concat(tmp_str);
for (var idx = 0; idx < iter_diff.length; idx++)
{
tmp_str = iter_diff[idx] + "\n";
debug_str = debug_str.concat(tmp_str);
}
var res_str = "# Sample value, Centroid index\n";
if (ncols == 1)
{
for (var idx = 0; idx < nrows; idx++)
{
tmp_str = samp_k[idx][0] + "," + samp_k[idx][1] + "\n";
res_str = res_str.concat(tmp_str);
}
}
else if (ncols == 2)
{
for (var idx = 0; idx < nrows; idx++)
{
tmp_str = samp_k[idx][0] + "," + samp_k[idx][1] + "," + samp_k[idx][2] + "\n";
res_str = res_str.concat(tmp_str);
}
}
var centroid_str = "#Centroid index, Centroid value\n";
if (ncols == 1)
{
for (var idx = 0; idx < k; idx++)
{
var mylabel = idx + 1;
tmp_str = mylabel + "," + cent_vec[idx].toFixed(3) + "\n";
centroid_str = centroid_str.concat(tmp_str);
}
}
else if (ncols == 2)
{
for (var idx = 0; idx < k; idx++)
{
var mylabel = idx + 1;
tmp_str = mylabel + "," + cent_vec[idx][0].toFixed(3) + "," + cent_vec[idx][1].toFixed(3) + "\n";
centroid_str = centroid_str.concat(tmp_str);
}
}
document.getElementById("centroid_out").value = centroid_str;
document.getElementById("Real_out").value = res_str;
// Plot the graph
var iter_data = new Array();
for (var idx = 0; idx < iter_diff.length; idx++)
iter_data[idx] = [idx,iter_diff[idx]];
var ser1 = {
label: "",
data: iter_data,
color: "#0000ff",
points: { show: false },
lines: { show: true },
bars: { show: false},
yaxis: 1
};
var data = [ ser1];
$.plot($("#chart1"),data);
// Plotting clusters..
var data2 = new Array();
if (ncols == 1)
{
for (var idx=0; idx < k; idx++)
{
var num1 = Math.round(((k - idx)/k) * 255);
var num2 = Math.round((idx/k) * 255);
var num1hex = num1.toString(16);
var num2hex = num2.toString(16);
if (num1hex.length == 1)
num1hex = "0" + num1hex;
if (num2hex.length == 1)
num2hex = "0" + num2hex;
col_str = "#" + num1hex + num2hex + "00";
var dset = new Array();
var lbl = idx + 1;
tmpstr = "Cluster " + lbl;
for (var t = 0; t < nrows; t++)
{
if (samp_k[t][1] == idx + 1)
dset.push([t,samp_k[t][0]]);
}
data2[idx] = {
label: tmpstr,
data: dset,
color: col_str,
points: { show: true },
lines: { show: false },
bars: { show: false },
yaxis: 1,
selection: { mode: "xy" }};
}
$.plot($("#chart0"), data2, options);
}
else if (ncols == 2)
{
for (var idx=0; idx < k; idx++)
{
var num1 = Math.round(((k - idx)/k) * 255);
var num2 = Math.round((idx/k) * 255);
var num1hex = num1.toString(16);
var num2hex = num2.toString(16);
if (num1hex.length == 1)
num1hex = "0" + num1hex;
if (num2hex.length == 1)
num2hex = "0" + num2hex;
col_str = "#" + num1hex + num2hex + "00";
var dset = new Array();
var lbl = idx + 1;
tmpstr = "Cluster " + lbl;
for (var t = 0; t < nrows; t++)
{
if (samp_k[t][2] == idx + 1)
dset.push([samp_k[t][0],samp_k[t][1]]);
}
data2[idx] = {
label: tmpstr,
data: dset,
color: col_str,
points: { show: true },
lines: { show: false },
bars: { show: false },
yaxis: 1,
selection: { mode: "xy" }};
}
$.plot($("#chart0"), data2,options);
}
}
function find_nearest_1d(sampval,centres)
{
var minpos = 1;
var dval = 0;
var calcval = 0;
for (var idx = 0; idx < centres.length; idx++)
{
if (idx == 0)
{
minpos = 1;
dval = (sampval - centres[idx]) * (sampval - centres[idx]);
}
else
{
calcval = (sampval - centres[idx]) * (sampval - centres[idx]);
if (calcval < dval)
{
minpos = idx + 1;
dval = calcval;
}
}
}
return minpos;
}
function find_nearest_2d(sampval1,sampval2,centres)
{
var minpos = 1;
var dval = 0;
var calcval = 0;
for (var idx = 0; idx < centres.length; idx++)
{
if (idx == 0)
{
minpos = 1;
dval = (sampval1 - centres[idx][0]) * (sampval1 - centres[idx][0]) + (sampval2 - centres[idx][1]) * (sampval2 - centres[idx][1]);
}
else
{
calcval = (sampval1 - centres[idx][0]) * (sampval1 - centres[idx][0]) + (sampval2 - centres[idx][1]) * (sampval2 - centres[idx][1]);
if (calcval < dval)
{
minpos = idx + 1;
dval = calcval;
}
}
}
return minpos;
}
function check_labels_assigned(dinp_arr,ctrs,ncols)
{
var dinp_arr_int = new Array();
var cnt_ctrs = new Array();
// alert("Number of columns " + ncols);
if (ncols == 1)
{
for (var t = 0; t < dinp_arr.length; t++)
dinp_arr_int[t] = Number(dinp_arr[t]);
}
else
{
for (var t = 0; t < dinp_arr.length; t++)
dinp_arr_int[t] = [Number(dinp_arr[t][0]),Number(dinp_arr[t][1])];
}
for (var v = 0; v < ctrs.length; v++)
cnt_ctrs.push(0);
// alert("Internal cluster counter length " + cnt_ctrs.length);
var minidx = 0;
for (var t = 0; t < dinp_arr_int.length; t++)
{
if (ncols == 1)
{
minidx = find_nearest_1d(dinp_arr_int[t],ctrs);
}
else
{
minidx = find_nearest_2d(dinp_arr_int[t][0],dinp_arr_int[t][1],ctrs);
}
// alert("Minimum index: " + minidx);
cnt_ctrs[minidx-1] = cnt_ctrs[minidx-1]+1;
}
var is_centre_zero = 0;
for (var t=0; t < ctrs.length; t++)
{
// alert(cnt_ctrs[t]);
if (cnt_ctrs[t] == 0)
is_centre_zero = 1;
}
return is_centre_zero;
}
var CustomRandom = function(nseed) {
var seed,
constant = Math.pow(2, 13)+1,
prime = 1987,
//any prime number, needed for calculations, 1987 is my favorite:)
maximum = 1000;
//maximum number needed for calculation the float precision of the numbers (10^n where n is number of digits after dot)
if (nseed) {
seed = nseed;
}
if (seed == null) {
//before you will correct me in this comparison, read Andrea Giammarchi's text about coercion http://goo.gl/N4jCB
seed = (new Date()).getTime();
//if there is no seed, use timestamp
}
return {
next : function(min, max) {
seed *= constant;
seed += prime;
return min && max ? min+seed%maximum/maximum*(max-min) : seed%maximum/maximum;
// if 'min' and 'max' are not provided, return random number between 0 & 1
}
}
}
function kpp_alg(inp_arr, kval, ncol, seedval) {
var tidx = 0;
Math.seed = seedval;
var array_int = new Array();
for (var t = 0; t < inp_arr.length; t++)
array_int[t] = inp_arr[t];
var res_arr = new Array();
if (ncol == 1)
{
var s_array = new Array();
for (var t = 0; t < array_int.length; t++)
s_array[t] = Number(array_int[t]);
s_array.sort(compare);
for (var t = 0; t < kval; t++)
{
if (t == 0)
{
tidx = Math.floor(Math.random()*s_array.length);
// tidx = Math.floor(CustomRandom(seedval)*s_array.length);
res_arr.push(s_array.splice(tidx,1));
}
else
{
tidx = sel_next(res_arr[res_arr.length - 1],s_array,seedval);
res_arr.push(s_array.splice(tidx,1));
}
}
}
else if (ncol == 2)
{
var s2_array = new Array();
for (var t = 0; t < array_int.length; t++)
{
var tmp2arr = new Array();
tmp2arr = array_int[t].split(",");
var tmp2var = [Number(tmp2arr[0]),Number(tmp2arr[1])];
s2_array.push(tmp2var);
}
s2_array.sort(compare_magn);
for (var t = 0; t < kval; t++)
{
if (t == 0)
{
tidx = Math.floor(Math.random()*s2_array.length);
res_arr.push(s2_array[tidx]);
s2_array.splice(tidx,1);
}
else
{
tidx = sel_next2(res_arr[res_arr.length - 1],s2_array);
res_arr.push(s2_array[tidx]);
s2_array.splice(tidx,1);
}
}
}
return res_arr;
}
function sel_next(clast,arr_in,seedval) {
var idx = 0;
var Dk2 = new Array();
var Dk2_sum = 0;
for (var t= 0; t < arr_in.length; t++)
{
Dk2[t] = (clast - arr_in[t])*(clast - arr_in[t]);
Dk2_sum = Dk2_sum + Dk2[t];
}
for (var t= 0; t < arr_in.length; t++)
Dk2[t] = Dk2[t]/Dk2_sum;
var cumsum = new Array();
for (var t= 0; t < arr_in.length; t++)
{
if (t == 0)
cumsum[t] = Dk2[t];
else
cumsum[t] = cumsum[t-1] + Dk2[t];
}
var u = Math.floor(Math.random());
// var u = Math.floor(CustomRandom(seedval));
for (var t= 0; t < cumsum.length; t++)
{
if (t == 0)
{
if (u < cumsum[t])
idx = 0;
}
else
{
if ((u >= cumsum[t-1]) & (u < cumsum[t]))
idx = t;
}
}
return idx;
}
function sel_next2(clast,arr_in) {
var idx = 0;
var Dk2 = new Array();
var Dk2_sum = 0;
for (var t= 0; t < arr_in.length; t++)
{
var tmpvar = arr_in[t];
Dk2[t] = (clast[0] - tmpvar[0])*(clast[0] - tmpvar[0]) + (clast[1] - tmpvar[1])*(clast[1] - tmpvar[1]);
Dk2_sum = Dk2_sum + Dk2[t];
}
for (var t= 0; t < arr_in.length; t++)
Dk2[t] = Dk2[t]/Dk2_sum;
var cumsum = new Array();
for (var t= 0; t < arr_in.length; t++)
{
if (t == 0)
cumsum[t] = Dk2[t];
else
cumsum[t] = cumsum[t-1] + Dk2[t];
}
var u = Math.floor(Math.random());
for (var t= 0; t < cumsum.length; t++)
{
if (t == 0)
{
if (u < cumsum[t])
idx = 0;
}
else
{
if ((u >= cumsum[t-1]) & (u < cumsum[t]))
idx = t;
}
}
return idx;
}
function compare(a,b) {
if (a < b)
return -1;
if (a > b)
return 1;
return 0;
}
function compare_magn(a,b) {
var magn_a = (a[0]*a[0]) + (a[1]*a[1]);
var magn_b = (b[0]*b[0]) + (b[1]*b[1]);
if (magn_a < magn_b)
return -1;
if (magn_a > magn_b)
return 1;
return 0;
}
function randperm(maxValue){
// first generate number sequence
var permArray = new Array(maxValue);
for(var i = 0; i < maxValue; i++){
permArray[i] = i;
}
// draw out of the number sequence
for (var i = (maxValue - 1); i >= 0; --i){
var randPos = Math.floor(i * Math.random());
var tmpStore = permArray[i];
permArray[i] = permArray[randPos];
permArray[randPos] = tmpStore;
}
return permArray;
}
</script>
Alijah Ahmedhttp://www.blogger.com/profile/01151589526613233918noreply@blogger.com103tag:blogger.com,1999:blog-8441309013858171051.post-61734752180842970542014-01-05T03:33:00.000-08:002023-12-14T01:38:37.020-08:00Chi-squared Test for Independence <script type="text/javascript" id="MathJax-script" async
src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js">
</script>
<p style="text-align:justify"> In this blog post I will discuss Pearson's Chi-squared test for independence, using an example.</p>
<p style="text-align:justify"> Pearson's Chi-squared test for independence is applied to outcomes that are arranged in a tabular form and tests for independence between two factors. The rows correspond to the levels for one factor and the columns to the levels for the other factor. The Null Hypothesis is that the two factors are independent.</p>
<p style="text-align:justify"> For example, we could have two rows corresponding to gender (Female and Male), and three columns corresponding to what products were purchased from a supermarket (Product A, Product B and Product C). The six entries would correspond to which gender bought which product. The Null Hypothesis is that the proportion of people who bought each of the three products is independent of their gender. For the Null Hypothesis to be valid, the proportion of females who bought Product A is equal to the proportion of males and females who bought Product A (out of the total population of males and females), the proportion of males who bought Product A is equal to the proportion of males and females who bought Product A (out of the total population of males and females), and so on. To elaborate on this further, we can represent the outcomes in the table below.</p>
<br>
<table border="1" style="width: 100%">
<tbody>
<tr>
<td></td>
<td>Product A </td>
<td>Product B </td>
<td>Product C </td>
</tr>
<tr>
<td>Female </td>
<td>\(a\)</td>
<td>\(b\)</td>
<td>\(c\)</td>
</tr>
<tr>
<td>Male </td>
<td>\(d\)</td>
<td>\(e\)</td>
<td>\(f\)</td>
</tr>
</tbody>
</table>
<br>
<p style="text-align:justify">For the Null Hypothesis to hold, the proportion of females who purchased Product A would be equal to the proportion of both males and females who purchased Product A out of the total population:-</p>
\(\Large \frac{a}{a+b+c}=\frac{a+d}{a+b+c+d+e+f}\)<br><br>
Rearranging the above equation, we obtain the expected value of \(a\) as
<br><br>\(\Large \hat{a}=\frac{(a+d)(a+b+c)}{a+b+c+d+e+f}\)<br><br>
and the proportion of males who purchased Product A would be equal to the proportion of both males and females who purchased Product A out of the total population:-
<br><br>\(\Large \frac{d}{d+e+f}=\frac{a+d}{a+b+c+d+e+f}\)<br><br>
Rearranging the above equation in a similar fashion, we obtain the expected value of \(d\) as
<br><br>\(\Large \hat{d}=\frac{(a+d)(d+e+f)}{a+b+c+d+e+f}\)<br><br>
<p style="text-align:justify">Going through all the entries, the expected value of each entry turns out to be the product of the entry's column sum and the entry's row sum divided by the total population.</p>
<p style="text-align:justify">Once all the expected values of the entries are computed, the sum of squares of the difference between the actual entry outcome and the expected value of that entry divided by the expected value of that entry is used to calculate the chi-squared test statistic. Mathematically, this can be represented as</p>
<br>\(\Large \chi^2=\sum_{m=1}^{r}\sum_{n=1}^{c}\frac{(O(m,n)-E(m,n))^2}{E(m,n)}\)<br><br>
<p style="text-align:justify">where \(r\)is the number of rows, \(c\) is the number of columns, \(O(m,n)\) is the outcome in row \(m\), column \(n\) and \(E(m,n)\) the corresponding expected value. The degree of freedom for this statistic is \((r-1)(c-1)\). Based on the chi-squared statistic and the degrees of freedom we can calculate the p-value, and if this value is less than a significance level of our choice (a commonly used value is 0.05) we reject the Null Hypothesis that the proportion of people who bought each of the three products is independent of their gender.</p>
</body>
</html>
Alijah Ahmedhttp://www.blogger.com/profile/01151589526613233918noreply@blogger.com34tag:blogger.com,1999:blog-8441309013858171051.post-102590858235093982013-12-23T06:27:00.061-08:002023-12-09T07:09:54.200-08:00FFT calculator<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<script language="Javascript">
/**
* jQuery-csv (jQuery Plugin)
*
* This document is licensed as free software under the terms of the
* MIT License: http://www.opensource.org/licenses/mit-license.php
*
* Acknowledgements:
* The original design and influence to implement this library as a jquery
* plugin is influenced by jquery-json (http://code.google.com/p/jquery-json/).
* If you're looking to use native JSON.Stringify but want additional backwards
* compatibility for browsers that don't support it, I highly recommend you
* check it out.
*
* A special thanks goes out to rwk@acm.org for providing a lot of valuable
* feedback to the project including the core for the new FSM
* (Finite State Machine) parsers. If you're looking for a stable TSV parser
* be sure to take a look at jquery-tsv (http://code.google.com/p/jquery-tsv/).
* For legal purposes I'll include the "NO WARRANTY EXPRESSED OR IMPLIED.
* USE AT YOUR OWN RISK.". Which, in 'layman's terms' means, by using this
* library you are accepting responsibility if it breaks your code.
*
* Legal jargon aside, I will do my best to provide a useful and stable core
* that can effectively be built on.
*
* Copyrighted 2012 by Evan Plaice.
*/
RegExp.escape= function(s) {
return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
};
(function (undefined) {
'use strict';
var $;
// to keep backwards compatibility
if (typeof jQuery !== 'undefined' && jQuery) {
$ = jQuery;
} else {
$ = {};
}
/**
* jQuery.csv.defaults
* Encapsulates the method paramater defaults for the CSV plugin module.
*/
$.csv = {
defaults: {
separator:',',
delimiter:'"',
headers:true
},
hooks: {
castToScalar: function(value, state) {
var hasDot = /\./;
if (isNaN(value)) {
return value;
} else {
if (hasDot.test(value)) {
return parseFloat(value);
} else {
var integer = parseInt(value);
if(isNaN(integer)) {
return null;
} else {
return integer;
}
}
}
}
},
parsers: {
parse: function(csv, options) {
// cache settings
var separator = options.separator;
var delimiter = options.delimiter;
// set initial state if it's missing
if(!options.state.rowNum) {
options.state.rowNum = 1;
}
if(!options.state.colNum) {
options.state.colNum = 1;
}
// clear initial state
var data = [];
var entry = [];
var state = 0;
var value = '';
var exit = false;
function endOfEntry() {
// reset the state
state = 0;
value = '';
// if 'start' hasn't been met, don't output
if(options.start && options.state.rowNum < options.start) {
// update global state
entry = [];
options.state.rowNum++;
options.state.colNum = 1;
return;
}
if(options.onParseEntry === undefined) {
// onParseEntry hook not set
data.push(entry);
} else {
var hookVal = options.onParseEntry(entry, options.state); // onParseEntry Hook
// false skips the row, configurable through a hook
if(hookVal !== false) {
data.push(hookVal);
}
}
//console.log('entry:' + entry);
// cleanup
entry = [];
// if 'end' is met, stop parsing
if(options.end && options.state.rowNum >= options.end) {
exit = true;
}
// update global state
options.state.rowNum++;
options.state.colNum = 1;
}
function endOfValue() {
if(options.onParseValue === undefined) {
// onParseValue hook not set
entry.push(value);
} else {
var hook = options.onParseValue(value, options.state); // onParseValue Hook
// false skips the row, configurable through a hook
if(hook !== false) {
entry.push(hook);
}
}
//console.log('value:' + value);
// reset the state
value = '';
state = 0;
// update global state
options.state.colNum++;
}
// escape regex-specific control chars
var escSeparator = RegExp.escape(separator);
var escDelimiter = RegExp.escape(delimiter);
// compile the regEx str using the custom delimiter/separator
var match = /(D|S|\r\n|\n|\r|[^DS\r\n]+)/;
var matchSrc = match.source;
matchSrc = matchSrc.replace(/S/g, escSeparator);
matchSrc = matchSrc.replace(/D/g, escDelimiter);
match = new RegExp(matchSrc, 'gm');
// put on your fancy pants...
// process control chars individually, use look-ahead on non-control chars
csv.replace(match, function (m0) {
if(exit) {
return;
}
switch (state) {
// the start of a value
case 0:
// null last value
if (m0 === separator) {
value += '';
endOfValue();
break;
}
// opening delimiter
if (m0 === delimiter) {
state = 1;
break;
}
// null last value
if (/^(\r\n|\n|\r)$/.test(m0)) {
endOfValue();
endOfEntry();
break;
}
// un-delimited value
value += m0;
state = 3;
break;
// delimited input
case 1:
// second delimiter? check further
if (m0 === delimiter) {
state = 2;
break;
}
// delimited data
value += m0;
state = 1;
break;
// delimiter found in delimited input
case 2:
// escaped delimiter?
if (m0 === delimiter) {
value += m0;
state = 1;
break;
}
// null value
if (m0 === separator) {
endOfValue();
break;
}
// end of entry
if (/^(\r\n|\n|\r)$/.test(m0)) {
endOfValue();
endOfEntry();
break;
}
// broken paser?
throw new Error('CSVDataError: Illegal State [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
// un-delimited input
case 3:
// null last value
if (m0 === separator) {
endOfValue();
break;
}
// end of entry
if (/^(\r\n|\n|\r)$/.test(m0)) {
endOfValue();
endOfEntry();
break;
}
if (m0 === delimiter) {
// non-compliant data
throw new Error('CSVDataError: Illegal Quote [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
}
// broken parser?
throw new Error('CSVDataError: Illegal Data [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
default:
// shenanigans
throw new Error('CSVDataError: Unknown State [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
}
//console.log('val:' + m0 + ' state:' + state);
});
// submit the last entry
// ignore null last line
if(entry.length !== 0) {
endOfValue();
endOfEntry();
}
return data;
},
// a csv-specific line splitter
splitLines: function(csv, options) {
// cache settings
var separator = options.separator;
var delimiter = options.delimiter;
// set initial state if it's missing
if(!options.state.rowNum) {
options.state.rowNum = 1;
}
// clear initial state
var entries = [];
var state = 0;
var entry = '';
var exit = false;
function endOfLine() {
// reset the state
state = 0;
// if 'start' hasn't been met, don't output
if(options.start && options.state.rowNum < options.start) {
// update global state
entry = '';
options.state.rowNum++;
return;
}
if(options.onParseEntry === undefined) {
// onParseEntry hook not set
entries.push(entry);
} else {
var hookVal = options.onParseEntry(entry, options.state); // onParseEntry Hook
// false skips the row, configurable through a hook
if(hookVal !== false) {
entries.push(hookVal);
}
}
// cleanup
entry = '';
// if 'end' is met, stop parsing
if(options.end && options.state.rowNum >= options.end) {
exit = true;
}
// update global state
options.state.rowNum++;
}
// escape regex-specific control chars
var escSeparator = RegExp.escape(separator);
var escDelimiter = RegExp.escape(delimiter);
// compile the regEx str using the custom delimiter/separator
var match = /(D|S|\n|\r|[^DS\r\n]+)/;
var matchSrc = match.source;
matchSrc = matchSrc.replace(/S/g, escSeparator);
matchSrc = matchSrc.replace(/D/g, escDelimiter);
match = new RegExp(matchSrc, 'gm');
// put on your fancy pants...
// process control chars individually, use look-ahead on non-control chars
csv.replace(match, function (m0) {
if(exit) {
return;
}
switch (state) {
// the start of a value/entry
case 0:
// null value
if (m0 === separator) {
entry += m0;
state = 0;
break;
}
// opening delimiter
if (m0 === delimiter) {
entry += m0;
state = 1;
break;
}
// end of line
if (m0 === '\n') {
endOfLine();
break;
}
// phantom carriage return
if (/^\r$/.test(m0)) {
break;
}
// un-delimit value
entry += m0;
state = 3;
break;
// delimited input
case 1:
// second delimiter? check further
if (m0 === delimiter) {
entry += m0;
state = 2;
break;
}
// delimited data
entry += m0;
state = 1;
break;
// delimiter found in delimited input
case 2:
// escaped delimiter?
var prevChar = entry.substr(entry.length - 1);
if (m0 === delimiter && prevChar === delimiter) {
entry += m0;
state = 1;
break;
}
// end of value
if (m0 === separator) {
entry += m0;
state = 0;
break;
}
// end of line
if (m0 === '\n') {
endOfLine();
break;
}
// phantom carriage return
if (m0 === '\r') {
break;
}
// broken paser?
throw new Error('CSVDataError: Illegal state [Row:' + options.state.rowNum + ']');
// un-delimited input
case 3:
// null value
if (m0 === separator) {
entry += m0;
state = 0;
break;
}
// end of line
if (m0 === '\n') {
endOfLine();
break;
}
// phantom carriage return
if (m0 === '\r') {
break;
}
// non-compliant data
if (m0 === delimiter) {
throw new Error('CSVDataError: Illegal quote [Row:' + options.state.rowNum + ']');
}
// broken parser?
throw new Error('CSVDataError: Illegal state [Row:' + options.state.rowNum + ']');
default:
// shenanigans
throw new Error('CSVDataError: Unknown state [Row:' + options.state.rowNum + ']');
}
//console.log('val:' + m0 + ' state:' + state);
});
// submit the last entry
// ignore null last line
if(entry !== '') {
endOfLine();
}
return entries;
},
// a csv entry parser
parseEntry: function(csv, options) {
// cache settings
var separator = options.separator;
var delimiter = options.delimiter;
// set initial state if it's missing
if(!options.state.rowNum) {
options.state.rowNum = 1;
}
if(!options.state.colNum) {
options.state.colNum = 1;
}
// clear initial state
var entry = [];
var state = 0;
var value = '';
function endOfValue() {
if(options.onParseValue === undefined) {
// onParseValue hook not set
entry.push(value);
} else {
var hook = options.onParseValue(value, options.state); // onParseValue Hook
// false skips the value, configurable through a hook
if(hook !== false) {
entry.push(hook);
}
}
// reset the state
value = '';
state = 0;
// update global state
options.state.colNum++;
}
// checked for a cached regEx first
if(!options.match) {
// escape regex-specific control chars
var escSeparator = RegExp.escape(separator);
var escDelimiter = RegExp.escape(delimiter);
// compile the regEx str using the custom delimiter/separator
var match = /(D|S|\n|\r|[^DS\r\n]+)/;
var matchSrc = match.source;
matchSrc = matchSrc.replace(/S/g, escSeparator);
matchSrc = matchSrc.replace(/D/g, escDelimiter);
options.match = new RegExp(matchSrc, 'gm');
}
// put on your fancy pants...
// process control chars individually, use look-ahead on non-control chars
csv.replace(options.match, function (m0) {
switch (state) {
// the start of a value
case 0:
// null last value
if (m0 === separator) {
value += '';
endOfValue();
break;
}
// opening delimiter
if (m0 === delimiter) {
state = 1;
break;
}
// skip un-delimited new-lines
if (m0 === '\n' || m0 === '\r') {
break;
}
// un-delimited value
value += m0;
state = 3;
break;
// delimited input
case 1:
// second delimiter? check further
if (m0 === delimiter) {
state = 2;
break;
}
// delimited data
value += m0;
state = 1;
break;
// delimiter found in delimited input
case 2:
// escaped delimiter?
if (m0 === delimiter) {
value += m0;
state = 1;
break;
}
// null value
if (m0 === separator) {
endOfValue();
break;
}
// skip un-delimited new-lines
if (m0 === '\n' || m0 === '\r') {
break;
}
// broken paser?
throw new Error('CSVDataError: Illegal State [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
// un-delimited input
case 3:
// null last value
if (m0 === separator) {
endOfValue();
break;
}
// skip un-delimited new-lines
if (m0 === '\n' || m0 === '\r') {
break;
}
// non-compliant data
if (m0 === delimiter) {
throw new Error('CSVDataError: Illegal Quote [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
}
// broken parser?
throw new Error('CSVDataError: Illegal Data [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
default:
// shenanigans
throw new Error('CSVDataError: Unknown State [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
}
//console.log('val:' + m0 + ' state:' + state);
});
// submit the last value
endOfValue();
return entry;
}
},
helpers: {
/**
* $.csv.helpers.collectPropertyNames(objectsArray)
* Collects all unique property names from all passed objects.
*
* @param {Array} objects Objects to collect properties from.
*
* Returns an array of property names (array will be empty,
* if objects have no own properties).
*/
collectPropertyNames: function (objects) {
var o, propName, props = [];
for (o in objects) {
for (propName in objects[o]) {
if ((objects[o].hasOwnProperty(propName)) &&
(props.indexOf(propName) < 0) &&
(typeof objects[o][propName] !== 'function')) {
props.push(propName);
}
}
}
return props;
}
},
/**
* $.csv.toArray(csv)
* Converts a CSV entry string to a javascript array.
*
* @param {Array} csv The string containing the CSV data.
* @param {Object} [options] An object containing user-defined options.
* @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
* @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
*
* This method deals with simple CSV strings only. It's useful if you only
* need to parse a single entry. If you need to parse more than one line,
* use $.csv2Array instead.
*/
toArray: function(csv, options, callback) {
options = (options !== undefined ? options : {});
var config = {};
config.callback = ((callback !== undefined && typeof(callback) === 'function') ? callback : false);
config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator;
config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter;
var state = (options.state !== undefined ? options.state : {});
// setup
options = {
delimiter: config.delimiter,
separator: config.separator,
onParseEntry: options.onParseEntry,
onParseValue: options.onParseValue,
state: state
};
var entry = $.csv.parsers.parseEntry(csv, options);
// push the value to a callback if one is defined
if(!config.callback) {
return entry;
} else {
config.callback('', entry);
}
},
/**
* $.csv.toArrays(csv)
* Converts a CSV string to a javascript array.
*
* @param {String} csv The string containing the raw CSV data.
* @param {Object} [options] An object containing user-defined options.
* @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
* @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
*
* This method deals with multi-line CSV. The breakdown is simple. The first
* dimension of the array represents the line (or entry/row) while the second
* dimension contains the values (or values/columns).
*/
toArrays: function(csv, options, callback) {
options = (options !== undefined ? options : {});
var config = {};
config.callback = ((callback !== undefined && typeof(callback) === 'function') ? callback : false);
config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator;
config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter;
// setup
var data = [];
options = {
delimiter: config.delimiter,
separator: config.separator,
onPreParse: options.onPreParse,
onParseEntry: options.onParseEntry,
onParseValue: options.onParseValue,
onPostParse: options.onPostParse,
start: options.start,
end: options.end,
state: {
rowNum: 1,
colNum: 1
}
};
// onPreParse hook
if(options.onPreParse !== undefined) {
options.onPreParse(csv, options.state);
}
// parse the data
data = $.csv.parsers.parse(csv, options);
// onPostParse hook
if(options.onPostParse !== undefined) {
options.onPostParse(data, options.state);
}
// push the value to a callback if one is defined
if(!config.callback) {
return data;
} else {
config.callback('', data);
}
},
/**
* $.csv.toObjects(csv)
* Converts a CSV string to a javascript object.
* @param {String} csv The string containing the raw CSV data.
* @param {Object} [options] An object containing user-defined options.
* @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
* @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
* @param {Boolean} [headers] Indicates whether the data contains a header line. Defaults to true.
*
* This method deals with multi-line CSV strings. Where the headers line is
* used as the key for each value per entry.
*/
toObjects: function(csv, options, callback) {
options = (options !== undefined ? options : {});
var config = {};
config.callback = ((callback !== undefined && typeof(callback) === 'function') ? callback : false);
config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator;
config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter;
config.headers = 'headers' in options ? options.headers : $.csv.defaults.headers;
options.start = 'start' in options ? options.start : 1;
// account for headers
if(config.headers) {
options.start++;
}
if(options.end && config.headers) {
options.end++;
}
// setup
var lines = [];
var data = [];
options = {
delimiter: config.delimiter,
separator: config.separator,
onPreParse: options.onPreParse,
onParseEntry: options.onParseEntry,
onParseValue: options.onParseValue,
onPostParse: options.onPostParse,
start: options.start,
end: options.end,
state: {
rowNum: 1,
colNum: 1
},
match: false,
transform: options.transform
};
// fetch the headers
var headerOptions = {
delimiter: config.delimiter,
separator: config.separator,
start: 1,
end: 1,
state: {
rowNum:1,
colNum:1
}
};
// onPreParse hook
if(options.onPreParse !== undefined) {
options.onPreParse(csv, options.state);
}
// parse the csv
var headerLine = $.csv.parsers.splitLines(csv, headerOptions);
var headers = $.csv.toArray(headerLine[0], options);
// fetch the data
lines = $.csv.parsers.splitLines(csv, options);
// reset the state for re-use
options.state.colNum = 1;
if(headers){
options.state.rowNum = 2;
} else {
options.state.rowNum = 1;
}
// convert data to objects
for(var i=0, len=lines.length; i<len; i++) {
var entry = $.csv.toArray(lines[i], options);
var object = {};
for(var j=0; j <headers.length; j++) {
object[headers[j]] = entry[j];
}
if (options.transform !== undefined) {
data.push(options.transform.call(undefined, object));
} else {
data.push(object);
}
// update row state
options.state.rowNum++;
}
// onPostParse hook
if(options.onPostParse !== undefined) {
options.onPostParse(data, options.state);
}
// push the value to a callback if one is defined
if(!config.callback) {
return data;
} else {
config.callback('', data);
}
},
/**
* $.csv.fromArrays(arrays)
* Converts a javascript array to a CSV String.
*
* @param {Array} arrays An array containing an array of CSV entries.
* @param {Object} [options] An object containing user-defined options.
* @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
* @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
*
* This method generates a CSV file from an array of arrays (representing entries).
*/
fromArrays: function(arrays, options, callback) {
options = (options !== undefined ? options : {});
var config = {};
config.callback = ((callback !== undefined && typeof(callback) === 'function') ? callback : false);
config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator;
config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter;
var output = '',
line,
lineValues,
i, j;
for (i = 0; i < arrays.length; i++) {
line = arrays[i];
lineValues = [];
for (j = 0; j < line.length; j++) {
var strValue = (line[j] === undefined || line[j] === null) ? '' : line[j].toString();
if (strValue.indexOf(config.delimiter) > -1) {
strValue = strValue.replace(config.delimiter, config.delimiter + config.delimiter);
}
var escMatcher = '\n|\r|S|D';
escMatcher = escMatcher.replace('S', config.separator);
escMatcher = escMatcher.replace('D', config.delimiter);
if (strValue.search(escMatcher) > -1) {
strValue = config.delimiter + strValue + config.delimiter;
}
lineValues.push(strValue);
}
output += lineValues.join(config.separator) + '\r\n';
}
// push the value to a callback if one is defined
if(!config.callback) {
return output;
} else {
config.callback('', output);
}
},
/**
* $.csv.fromObjects(objects)
* Converts a javascript dictionary to a CSV string.
*
* @param {Object} objects An array of objects containing the data.
* @param {Object} [options] An object containing user-defined options.
* @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
* @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
* @param {Character} [sortOrder] Sort order of columns (named after
* object properties). Use 'alpha' for alphabetic. Default is 'declare',
* which means, that properties will _probably_ appear in order they were
* declared for the object. But without any guarantee.
* @param {Character or Array} [manualOrder] Manually order columns. May be
* a strin in a same csv format as an output or an array of header names
* (array items won't be parsed). All the properties, not present in
* `manualOrder` will be appended to the end in accordance with `sortOrder`
* option. So the `manualOrder` always takes preference, if present.
*
* This method generates a CSV file from an array of objects (name:value pairs).
* It starts by detecting the headers and adding them as the first line of
* the CSV file, followed by a structured dump of the data.
*/
fromObjects: function(objects, options, callback) {
options = (options !== undefined ? options : {});
var config = {};
config.callback = ((callback !== undefined && typeof(callback) === 'function') ? callback : false);
config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator;
config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter;
config.headers = 'headers' in options ? options.headers : $.csv.defaults.headers;
config.sortOrder = 'sortOrder' in options ? options.sortOrder : 'declare';
config.manualOrder = 'manualOrder' in options ? options.manualOrder : [];
config.transform = options.transform;
if (typeof config.manualOrder === 'string') {
config.manualOrder = $.csv.toArray(config.manualOrder, config);
}
if (config.transform !== undefined) {
var origObjects = objects;
objects = [];
var i;
for (i = 0; i < origObjects.length; i++) {
objects.push(config.transform.call(undefined, origObjects[i]));
}
}
var props = $.csv.helpers.collectPropertyNames(objects);
if (config.sortOrder === 'alpha') {
props.sort();
} // else {} - nothing to do for 'declare' order
if (config.manualOrder.length > 0) {
var propsManual = [].concat(config.manualOrder);
var p;
for (p = 0; p < props.length; p++) {
if (propsManual.indexOf( props[p] ) < 0) {
propsManual.push( props[p] );
}
}
props = propsManual;
}
var o, p, line, output = [], propName;
if (config.headers) {
output.push(props);
}
for (o = 0; o < objects.length; o++) {
line = [];
for (p = 0; p < props.length; p++) {
propName = props[p];
if (propName in objects[o] && typeof objects[o][propName] !== 'function') {
line.push(objects[o][propName]);
} else {
line.push('');
}
}
output.push(line);
}
// push the value to a callback if one is defined
return $.csv.fromArrays(output, options, config.callback);
}
};
// Maintenance code to maintain backward-compatibility
// Will be removed in release 1.0
$.csvEntry2Array = $.csv.toArray;
$.csv2Array = $.csv.toArrays;
$.csv2Dictionary = $.csv.toObjects;
// CommonJS module is defined
if (typeof module !== 'undefined' && module.exports) {
module.exports = $.csv;
}
}).call( this );
</script>
<script>
$(document).ready(function() {
if(isAPIAvailable()) {
$('#files').bind('change', handleFileSelect);
}
});
function isAPIAvailable() {
// Check for the various File API support.
if (window.File && window.FileReader && window.FileList && window.Blob) {
// Great success! All the File APIs are supported.
return true;
} else {
// source: File API availability - http://caniuse.com/#feat=fileapi
// source: <output> availability - http://html5doctor.com/the-output-element/
document.writeln('The HTML5 APIs used in this form are only available in the following browsers:<br />');
// 6.0 File API & 13.0 <output>
document.writeln(' - Google Chrome: 13.0 or later<br />');
// 3.6 File API & 6.0 <output>
document.writeln(' - Mozilla Firefox: 6.0 or later<br />');
// 10.0 File API & 10.0 <output>
document.writeln(' - Internet Explorer: Not supported (partial support expected in 10.0)<br />');
// ? File API & 5.1 <output>
document.writeln(' - Safari: Not supported<br />');
// ? File API & 9.2 <output>
document.writeln(' - Opera: Not supported');
return false;
}
}
function handleFileSelect(evt) {
var files = evt.target.files; // FileList object
var file = files[0];
// read the file metadata
var output = ''
output += '<span style="font-weight:bold;">' + escape(file.name) + '</span><br />\n';
output += ' - FileType: ' + (file.type || 'n/a') + '<br />\n';
output += ' - FileSize: ' + file.size + ' bytes<br />\n';
output += ' - LastModified: ' + (file.lastModifiedDate ? file.lastModifiedDate.toLocaleDateString() : 'n/a') + '<br />\n';
// read the file contents
printTextArea(file);
// post the results
$('#list').append(output);
}
function printTextArea(file) {
var reader = new FileReader();
reader.readAsText(file);
reader.onload = function(event){
var csv = event.target.result;
var data = $.csv.toArrays(csv);
var mystr = "";
var str2 = "";
for(var row in data) {
if (!isNaN(Number(data[row][0])))
{
// mystr = mystr.concat((Number(data[row][0]).toFixed(3)).toString());
mystr = mystr.concat(data[row][0]);
mystr = mystr.concat("\n");
}
if (!isNaN(Number(data[row][1])))
{
// str2 = str2.concat((Number(data[row][1]).toFixed(3)).toString());
str2 = str2.concat(data[row][1]);
str2 = str2.concat("\n");
}
}
mystr = mystr.substring(0, mystr.length - 1)
str2 = str2.substring(0, str2.length - 1)
document.getElementById("Real_inp").value = mystr;
document.getElementById("Imag_inp").value = str2;
};
reader.onerror = function(){ alert('Unable to read ' + file.fileName); };
}
</script>
<!--[if IE ]>
<script type="text/javascript" src="https://firebasestorage.googleapis.com/v0/b/js-static-assets.appspot.com/o/excanvas_min.js?alt=media&token=9dd78ad2-f09b-45dd-8a09-8db2b2546d1d"></script>
<![endif]-->
<script type="text/javascript" src="https://firebasestorage.googleapis.com/v0/b/js-static-assets.appspot.com/o/jquery_flot.js?alt=media&token=bde69a50-3df9-4794-b7d8-dd4e840472cd"></script>
<script type="text/javascript" src="https://firebasestorage.googleapis.com/v0/b/js-static-assets.appspot.com/o/jquery_flot_selection.js?alt=media&token=7904aab7-3a10-4ea1-818a-12c821090771"></script>
<script type="text/javascript" id="MathJax-script" async
src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js">
</script>
<style>
textarea {
overflow: auto;
overflow-y: scroll;
font-size: 18px;
background-color: black;
color: white;
resize: none; /* disable resizing */
}
</style>
<p style="text-align:justify">This blog post implements a Fast Fourier Transform (FFT) or an Inverse Fast Fourier Transform (IFFT) on a complex input, dependent on the checkbox setting below. You can specify the sampling frequency in arbitrary units (e.g. Hz) in the appropriately labelled text area below (a default of 100 is used).</p>
<p style="text-align:justify">For discrete time-domain input samples \(x[n]\) for \(n={0,1,2,..,N-1}\) the FFT (at bin \(k\) for \(k={0,1,2,..,N-1})\) is defined by equation</p>
<p style="text-align:center">\(X[k]=\sum_{n=0}^{N-1}x[n]exp(-j2\pi\frac{nk}{N})\)</p>
<p style="text-align:justify">while for discrete frequency-domain input bins \(X[k]\) for \(k={0,1,2,..,N-1}\) the IFFT (at time index \(n\) for \(n={0,1,2,..,N-1}\)) is defined by equation</p>
<p style="text-align:center"> \(x[n]=\frac{1}{N}\sum_{k=0}^{N-1}X[k]exp(+j2\pi\frac{nk}{N})\) </p>
<p style="text-align:justify">where \(N=2^g\) for integer \(g\).</p>
<p style="text-align:justify">Please enter the numbers in the text areas below - one number per line, for each of the Real and Imaginary input textareas (the textareas have already been filled in with some numbers for illustration purposes). There must be no new line after the last number.</p>
<p style="text-align:justify">Note that if the input is real only, the imaginary input textarea can be left empty (rather than having to fill it with the same number of zeros as there are real inputs, which can be a bit more cumbersome). Conversely, if the input is imaginary only, the real input textarea can be left empty (rather than having to fill it with the same number of zeros as there are imaginary inputs).</p>
<p style="text-align:justify">Alternatively you can choose to load a CSV file, which must be either a single column of numbers (for a real only input) or two comma-separated columns of numbers - the first line can be a comment line, starting with the character #.</p>
<p style="text-align:justify">To perform the FFT/IFFT, please press the button labelled "Perform FFT/IFFT" below - the results will populate the textareas below labelled "Real Output" and "Imaginary Output", as well as a textarea at the bottom that will contain the real and imaginary output joined using a comma - this is suitable for copying and pasting the results to a CSV file.</p>
<p style="text-align:justify">In addition, graphical outputs of the FFT are displayed below. These include a graph of FFT magnitude (using the drop-down menu below, you can select the units of this parameter) and a graph of the phase response (units of either radian or degrees also selectable by a drop-down menu below). When a unit is altered, you would need to perform the FFT again by pressing the calculate button for the changes to take effect.</p>
<p style="text-align:justify">At the bottom of this blog post, the Decimation In Time (DIT) twiddle Q factors will be displayed, as defined in <a href="https://www.dsprelated.com/showarticle/107.php" target="_blank">https://www.dsprelated.com/showarticle/107.php</a>. For an $N$ point FFT, there are $log_2(N)$ stages, and for each stage there are $N/2$ twiddle factors that do not equal the $-1$ term - these are the ones that will be printed.</p>
<p style="text-align:justify">If you change inputs to a smaller number of samples, please press the calculate button twice for the results to take effect. Alternatively, you can simply reload the page, then fill in the input textareas. </p>
<p style="text-align:justify">As the FFT operates on inputs that contain an integer power of two number of samples, the input data length will be augmented by zero padding the real and imaginary data samples to satisfy this condition were this not to hold.</p>
<p style="text-align:justify">You can find an FFT based Power Spectral Density (PSD) Estimator <a href="https://www.dsprelated.com/showarticle/107.php" target="_blank">here</a>.</p>
<br>
<div id=inputs class=clearfix>
<input type=file id=files name=files[] single />
</div>
<br>
<div style="width:350px;float: left;">
Real Input                                         Imaginary Input
</div>
<br>
<br>
<textarea rows="10" cols="20" id ="Real_inp">
</textarea>
<textarea rows="10" cols="20" id ="Imag_inp">
</textarea>
<br>
<br>
<div style="width:350px;float: left;">
<input name="b1" onclick="clear_real_inp()" style="margin-left: 0px;color: white; background: blue;" type="Button" value="Clear Real Input" />
                         
<input name="b1" onclick="clear_imag_inp()" style="margin-left: 0px;color: white; background: blue;" type="Button" value="Clear Imag Input" />
</div>
<br>
<p>
<label>Select FFT Magnitude output units for graphical display</label>
<select id = "myList">
<option value = "1">Complex</option>
<option value = "2">Magnitude</option>
<option value = "3">Magnitude Squared</option>
<option value = "4">dB</option>
</select>
</p>
<p>
<label>Select FFT Phase output Units for graphical display</label>
<select id = "myPhase">
<option value = "1">Radians</option>
<option value = "2">Degrees</option>
</select>
</p>
<p><label><input id="fft_dir" type="checkbox" />Check for IFFT - uncheck for FFT.</label></p>
Sampling frequency:-
<br>
<textarea rows="1" cols="10" id ="sampling_freq">
</textarea>
<br>
<br>
<input name="b1" onclick="calc_fft()" style="margin-left: 0px;height:50px; width:150px; color: white; background: blue; font-size: 16px; border-radius: 10px;" type="Button" value="Perform FFT/IFFT"/>
<br><br>
<p id="fft_size">FFT size...</p>
<br>
<br>
<div style="width:350px;float: left;">
Real Output                                 Imaginary Output
</div>
<br>
<br>
<textarea rows="10" cols="20" id="Real_out">
</textarea>
<textarea rows="10" cols="20" id="Imag_out">
</textarea>
<br>
<br>
Real and Imaginary output concatenated on each line:-
<br>
<textarea rows="10" cols="25" id ="Real_Imag_out">
</textarea>
<br>
<table style="text-align: center;">
<tbody>
<tr>
<td></td>
<td>FFT Magnitude</td>
<td></td>
</tr>
<tr></tr>
<tr>
<td><p id="ylabela">Complex (Linear)</p></td>
<td><div id='chart1' style='height:300px;width:500px;'>
</div>
</td>
</tr>
<td></td>
</tr>
<tr>
<td></td>
<td><p id="xlabela">Frequency</p></td>
<td></td>
</tr>
<tr>
<td><p id="ylabelb">Complex (Linear)</p></td>
<td><div id='overview' style='height:150px;width:500px;'>
</div>
</td>
</tr>
<tr>
<td></td>
<td><p id="xlabelb">Frequency</p></td>
<td></td>
</tr>
</tbody></table>
<br><br>
<br>
<table style="text-align: center;">
<tbody>
<tr>
<td></td>
<td>FFT Phase Response</td>
<td></td>
</tr>
<tr></tr>
<tr>
<td><p id="yphlabela">Radians</td>
<td><div id='chart2' style='height:300px;width:500px;'>
</div>
</td>
</tr>
<td></td>
</tr>
<tr>
<td></td>
<td><p id="xlabela">Frequency</p></td>
<td></td>
</tr>
<tr>
<td><p id="yphlabelb">Radians</td>
<td><div id='overview2' style='height:150px;width:500px;'>
</div>
</td>
</tr>
<tr>
<td></td>
<td><p id="xlabelb">Frequency</p></td>
<td></td>
</tr>
</tbody></table>
<br>
<p id="twiddle">FFT twiddle factors...</p>
<script language="JavaScript">
var global_data = new Array();
global_data = [ [0,0], [1,1], [2,2], [3,3], [4,4] ];
var global_data2 = new Array();
global_data2 = [ [0,0], [1,1], [2,2], [3,3], [4,4] ];
var optionsov = {legend:{show:false},selection:{mode:"xy"}};
var options = {legend:{position:"nw"},selection:{mode:"xy"}};
$(function () {
var ser_start = {
label: "",
data: [ [0,0], [1,1], [2,2], [3,3], [4,4] ],
color: "#ff0000",
points: { show: false },
lines: { show: true },
bars: { show: false },
yaxis: 1,
selection: { mode: "xy" }};
data = [ser_start];
$.plot($("#chart1"), data,options);
$.plot($("#overview"), data,optionsov);
// Zoom?
$("#chart1").bind("plotselected", function (event, ranges) {
// clamp the zooming to prevent eternal zoom
if (ranges.xaxis.to - ranges.xaxis.from < 0.00001)
ranges.xaxis.to = ranges.xaxis.from + 0.00001;
if (ranges.yaxis.to - ranges.yaxis.from < 0.00001)
ranges.yaxis.to = ranges.yaxis.from + 0.00001;
// do the zooming
plot = $.plot($("#chart1"), global_data,
$.extend(true, {}, options, {
xaxis: { min: ranges.xaxis.from, max: ranges.xaxis.to },
yaxis: { min: ranges.yaxis.from, max: ranges.yaxis.to }
}));
// don't fire event on the overview to prevent eternal loop
var overview = $.plot($("#overview"), global_data,optionsov);
overview.setSelection(ranges, true);
});
$("#overview").bind("plotselected", function (event, ranges) {
var chart1 = $.plot($("#chart1"), global_data);
chart1.setSelection(ranges);
});
$.plot($("#chart2"), data,options);
$.plot($("#overview2"), data,optionsov);
// Zoom?
$("#chart2").bind("plotselected", function (event, ranges) {
// clamp the zooming to prevent eternal zoom
if (ranges.xaxis.to - ranges.xaxis.from < 0.00001)
ranges.xaxis.to = ranges.xaxis.from + 0.00001;
if (ranges.yaxis.to - ranges.yaxis.from < 0.00001)
ranges.yaxis.to = ranges.yaxis.from + 0.00001;
// do the zooming
plot = $.plot($("#chart2"), global_data2,
$.extend(true, {}, options, {
xaxis: { min: ranges.xaxis.from, max: ranges.xaxis.to },
yaxis: { min: ranges.yaxis.from, max: ranges.yaxis.to }
}));
// don't fire event on the overview to prevent eternal loop
var overview2 = $.plot($("#overview2"), global_data2,optionsov);
overview2.setSelection(ranges, true);
});
$("#overview2").bind("plotselected", function (event, ranges) {
var chart2 = $.plot($("#chart2"), global_data);
chart2.setSelection(ranges);
});
});
window.onload = set_inputs();
var x = new Array();
var y = new Array();
function set_inputs() {
var strtmp = 1 + "\n" + 2 + "\n" + 3 + "\n" + 4;
document.getElementById("Real_inp").value = strtmp;
strtmp = 0 + "\n" + 0 + "\n" + 0 + "\n" + 0;
document.getElementById("Imag_inp").value = strtmp;
document.getElementById("sampling_freq").value = "100";
}
function clear_real_inp() {
document.getElementById("Real_inp").value = "";
}
function clear_imag_inp() {
document.getElementById("Imag_inp").value = "";
}
function calc_fft() {
var re_inp = document.getElementById("Real_inp").value;
var re_arr = new Array();
var im_inp = document.getElementById("Imag_inp").value;
var im_arr = new Array();
var debug_str = '';
var tmp_str = '';
re_arr = re_inp.split("\n");
im_arr = im_inp.split("\n");
var re_num = new Array();
var im_num = new Array();
var im_arr_null = 0;
var re_arr_null = 0;
if (re_arr.length != im_arr.length)
{
if (im_arr.length == 1 & im_arr[0] == '' & re_arr.length > 1)
{
im_arr_null = 1;
}
else if (re_arr.length == 1 & re_arr[0] == '' & im_arr.length > 1)
{
re_arr_null = 1;
}
else
{
alert("Real and Imaginary components must have either same number of elements, or either the Imaginary or the Real component must have no elements!");
return;
}
}
var data_size = 0;
if (im_arr_null == 1)
data_size = re_arr.length;
else if (re_arr_null == 1)
data_size = im_arr.length;
else
data_size = re_arr.length;
for (k = 0; k < data_size; k++)
{
if (re_arr_null == 0)
re_num[k] = Number(re_arr[k]);
else
re_num[k] = 0;
if (im_arr_null == 0)
im_num[k] = Number(im_arr[k]);
else
im_num[k] = 0;
if (re_arr_null == 0)
x[k] = Number(re_arr[k]);
else
x[k] = 0;
if (im_arr_null == 0)
y[k] = Number(im_arr[k]);
else
y[k] = 0;
tmp_str = re_arr[k] + ", " + im_arr[k] + ",<br>";
debug_str = debug_str.concat(tmp_str);
}
var m = Math.ceil(Math.log(x.length)/Math.log(2));
var Nfft = (1 << m);
var fft_size_str =""
fft_size_str += "FFT size is " + Nfft + " bins"
fft_size_str = fft_size_str.fontcolor("white")
document.getElementById("fft_size").innerHTML = fft_size_str
if (Nfft > data_size)
{
for (k = data_size; k < Nfft; k++)
{
x[k] = 0;
y[k] = 0;
re_num[k] = 0;
im_num[k] = 0;
}
}
var ifft_on = $("#fft_dir").attr("checked");
if (ifft_on)
FFT(-1,m,x,y);
else
FFT(1,m,x,y);
var re_out_str = "";
var im_out_str = "";
var re_im_out_str = "";
for (k = 0; k < Nfft; k++)
{
if (k < x.length - 1)
tmp_str = x[k].toFixed(6) + "\n";
else
tmp_str = x[k].toFixed(6);
re_out_str = re_out_str.concat(tmp_str);
if (k < x.length - 1)
tmp_str = y[k].toFixed(6) + "\n";
else
tmp_str = y[k].toFixed(6);
im_out_str = im_out_str.concat(tmp_str);
if (k < x.length - 1)
tmp_str = x[k].toFixed(6) + "," + y[k].toFixed(6) + "\n";
else
tmp_str = x[k].toFixed(6) + "," + y[k].toFixed(6);
re_im_out_str = re_im_out_str.concat(tmp_str);
}
document.getElementById("Real_out").value = re_out_str;
document.getElementById("Imag_out").value = im_out_str;
document.getElementById("Real_Imag_out").value = re_im_out_str;
var magn = new Array();
var magn_sq = new Array();
var magn_db = new Array();
var datar = new Array();
var datai = new Array();
var phas = new Array();
var fsamp = Number(document.getElementById("sampling_freq").value)
for (k = 0; k < Nfft; k++) {
magn[k] = [(k * fsamp)/(Nfft + 0.0),Math.sqrt((x[k]*x[k]) + (y[k]*y[k]))];
magn_sq[k] = [(k * fsamp)/(Nfft + 0.0),(x[k]*x[k]) + (y[k]*y[k])];
magn_db[k]= [(k * fsamp)/(Nfft + 0.0),10 * ( Math.log((x[k]*x[k]) + (y[k]*y[k]) + 1e-16)/Math.log(10))];
datar[k] = [(k * fsamp)/(Nfft + 0.0),x[k]];
datai[k] = [(k * fsamp)/(Nfft + 0.0),y[k]];
}
for (k = 0; k < Nfft; k++) {
var fft_angle = Math.atan2(y[k],x[k]);
if (document.getElementById("myPhase").value == "2")
fft_angle = 180.0 * (fft_angle/Math.PI);
phas[k] = [(k * fsamp)/(Nfft + 0.0),fft_angle];
}
plot_data = magn;
var Nfft_div_2 = Nfft/2;
twiddle_str = "Decimation in Time Q twiddle factors for half the values that do not equal -1:-"
// Twiddle factor calculation
for (P=1; P <= m; P++)
{
twiddle_str += "<br>FFT Stage " + P;
twiddle_str += "<br>"
for (k=0; k < Nfft_div_2; k++)
{
twiddle_res = calc_twiddle(k,P,Nfft,m)
twiddle_str += twiddle_res
if (k < Nfft_div_2 -1)
{
twiddle_str += ", "
}
}
twiddle_str += "<br>"
}
twiddle_str = twiddle_str.fontcolor("white")
document.getElementById("twiddle").innerHTML = twiddle_str
function calc_twiddle(k,P,Nfft,m)
{
bitwidth = m - 1
floor_val = Math.floor((k * Math.pow(2.0,P))/(Nfft + 0.0))
num_str = floor_val.toString(2)
num_str_pad = pad(num_str, bitwidth)
num_str_pad_rev = ""
for (i=0; i < num_str_pad.length; i++)
{
num_str_pad_rev += num_str_pad[num_str_pad.length - 1 - i]
}
result = parseInt(num_str_pad_rev,2)
return result
}
function pad(num_str, size) {
while (num_str.length < size) num_str = "0" + num_str;
return num_str;
}
if (document.getElementById("myPhase").value == "1")
{
document.getElementById("yphlabela").innerHTML = "Radians";
document.getElementById("yphlabelb").innerHTML = "Radians";
plot_data = datar;
}
else if (document.getElementById("myPhase").value == "2")
{
document.getElementById("yphlabela").innerHTML = "Degrees";
document.getElementById("yphlabelb").innerHTML = "Degrees";
plot_data = magn;
}
if (document.getElementById("myList").value == "1")
{
document.getElementById("ylabela").innerHTML = "Complex (Linear)";
document.getElementById("ylabelb").innerHTML = "Complex (Linear)";
plot_data = datar;
}
else if (document.getElementById("myList").value == "2")
{
document.getElementById("ylabela").innerHTML = "Mag";
document.getElementById("ylabelb").innerHTML = "Mag";
plot_data = magn;
}
else if (document.getElementById("myList").value == "3")
{
document.getElementById("ylabela").innerHTML = "Mag Squared";
document.getElementById("ylabelb").innerHTML = "Mag Squared";
plot_data = magn_sq;
}
else if (document.getElementById("myList").value == "4")
{
document.getElementById("ylabela").innerHTML = "dB";
document.getElementById("ylabelb").innerHTML = "dB";
plot_data = magn_db;
}
var rlabel = "";
if (document.getElementById("myList").value == "1")
rlabel = "Real";
var ser_start = {
label: rlabel,
data: plot_data,
color: "#ff0000",
points: { show: false },
lines: { show: true },
bars: { show: false },
yaxis: 1,
selection: { mode: "xy" }};
var ser_start_im = {
label: "Imag",
data: datai,
color: "#0000ff",
points: { show: false },
lines: { show: true },
bars: { show: false },
yaxis: 1,
selection: { mode: "xy" }};
if (document.getElementById("myList").value == "1")
data = [ser_start,ser_start_im];
else
data = [ser_start];
global_data = data;
$.plot($("#chart1"), data,options);
$.plot($("#overview"), data,options);
var ser_start2 = {
label: "",
data: phas,
color: "#ff0000",
points: { show: false },
lines: { show: true },
bars: { show: false },
yaxis: 1,
selection: { mode: "xy" }};
data2 = [ser_start2];
global_data2 = data2;
$.plot($("#chart2"), data2,options);
$.plot($("#overview2"), data2,options);
// Reset the values of x and y
x = [];
y = [];
m = Math.ceil(Math.log(document.getElementById("Real_inp").value.split("\n").length)/Math.log(2));
Nfft = (1 << m);
for (k = 0; k < Nfft; k++)
{
x[k] = re_num[k];
y[k] = im_num[k];
}
}
/*
This computes an in-place complex-to-complex FFT
x and y are the real and imaginary arrays of 2^m points.
dir = 1 gives forward transform
dir = -1 gives reverse transform
*/
function FFT(dir,m,x,y)
{
/* Calculate the number of points */
n = 1;
for (i=0;i<m;i++)
n *= 2;
/* Do the bit reversal */
i2 = n >> 1;
j = 0;
for (i=0;i<n-1;i++) {
if (i < j) {
tx = x[i];
ty = y[i];
x[i] = x[j];
y[i] = y[j];
x[j] = tx;
y[j] = ty;
}
k = i2;
while (k <= j) {
j -= k;
k >>= 1;
}
j += k;
}
/* Compute the FFT */
c1 = -1.0;
c2 = 0.0;
l2 = 1;
for (l=0;l<m;l++) {
l1 = l2;
l2 <<= 1;
u1 = 1.0;
u2 = 0.0;
for (j=0;j<l1;j++) {
for (i=j;i<n;i+=l2) {
i1 = i + l1;
t1 = u1 * x[i1] - u2 * y[i1];
t2 = u1 * y[i1] + u2 * x[i1];
x[i1] = x[i] - t1;
y[i1] = y[i] - t2;
x[i] += t1;
y[i] += t2;
}
z = u1 * c1 - u2 * c2;
u2 = u1 * c2 + u2 * c1;
u1 = z;
}
c2 = Math.sqrt((1.0 - c1) / 2.0);
if (dir == 1)
c2 = -c2;
c1 = Math.sqrt((1.0 + c1) / 2.0);
}
/* Scaling for reverse transform */
if (dir == -1) {
for (i=0;i<n;i++) {
x[i] /= n;
y[i] /= n;
}
}
return(1);
}
</script>
Alijah Ahmedhttp://www.blogger.com/profile/01151589526613233918noreply@blogger.comtag:blogger.com,1999:blog-8441309013858171051.post-49530848396776002812013-12-20T09:47:00.001-08:002016-08-29T04:51:40.523-07:00Interpreting the two-way ANOVA test <!--[if IE ]>
<script type="text/javascript" src="https://firebasestorage.googleapis.com/v0/b/js-static-assets.appspot.com/o/excanvas_min.js?alt=media&token=9dd78ad2-f09b-45dd-8a09-8db2b2546d1d"></script>
<![endif]-->
<script type="text/javascript" src="https://firebasestorage.googleapis.com/v0/b/js-static-assets.appspot.com/o/jquery_flot.js?alt=media&token=bde69a50-3df9-4794-b7d8-dd4e840472cd"></script>
<p style="text-align:justify"> In this blog post, I will try to explain how to interpret the two-way ANOVA test using a simple example.</p>
<p style="text-align:justify"> Suppose we were testing the yield of a crop plant based on seed types and which field they were planted in, so we have two factors: Seed Type, and Field Type. The yield could be the number of grains in a plant. For the first factor, let us assume we have three seed types, which we call Seed 1, Seed 2 and Seed 3. As for the second factor, let us assume we have two field types, which we denote as Field 1 and Field 2. For each field type and seed type, let us assume we have three samples (also known as replicates). We can represent the results in a table as below, where entries $a_{ij}(k)$ are the number of grains in a plant.</p>
<br>
<table border="1" style="width: 100%">
<tbody>
<tr>
<td></td>
<td>Seed 1 </td>
<td>Seed 2 </td>
<td>seed 3 </td>
</tr>
<tr>
<td>Field 1 </td>
<td>$a_{11}(1),a_{11}(2),a_{11}(3)$</td>
<td>$a_{12}(1),a_{12}(2),a_{12}(3)$</td>
<td>$a_{13}(1),a_{13}(2),a_{13}(3)$</td>
</tr>
<tr>
<td>Field 2 </td>
<td>$a_{21}(1),a_{21}(2),a_{21}(3)$</td>
<td>$a_{22}(1),a_{22}(2),a_{22}(3)$</td>
<td>$a_{23}(1),a_{23}(2),a_{23}(3)$</td>
</tr>
</tbody>
</table>
<br>
<p style="text-align:justify"> Now, in a two-way ANOVA test, we calculate the F statistic for factor 1, factor 2 and the interaction. Based on the F-statistic, we calculate the p-value for factor 1, factor 2 and the interaction. What do we mean by these values?</p>
<p style="text-align:justify"> A very low p-value for factor 1 (Seed Type) (i.e. the result is significant for the first factor), arises when the the mean values of the seed yields are different for each Seed Type. Suppose this is indeed the case, where Seed 3 has the highest yield followed by Seed 2, then Seed 1. The mean values of the yield for field 1 could look as follows.</p>
<table style="text-align: center;">
<tbody>
<tr>
<td></td>
<td>Replicate means for field 1</td>
<td></td>
</tr>
<tr>
<td>Seed yield means</td>
<td><div id='chart1' style='height:300px;width:500px;'>
</div>
</td>
<td></td>
</tr>
<tr>
<td></td>
<td>Seed type number</td>
<td></td>
</tr>
</tbody></table>
<p style="text-align:justify"> Now let us look at the second factor, Field type, and suppose the p-value for this is very low as well (i.e. the result is significant for the second factor). This tells us that the plant yields are different for different field types, and suppose that Field 2 has the lower yield plants, as it has poorer irrigation than field 1. Supposing we plotted the means of the three seed types for the two fields, and we obtain the result below</p>
<table style="text-align: center;">
<tbody>
<tr>
<td></td>
<td>Replicate means for fields 1 and 2</td>
<td></td>
</tr>
<tr>
<td>Seed yield means</td>
<td><div id='chart2' style='height:300px;width:500px;'>
</div>
</td>
<td></td>
</tr>
<tr>
<td></td>
<td>Seed type number</td>
<td></td>
</tr>
</tbody></table>
<p style="text-align:justify">Examining the plot above, we are in a position to describe what p-value the interaction will take. Note that the mean plots are parallel - the difference in means for all three seed types between field 1 and field 2 are the same. The p-value for interaction will thus tend to 1, and so there will be no significant interaction.</p>
<p style="text-align:justify">A worthwhile question to pose would be what if there was significant interaction? In such a scenario, the difference in yields for each of the seed type between field 1 and field 2 would not be the same. For example, the difference in means for Seed type 3 could be much lower, resulting in the plot below. This means that there is interaction between seed type and field type - seed 3 appears to more resistant to lower water supply, for example. </p>
<table style="text-align: center;">
<tbody>
<tr>
<td></td>
<td>Replicate means for fields 1 and 2</td>
<td></td>
</tr>
<tr>
<td>Seed yield means</td>
<td><div id='chart3' style='height:300px;width:500px;'>
</div>
</td>
<td></td>
</tr>
<tr>
<td></td>
<td>Seed type number</td>
<td></td>
</tr>
</tbody></table>
<script language="javascript">
var options = {legend:{position:"nw"},selection:{mode:"xy"}};
var series1 = {
label: "",
data: [ [1,100], [2,200], [3,300] ],
color: "#0000ff",
points: { show: false },
lines: { show: true },
bars: { show: false }};
data = [series1];
$.plot($("#chart1"), data);
var ser1 = {
label: "Field 1",
data: [ [1,100], [2,200], [3,300] ],
color: "#0000ff",
points: { show: false },
lines: { show: true },
bars: { show: false },
yaxis: 1
};
var ser2 = {
label: "Field 2",
data: [ [1,25], [2,125], [3,225] ],
color: "#ff0000",
points: { show: false },
lines: { show: true},
bars: { show: false },
yaxis: 1
};
var data2 = [ ser1, ser2 ];
$.plot($("#chart2"), data2,options);
var ser3 = {
label: "Field 2",
data: [ [1,25], [2,125], [3,280] ],
color: "#ff0000",
points: { show: false },
lines: { show: true},
bars: { show: false },
yaxis: 1
};
var data3 = [ ser1, ser3 ];
$.plot($("#chart3"), data3, options);
</script>Alijah Ahmedhttp://www.blogger.com/profile/01151589526613233918noreply@blogger.com37tag:blogger.com,1999:blog-8441309013858171051.post-33688170422647447802013-12-18T09:43:00.002-08:002013-12-22T14:00:04.381-08:00Type I and Type II Errors in Hypothesis Testing<p style="text-align:justify">In hypothesis testing, mention is made of Type I and Type II Errors.</p>
<p style="text-align:justify"> A Type I error is when a Null Hypothesis is incorrectly <i>rejected</i>, and is also known as a <i>false positive</i>. In such a scenario, the p-value calculated is below the significance level (0.05 being a common value used), when in fact there is no significant effect and the p-value should have been higher.</p>
<p style="text-align:justify"> A Type II error is when a Null Hypothesis is incorrectly <i>accepted</i>, and is also known as a <i>false negative</i>. In such a scenario, the p-value calculated is above the significance level (0.05 being a common value used), when in fact there is a significant effect and the p-value should have been below the significance level. Such an error can have more serious ramifications than a Type I error, for example, when one is screening for the presence of potentially malignant cells in a patient and the Null Hypothesis is that there are no malignant cells present.</p>
<p style="text-align:justify"> The Power of a test is one minus the probability of a Type II error, and should ideally be one. If there are two statistical tests for testing the same Null Hypothesis, the test with greater power will yield a lower p-value, and so the chances of rejecting the Null Hypothesis for this test will be greater - in other words, the chances of incorrectly accepting the Null Hypothesis will be lower for the more powerful test.</p> Alijah Ahmedhttp://www.blogger.com/profile/01151589526613233918noreply@blogger.com12tag:blogger.com,1999:blog-8441309013858171051.post-25468561800887493792013-12-15T09:33:00.002-08:002023-12-15T11:55:07.374-08:00Chi-squared test for independence Calculator<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<script language="Javascript">
/**
* jQuery-csv (jQuery Plugin)
*
* This document is licensed as free software under the terms of the
* MIT License: http://www.opensource.org/licenses/mit-license.php
*
* Acknowledgements:
* The original design and influence to implement this library as a jquery
* plugin is influenced by jquery-json (http://code.google.com/p/jquery-json/).
* If you're looking to use native JSON.Stringify but want additional backwards
* compatibility for browsers that don't support it, I highly recommend you
* check it out.
*
* A special thanks goes out to rwk@acm.org for providing a lot of valuable
* feedback to the project including the core for the new FSM
* (Finite State Machine) parsers. If you're looking for a stable TSV parser
* be sure to take a look at jquery-tsv (http://code.google.com/p/jquery-tsv/).
* For legal purposes I'll include the "NO WARRANTY EXPRESSED OR IMPLIED.
* USE AT YOUR OWN RISK.". Which, in 'layman's terms' means, by using this
* library you are accepting responsibility if it breaks your code.
*
* Legal jargon aside, I will do my best to provide a useful and stable core
* that can effectively be built on.
*
* Copyrighted 2012 by Evan Plaice.
*/
RegExp.escape= function(s) {
return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
};
(function (undefined) {
'use strict';
var $;
// to keep backwards compatibility
if (typeof jQuery !== 'undefined' && jQuery) {
$ = jQuery;
} else {
$ = {};
}
/**
* jQuery.csv.defaults
* Encapsulates the method paramater defaults for the CSV plugin module.
*/
$.csv = {
defaults: {
separator:',',
delimiter:'"',
headers:true
},
hooks: {
castToScalar: function(value, state) {
var hasDot = /\./;
if (isNaN(value)) {
return value;
} else {
if (hasDot.test(value)) {
return parseFloat(value);
} else {
var integer = parseInt(value);
if(isNaN(integer)) {
return null;
} else {
return integer;
}
}
}
}
},
parsers: {
parse: function(csv, options) {
// cache settings
var separator = options.separator;
var delimiter = options.delimiter;
// set initial state if it's missing
if(!options.state.rowNum) {
options.state.rowNum = 1;
}
if(!options.state.colNum) {
options.state.colNum = 1;
}
// clear initial state
var data = [];
var entry = [];
var state = 0;
var value = '';
var exit = false;
function endOfEntry() {
// reset the state
state = 0;
value = '';
// if 'start' hasn't been met, don't output
if(options.start && options.state.rowNum < options.start) {
// update global state
entry = [];
options.state.rowNum++;
options.state.colNum = 1;
return;
}
if(options.onParseEntry === undefined) {
// onParseEntry hook not set
data.push(entry);
} else {
var hookVal = options.onParseEntry(entry, options.state); // onParseEntry Hook
// false skips the row, configurable through a hook
if(hookVal !== false) {
data.push(hookVal);
}
}
//console.log('entry:' + entry);
// cleanup
entry = [];
// if 'end' is met, stop parsing
if(options.end && options.state.rowNum >= options.end) {
exit = true;
}
// update global state
options.state.rowNum++;
options.state.colNum = 1;
}
function endOfValue() {
if(options.onParseValue === undefined) {
// onParseValue hook not set
entry.push(value);
} else {
var hook = options.onParseValue(value, options.state); // onParseValue Hook
// false skips the row, configurable through a hook
if(hook !== false) {
entry.push(hook);
}
}
//console.log('value:' + value);
// reset the state
value = '';
state = 0;
// update global state
options.state.colNum++;
}
// escape regex-specific control chars
var escSeparator = RegExp.escape(separator);
var escDelimiter = RegExp.escape(delimiter);
// compile the regEx str using the custom delimiter/separator
var match = /(D|S|\r\n|\n|\r|[^DS\r\n]+)/;
var matchSrc = match.source;
matchSrc = matchSrc.replace(/S/g, escSeparator);
matchSrc = matchSrc.replace(/D/g, escDelimiter);
match = new RegExp(matchSrc, 'gm');
// put on your fancy pants...
// process control chars individually, use look-ahead on non-control chars
csv.replace(match, function (m0) {
if(exit) {
return;
}
switch (state) {
// the start of a value
case 0:
// null last value
if (m0 === separator) {
value += '';
endOfValue();
break;
}
// opening delimiter
if (m0 === delimiter) {
state = 1;
break;
}
// null last value
if (/^(\r\n|\n|\r)$/.test(m0)) {
endOfValue();
endOfEntry();
break;
}
// un-delimited value
value += m0;
state = 3;
break;
// delimited input
case 1:
// second delimiter? check further
if (m0 === delimiter) {
state = 2;
break;
}
// delimited data
value += m0;
state = 1;
break;
// delimiter found in delimited input
case 2:
// escaped delimiter?
if (m0 === delimiter) {
value += m0;
state = 1;
break;
}
// null value
if (m0 === separator) {
endOfValue();
break;
}
// end of entry
if (/^(\r\n|\n|\r)$/.test(m0)) {
endOfValue();
endOfEntry();
break;
}
// broken paser?
throw new Error('CSVDataError: Illegal State [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
// un-delimited input
case 3:
// null last value
if (m0 === separator) {
endOfValue();
break;
}
// end of entry
if (/^(\r\n|\n|\r)$/.test(m0)) {
endOfValue();
endOfEntry();
break;
}
if (m0 === delimiter) {
// non-compliant data
throw new Error('CSVDataError: Illegal Quote [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
}
// broken parser?
throw new Error('CSVDataError: Illegal Data [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
default:
// shenanigans
throw new Error('CSVDataError: Unknown State [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
}
//console.log('val:' + m0 + ' state:' + state);
});
// submit the last entry
// ignore null last line
if(entry.length !== 0) {
endOfValue();
endOfEntry();
}
return data;
},
// a csv-specific line splitter
splitLines: function(csv, options) {
// cache settings
var separator = options.separator;
var delimiter = options.delimiter;
// set initial state if it's missing
if(!options.state.rowNum) {
options.state.rowNum = 1;
}
// clear initial state
var entries = [];
var state = 0;
var entry = '';
var exit = false;
function endOfLine() {
// reset the state
state = 0;
// if 'start' hasn't been met, don't output
if(options.start && options.state.rowNum < options.start) {
// update global state
entry = '';
options.state.rowNum++;
return;
}
if(options.onParseEntry === undefined) {
// onParseEntry hook not set
entries.push(entry);
} else {
var hookVal = options.onParseEntry(entry, options.state); // onParseEntry Hook
// false skips the row, configurable through a hook
if(hookVal !== false) {
entries.push(hookVal);
}
}
// cleanup
entry = '';
// if 'end' is met, stop parsing
if(options.end && options.state.rowNum >= options.end) {
exit = true;
}
// update global state
options.state.rowNum++;
}
// escape regex-specific control chars
var escSeparator = RegExp.escape(separator);
var escDelimiter = RegExp.escape(delimiter);
// compile the regEx str using the custom delimiter/separator
var match = /(D|S|\n|\r|[^DS\r\n]+)/;
var matchSrc = match.source;
matchSrc = matchSrc.replace(/S/g, escSeparator);
matchSrc = matchSrc.replace(/D/g, escDelimiter);
match = new RegExp(matchSrc, 'gm');
// put on your fancy pants...
// process control chars individually, use look-ahead on non-control chars
csv.replace(match, function (m0) {
if(exit) {
return;
}
switch (state) {
// the start of a value/entry
case 0:
// null value
if (m0 === separator) {
entry += m0;
state = 0;
break;
}
// opening delimiter
if (m0 === delimiter) {
entry += m0;
state = 1;
break;
}
// end of line
if (m0 === '\n') {
endOfLine();
break;
}
// phantom carriage return
if (/^\r$/.test(m0)) {
break;
}
// un-delimit value
entry += m0;
state = 3;
break;
// delimited input
case 1:
// second delimiter? check further
if (m0 === delimiter) {
entry += m0;
state = 2;
break;
}
// delimited data
entry += m0;
state = 1;
break;
// delimiter found in delimited input
case 2:
// escaped delimiter?
var prevChar = entry.substr(entry.length - 1);
if (m0 === delimiter && prevChar === delimiter) {
entry += m0;
state = 1;
break;
}
// end of value
if (m0 === separator) {
entry += m0;
state = 0;
break;
}
// end of line
if (m0 === '\n') {
endOfLine();
break;
}
// phantom carriage return
if (m0 === '\r') {
break;
}
// broken paser?
throw new Error('CSVDataError: Illegal state [Row:' + options.state.rowNum + ']');
// un-delimited input
case 3:
// null value
if (m0 === separator) {
entry += m0;
state = 0;
break;
}
// end of line
if (m0 === '\n') {
endOfLine();
break;
}
// phantom carriage return
if (m0 === '\r') {
break;
}
// non-compliant data
if (m0 === delimiter) {
throw new Error('CSVDataError: Illegal quote [Row:' + options.state.rowNum + ']');
}
// broken parser?
throw new Error('CSVDataError: Illegal state [Row:' + options.state.rowNum + ']');
default:
// shenanigans
throw new Error('CSVDataError: Unknown state [Row:' + options.state.rowNum + ']');
}
//console.log('val:' + m0 + ' state:' + state);
});
// submit the last entry
// ignore null last line
if(entry !== '') {
endOfLine();
}
return entries;
},
// a csv entry parser
parseEntry: function(csv, options) {
// cache settings
var separator = options.separator;
var delimiter = options.delimiter;
// set initial state if it's missing
if(!options.state.rowNum) {
options.state.rowNum = 1;
}
if(!options.state.colNum) {
options.state.colNum = 1;
}
// clear initial state
var entry = [];
var state = 0;
var value = '';
function endOfValue() {
if(options.onParseValue === undefined) {
// onParseValue hook not set
entry.push(value);
} else {
var hook = options.onParseValue(value, options.state); // onParseValue Hook
// false skips the value, configurable through a hook
if(hook !== false) {
entry.push(hook);
}
}
// reset the state
value = '';
state = 0;
// update global state
options.state.colNum++;
}
// checked for a cached regEx first
if(!options.match) {
// escape regex-specific control chars
var escSeparator = RegExp.escape(separator);
var escDelimiter = RegExp.escape(delimiter);
// compile the regEx str using the custom delimiter/separator
var match = /(D|S|\n|\r|[^DS\r\n]+)/;
var matchSrc = match.source;
matchSrc = matchSrc.replace(/S/g, escSeparator);
matchSrc = matchSrc.replace(/D/g, escDelimiter);
options.match = new RegExp(matchSrc, 'gm');
}
// put on your fancy pants...
// process control chars individually, use look-ahead on non-control chars
csv.replace(options.match, function (m0) {
switch (state) {
// the start of a value
case 0:
// null last value
if (m0 === separator) {
value += '';
endOfValue();
break;
}
// opening delimiter
if (m0 === delimiter) {
state = 1;
break;
}
// skip un-delimited new-lines
if (m0 === '\n' || m0 === '\r') {
break;
}
// un-delimited value
value += m0;
state = 3;
break;
// delimited input
case 1:
// second delimiter? check further
if (m0 === delimiter) {
state = 2;
break;
}
// delimited data
value += m0;
state = 1;
break;
// delimiter found in delimited input
case 2:
// escaped delimiter?
if (m0 === delimiter) {
value += m0;
state = 1;
break;
}
// null value
if (m0 === separator) {
endOfValue();
break;
}
// skip un-delimited new-lines
if (m0 === '\n' || m0 === '\r') {
break;
}
// broken paser?
throw new Error('CSVDataError: Illegal State [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
// un-delimited input
case 3:
// null last value
if (m0 === separator) {
endOfValue();
break;
}
// skip un-delimited new-lines
if (m0 === '\n' || m0 === '\r') {
break;
}
// non-compliant data
if (m0 === delimiter) {
throw new Error('CSVDataError: Illegal Quote [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
}
// broken parser?
throw new Error('CSVDataError: Illegal Data [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
default:
// shenanigans
throw new Error('CSVDataError: Unknown State [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
}
//console.log('val:' + m0 + ' state:' + state);
});
// submit the last value
endOfValue();
return entry;
}
},
helpers: {
/**
* $.csv.helpers.collectPropertyNames(objectsArray)
* Collects all unique property names from all passed objects.
*
* @param {Array} objects Objects to collect properties from.
*
* Returns an array of property names (array will be empty,
* if objects have no own properties).
*/
collectPropertyNames: function (objects) {
var o, propName, props = [];
for (o in objects) {
for (propName in objects[o]) {
if ((objects[o].hasOwnProperty(propName)) &&
(props.indexOf(propName) < 0) &&
(typeof objects[o][propName] !== 'function')) {
props.push(propName);
}
}
}
return props;
}
},
/**
* $.csv.toArray(csv)
* Converts a CSV entry string to a javascript array.
*
* @param {Array} csv The string containing the CSV data.
* @param {Object} [options] An object containing user-defined options.
* @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
* @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
*
* This method deals with simple CSV strings only. It's useful if you only
* need to parse a single entry. If you need to parse more than one line,
* use $.csv2Array instead.
*/
toArray: function(csv, options, callback) {
options = (options !== undefined ? options : {});
var config = {};
config.callback = ((callback !== undefined && typeof(callback) === 'function') ? callback : false);
config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator;
config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter;
var state = (options.state !== undefined ? options.state : {});
// setup
options = {
delimiter: config.delimiter,
separator: config.separator,
onParseEntry: options.onParseEntry,
onParseValue: options.onParseValue,
state: state
};
var entry = $.csv.parsers.parseEntry(csv, options);
// push the value to a callback if one is defined
if(!config.callback) {
return entry;
} else {
config.callback('', entry);
}
},
/**
* $.csv.toArrays(csv)
* Converts a CSV string to a javascript array.
*
* @param {String} csv The string containing the raw CSV data.
* @param {Object} [options] An object containing user-defined options.
* @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
* @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
*
* This method deals with multi-line CSV. The breakdown is simple. The first
* dimension of the array represents the line (or entry/row) while the second
* dimension contains the values (or values/columns).
*/
toArrays: function(csv, options, callback) {
options = (options !== undefined ? options : {});
var config = {};
config.callback = ((callback !== undefined && typeof(callback) === 'function') ? callback : false);
config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator;
config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter;
// setup
var data = [];
options = {
delimiter: config.delimiter,
separator: config.separator,
onPreParse: options.onPreParse,
onParseEntry: options.onParseEntry,
onParseValue: options.onParseValue,
onPostParse: options.onPostParse,
start: options.start,
end: options.end,
state: {
rowNum: 1,
colNum: 1
}
};
// onPreParse hook
if(options.onPreParse !== undefined) {
options.onPreParse(csv, options.state);
}
// parse the data
data = $.csv.parsers.parse(csv, options);
// onPostParse hook
if(options.onPostParse !== undefined) {
options.onPostParse(data, options.state);
}
// push the value to a callback if one is defined
if(!config.callback) {
return data;
} else {
config.callback('', data);
}
},
/**
* $.csv.toObjects(csv)
* Converts a CSV string to a javascript object.
* @param {String} csv The string containing the raw CSV data.
* @param {Object} [options] An object containing user-defined options.
* @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
* @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
* @param {Boolean} [headers] Indicates whether the data contains a header line. Defaults to true.
*
* This method deals with multi-line CSV strings. Where the headers line is
* used as the key for each value per entry.
*/
toObjects: function(csv, options, callback) {
options = (options !== undefined ? options : {});
var config = {};
config.callback = ((callback !== undefined && typeof(callback) === 'function') ? callback : false);
config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator;
config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter;
config.headers = 'headers' in options ? options.headers : $.csv.defaults.headers;
options.start = 'start' in options ? options.start : 1;
// account for headers
if(config.headers) {
options.start++;
}
if(options.end && config.headers) {
options.end++;
}
// setup
var lines = [];
var data = [];
options = {
delimiter: config.delimiter,
separator: config.separator,
onPreParse: options.onPreParse,
onParseEntry: options.onParseEntry,
onParseValue: options.onParseValue,
onPostParse: options.onPostParse,
start: options.start,
end: options.end,
state: {
rowNum: 1,
colNum: 1
},
match: false,
transform: options.transform
};
// fetch the headers
var headerOptions = {
delimiter: config.delimiter,
separator: config.separator,
start: 1,
end: 1,
state: {
rowNum:1,
colNum:1
}
};
// onPreParse hook
if(options.onPreParse !== undefined) {
options.onPreParse(csv, options.state);
}
// parse the csv
var headerLine = $.csv.parsers.splitLines(csv, headerOptions);
var headers = $.csv.toArray(headerLine[0], options);
// fetch the data
lines = $.csv.parsers.splitLines(csv, options);
// reset the state for re-use
options.state.colNum = 1;
if(headers){
options.state.rowNum = 2;
} else {
options.state.rowNum = 1;
}
// convert data to objects
for(var i=0, len=lines.length; i<len; i++) {
var entry = $.csv.toArray(lines[i], options);
var object = {};
for(var j=0; j <headers.length; j++) {
object[headers[j]] = entry[j];
}
if (options.transform !== undefined) {
data.push(options.transform.call(undefined, object));
} else {
data.push(object);
}
// update row state
options.state.rowNum++;
}
// onPostParse hook
if(options.onPostParse !== undefined) {
options.onPostParse(data, options.state);
}
// push the value to a callback if one is defined
if(!config.callback) {
return data;
} else {
config.callback('', data);
}
},
/**
* $.csv.fromArrays(arrays)
* Converts a javascript array to a CSV String.
*
* @param {Array} arrays An array containing an array of CSV entries.
* @param {Object} [options] An object containing user-defined options.
* @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
* @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
*
* This method generates a CSV file from an array of arrays (representing entries).
*/
fromArrays: function(arrays, options, callback) {
options = (options !== undefined ? options : {});
var config = {};
config.callback = ((callback !== undefined && typeof(callback) === 'function') ? callback : false);
config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator;
config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter;
var output = '',
line,
lineValues,
i, j;
for (i = 0; i < arrays.length; i++) {
line = arrays[i];
lineValues = [];
for (j = 0; j < line.length; j++) {
var strValue = (line[j] === undefined || line[j] === null) ? '' : line[j].toString();
if (strValue.indexOf(config.delimiter) > -1) {
strValue = strValue.replace(config.delimiter, config.delimiter + config.delimiter);
}
var escMatcher = '\n|\r|S|D';
escMatcher = escMatcher.replace('S', config.separator);
escMatcher = escMatcher.replace('D', config.delimiter);
if (strValue.search(escMatcher) > -1) {
strValue = config.delimiter + strValue + config.delimiter;
}
lineValues.push(strValue);
}
output += lineValues.join(config.separator) + '\r\n';
}
// push the value to a callback if one is defined
if(!config.callback) {
return output;
} else {
config.callback('', output);
}
},
/**
* $.csv.fromObjects(objects)
* Converts a javascript dictionary to a CSV string.
*
* @param {Object} objects An array of objects containing the data.
* @param {Object} [options] An object containing user-defined options.
* @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
* @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
* @param {Character} [sortOrder] Sort order of columns (named after
* object properties). Use 'alpha' for alphabetic. Default is 'declare',
* which means, that properties will _probably_ appear in order they were
* declared for the object. But without any guarantee.
* @param {Character or Array} [manualOrder] Manually order columns. May be
* a strin in a same csv format as an output or an array of header names
* (array items won't be parsed). All the properties, not present in
* `manualOrder` will be appended to the end in accordance with `sortOrder`
* option. So the `manualOrder` always takes preference, if present.
*
* This method generates a CSV file from an array of objects (name:value pairs).
* It starts by detecting the headers and adding them as the first line of
* the CSV file, followed by a structured dump of the data.
*/
fromObjects: function(objects, options, callback) {
options = (options !== undefined ? options : {});
var config = {};
config.callback = ((callback !== undefined && typeof(callback) === 'function') ? callback : false);
config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator;
config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter;
config.headers = 'headers' in options ? options.headers : $.csv.defaults.headers;
config.sortOrder = 'sortOrder' in options ? options.sortOrder : 'declare';
config.manualOrder = 'manualOrder' in options ? options.manualOrder : [];
config.transform = options.transform;
if (typeof config.manualOrder === 'string') {
config.manualOrder = $.csv.toArray(config.manualOrder, config);
}
if (config.transform !== undefined) {
var origObjects = objects;
objects = [];
var i;
for (i = 0; i < origObjects.length; i++) {
objects.push(config.transform.call(undefined, origObjects[i]));
}
}
var props = $.csv.helpers.collectPropertyNames(objects);
if (config.sortOrder === 'alpha') {
props.sort();
} // else {} - nothing to do for 'declare' order
if (config.manualOrder.length > 0) {
var propsManual = [].concat(config.manualOrder);
var p;
for (p = 0; p < props.length; p++) {
if (propsManual.indexOf( props[p] ) < 0) {
propsManual.push( props[p] );
}
}
props = propsManual;
}
var o, p, line, output = [], propName;
if (config.headers) {
output.push(props);
}
for (o = 0; o < objects.length; o++) {
line = [];
for (p = 0; p < props.length; p++) {
propName = props[p];
if (propName in objects[o] && typeof objects[o][propName] !== 'function') {
line.push(objects[o][propName]);
} else {
line.push('');
}
}
output.push(line);
}
// push the value to a callback if one is defined
return $.csv.fromArrays(output, options, config.callback);
}
};
// Maintenance code to maintain backward-compatibility
// Will be removed in release 1.0
$.csvEntry2Array = $.csv.toArray;
$.csv2Array = $.csv.toArrays;
$.csv2Dictionary = $.csv.toObjects;
// CommonJS module is defined
if (typeof module !== 'undefined' && module.exports) {
module.exports = $.csv;
}
}).call( this );
</script>
<script>
$(document).ready(function() {
if(isAPIAvailable()) {
$('#files').bind('change', handleFileSelect);
}
});
function isAPIAvailable() {
// Check for the various File API support.
if (window.File && window.FileReader && window.FileList && window.Blob) {
// Great success! All the File APIs are supported.
return true;
} else {
// source: File API availability - http://caniuse.com/#feat=fileapi
// source: <output> availability - http://html5doctor.com/the-output-element/
document.writeln('The HTML5 APIs used in this form are only available in the following browsers:<br />');
// 6.0 File API & 13.0 <output>
document.writeln(' - Google Chrome: 13.0 or later<br />');
// 3.6 File API & 6.0 <output>
document.writeln(' - Mozilla Firefox: 6.0 or later<br />');
// 10.0 File API & 10.0 <output>
document.writeln(' - Internet Explorer: Not supported (partial support expected in 10.0)<br />');
// ? File API & 5.1 <output>
document.writeln(' - Safari: Not supported<br />');
// ? File API & 9.2 <output>
document.writeln(' - Opera: Not supported');
return false;
}
}
function handleFileSelect(evt) {
var files = evt.target.files; // FileList object
var file = files[0];
// read the file metadata
var output = ''
output += '<span style="font-weight:bold;">' + escape(file.name) + '</span><br />\n';
output += ' - FileType: ' + (file.type || 'n/a') + '<br />\n';
output += ' - FileSize: ' + file.size + ' bytes<br />\n';
output += ' - LastModified: ' + (file.lastModifiedDate ? file.lastModifiedDate.toLocaleDateString() : 'n/a') + '<br />\n';
// read the file contents
printTextArea(file);
// post the results
$('#list').append(output);
}
function printTextArea(file) {
var container = document.getElementById("area");
var inps = container.getElementsByTagName("input");
var nr = inps.length;
var idx_selected = 0;
for (k=0; k < nr; k++)
{
if (inps.item(k).value == "")
{
idx_selected = k;
break;
}
}
var reader = new FileReader();
reader.readAsText(file);
reader.onload = function(event){
var csv = event.target.result;
var data = $.csv.toArrays(csv);
var multi_col = 0;
var mystr = new Array();
for (k=0;k<nr;k++)
mystr[k] = '';
for(var row in data) {
if (!isNaN(Number(data[row][0])))
{
if (data[row].length > 1)
{
multi_col = 1;
for (k=0; k < nr; k++)
{
mystr[k] = (mystr[k]).concat((Number(data[row][k]).toFixed(3)).toString());
mystr[k] = (mystr[k]).concat(",");
}
}
else
{
mystr[0] = (mystr[0]).concat((Number(data[row][0]).toFixed(3)).toString());
mystr[0] = (mystr[0]).concat(",");
}
}
}
if (multi_col == 0)
{
mystr[0] = (mystr[0]).substring(0, mystr[0].length - 1)
inps.item(idx_selected).value = mystr[0];
}
else
{
for (k=0; k < nr; k++)
{
mystr[k] = (mystr[k]).substring(0, mystr[k].length - 1)
inps.item(k).value = mystr[k];
}
}
};
reader.onerror = function(){ alert('Unable to read ' + file.fileName); };
}
</script>
<!--[if IE ]>
<script type="text/javascript" src="https://firebasestorage.googleapis.com/v0/b/js-static-assets.appspot.com/o/excanvas_min.js?alt=media&token=9dd78ad2-f09b-45dd-8a09-8db2b2546d1d"></script>
<![endif]-->
<script type="text/javascript" src="https://firebasestorage.googleapis.com/v0/b/js-static-assets.appspot.com/o/jquery_flot.js?alt=media&token=bde69a50-3df9-4794-b7d8-dd4e840472cd"></script>
</head>
<body>
<!-- Just call function 'addTextBox()' to add textbox -->
<a onclick='addTextBox()' href='#'>Please click to add a row.</a>
<br/>
<br/>
<!-- Textbox will be added in followng DIV -->
<div id='area'></div>
<div id=inputs class=clearfix>
<input type=file id=files name=files[] multiple />
</div>
<p style="text-align:justify"> This blog post implements an online calculator for Pearson's Chi-squared test for independence. For a discussion on this test, you can have a look <a href="http://scistatcalc.blogspot.co.uk/2014/01/chi-squared-test-for-independence.html" target="_blank">here</a>.</p>
<p style="text-align:justify"> Simply click on the link near the top to add text boxes. Each text box stores a single row of data and needs to be filled in with comma separated numbers. All rows need to have the same number of samples, which is equal to the number of columns.</p>
<p style="text-align:justify">Alternatively, you can choose two file entry methods:-
<ol>
<li>Select multiple single column CSV files to populate the text boxes by repeatedly pressing the Choose File button - there must be one distinct (and differently named) file for each text box i.e. one file per group. Each file can have a different number of samples.</li>
<li>Select a single multi-column CSV file by pressing the Choose File button once, where the number of columns equals the number of groups - all groups need to have the same number of samples.</li>
</ol>
</p>
<p style="text-align:justify"> In addition, a table of standardised residuals is calculated. A negative value of a particular element means that the observed frequency is lower than the expected frequency, whereas a positive value implies that the observed frequency is greater than the expected frequency. This is useful to examine which element has a significant difference between the observed and expected frequencies - an absolute value greater than 1.96 can be considered significant for the 0.05 level. </p>
<input name="b1" onclick="calculate_chi_test()" style="margin-left: 0px;" type="Button" value="Calculate.." />
<p id="p1">Results pending...</p>
<input name="b1" onclick="clearReset()" style="margin-left: 0px;" type="Button" value="Reset Results log" />
<br>
<br>
<!-- Post hoc Textbox will be added in followng DIV -->
<div id='areaph'></div>
</body>
</html>
<script language="javascript">
var inival=0; // Initialise starting element number
var phval = 0;
function calculate_chi_test() {
phval = 0;
var chi_msg = '';
var tmp_str = '';
var str = inival.toString();
var container = document.getElementById("area");
var inps = container.getElementsByTagName("input");
var nr= inps.length;
tmp_str = "Number of rows is " + nr + "<br>";
chi_msg = chi_msg.concat(tmp_str);
var dataSet;
var datavec = new Array();
var nc = 0;
var first_len;
var all_same_len = 1;
for (k=0; k < nr; k++)
{
if (inps.item(k).value.split(',').length > nc)
nc = inps.item(k).value.split(',').length;
if (k == 0)
first_len = inps.item(k).value.split(',').length;
if (inps.item(k).value.split(',').length != first_len)
all_same_len = 0;
}
if (all_same_len == 0)
{
alert("All rows must have same number of samples i.e same number of columns");
return;
}
tmp_str = "Number of columns is " + first_len + "<br>";
chi_msg = chi_msg.concat(tmp_str);
var omat = [];
var emat = [];
var resimat = [];
for(var i=0; i< nr ; i++)
{
omat[i] = [];
emat[i] = [];
resimat[i] = [];
for(var j=0; j< first_len; j++) {
omat[i][j] = 0;
emat[i][j] = 0;
resimat[i][j] = 0;
}
}
for (k=0; k < nr; k++)
{
dataSet = inps.item(k).value;
datavec = dataSet.split(',');
if (datavec.length > nc)
nc = datavec.length;
for (m=0; m < datavec.length; m++)
{
omat[k][m] = Number(datavec[m]);
}
}
// Total of samples
var N = 0;
for (k = 0; k < nr; k++)
{
for (m = 0; m < nc; m++)
N = N + omat[k][m];
}
tmp_str = "Total number of outcomes is " + N + "<br><br>";
chi_msg = chi_msg.concat(tmp_str);
var rowsum = 0;
var colsum = 0;
// Set emat values
for (k=0; k < nr; k++)
{
for (m=0; m < nc; m++)
{
rowsum = 0;
for (p = 0; p < nr; p++)
rowsum = rowsum + omat[p][m];
colsum = 0;
for (p = 0; p < nc; p++)
colsum = colsum + omat[k][p];
emat[k][m] = (rowsum * colsum)/N;
}
}
// Debug array printing..
/*
tmp_str = "O matrix entries " + "<br>";
for (k=0; k < nr; k++)
{
tmp_str = '';
for (m = 0; m < nc; m++)
{
var in_str = omat[k][m] + ","
tmp_str = tmp_str.concat(in_str);
}
tmp_str = tmp_str.concat("<br>");
chi_msg = chi_msg.concat(tmp_str);
}
tmp_str = "E (expected frequency) matrix entries " + "<br>";
for (k=0; k < nr; k++)
{
tmp_str = '';
for (m = 0; m < nc; m++)
{
var in_str = emat[k][m].toFixed(3) + ","
tmp_str = tmp_str.concat(in_str);
}
tmp_str = tmp_str.concat("<br>");
chi_msg = chi_msg.concat(tmp_str);
}
*/
var T = 0;
for (k=0; k < nr; k++)
{
for (m = 0; m < nc; m++)
{
var d2 = ((omat[k][m] - emat[k][m]) * (omat[k][m] - emat[k][m]))/emat[k][m];
T = T + d2;
}
}
var kdf = (nr - 1) * (nc - 1);
var pval = 1 - chisquared_cdf(T,kdf);
tmp_str = "Chi-squared test statistic is " + T.toFixed(6) + "<br>";
chi_msg = chi_msg.concat(tmp_str);
tmp_str = "Corresponding p-value for Chi-squared distn of df " + kdf + " is " + pval.toFixed(6) + "<br>";
chi_msg = chi_msg.concat(tmp_str);
chi_msg = chi_msg.fontcolor("white");
for (k = 0; k < nr; k++)
{
for (m = 0; m < nc; m++)
resimat[k][m] = (omat[k][m] - emat[k][m])/Math.sqrt(emat[k][m]);
}
document.getElementById("areaph").innerHTML = "Table of residuals:-<br>";
for (k = 0; k < nr; k++)
addTextBox2();
var cont2 = document.getElementById("areaph");
var inps2 = cont2.getElementsByTagName("input");
var strtmp = '';
var strel = '';
for (k = 0; k < nr; k++)
{
strtmp = '';
for (m = 0; m < nc - 1; m++)
{
strel = resimat[k][m].toFixed(3) + ",";
inps2.item(k).value = inps2.item(k).value.concat(strel);
}
strel = resimat[k][nc - 1].toFixed(3);
// strtmp = strtmp.concat(strel);
inps2.item(k).value = inps2.item(k).value.concat(strel);
}
document.getElementById("p1").innerHTML=chi_msg;
}
// Call this function to add textbox
function addTextBox()
{
phval = 0;
var newArea = add_New_Element();
var str = inival.toString();
var htcontents = "Row ";
htcontents = htcontents.concat(inival.toString());
htcontents = htcontents.concat(": <input type='textArea' style='width:300px;' name='textbx[]' />");
htcontents = htcontents.concat("<br><br>");
document.getElementById(newArea).innerHTML = htcontents; // You can any other elements in place of 'htcontents'
}
function add_New_Element() {
inival=inival+1; // Increment element number by 1
var ni = document.getElementById('area');
var newdiv = document.createElement('div'); // Create dynamic element
var divIdName = 'my'+inival+'Div';
newdiv.setAttribute('id',divIdName);
ni.appendChild(newdiv);
return divIdName;
}
// Post-hoc text boxes
// Call this function to add secondary textbox
function addTextBox2()
{
var newArea2 = add_New_Element2();
var str = phval.toString();
var htcontents = "Row ";
htcontents = htcontents.concat(phval.toString());
htcontents = htcontents.concat(": <input type='textArea' style='width:300px;' />");
htcontents = htcontents.concat("<br><br>");
document.getElementById(newArea2).innerHTML = htcontents; // You can any other elements in place of 'htcontents'
}
function add_New_Element2() {
phval=phval+1; // Increment element number by 1
var ni = document.getElementById('areaph');
var newdiv = document.createElement('div'); // Create dynamic element
var divIdName = 'moi'+phval+'Div';
newdiv.setAttribute('id',divIdName);
ni.appendChild(newdiv);
return divIdName;
}
function clearReset() {
document.getElementById("p1").innerHTML="Results pending..";
}
function compare(a,b) {
if (a[0] < b[0])
return -1;
if (a[0] > b[0])
return 1;
return
}
function rankify(x,idx,Xlen)
{
var lmatch = new Array()
var match_size = 0
var lrank = new Array()
for (k=0; k < Xlen; k++)
lrank[k] = k + 1;
for (k=0; k < Xlen; k++)
{
if (k == 0)
match_size = 0;
else
{
var mytmp1 = x[k]
var mytmp2 = x[k-1]
if (Math.abs(mytmp1[idx] - mytmp2[idx]) < 1e-15)
{
match_size++;
}
else
{
match_size = 0;
}
}
lmatch[k] = match_size;
}
for (k = 0; k < Xlen; k++)
{
if (k < Xlen - 1)
{
if ((lmatch[k] != 0) &
(lmatch[k+1] == 0))
{
sum = 0.0;
for (m = k; m >= k - lmatch[k]; m--)
{
sum = sum + lrank[m];
}
sum = sum/(lmatch[k] + 1.0);
for (m = k; m >= k - lmatch[k]; m--)
{
lrank[m] = sum;
}
}
}
else
{
if (lmatch[k] != 0)
{
sum = 0.0;
for (m = k; m >= k - lmatch[k]; m--)
{
sum = sum + lrank[m];
}
sum = sum/(lmatch[k] + 1.0);
for (m = k; m >= k - lmatch[k]; m--)
{
lrank[m] = sum
}
}
}
}
return lrank;
}
function chisquared_cdf(inp,p1)
{
var res;
res = land_gammainc_eval(inp/2.0, p1/2.0, 0);
return res;
}
function chisquared_icdf(prob,p1)
{
var res;
res = find_inverse_gamma(p1/2.0, prob, 1.0 - prob);
res = res*2;
if (prob == 0)
res = 0;
return res;
}
function land_gammainc_eval(x, a, upper)
{
var tmp;
var xeg = ((1 + 0.0)/land_ios_gamma(a+1))*Math.pow(x+0.0,a)*Math.exp(-x);
var acc = 0.0;
var Pax;
var Qax;
var lambda;
var eta;
var gamma_star;
var var1;
var acc_f;
var SaEta,RaEta;
var f = new Array();
var b = new Array();
var ak_vec = new Array();
var s_vec = new Array();
var t_vec = new Array();
var rho_vec = new Array();
var log_diff;
var NUMSERIES = 100;
var VNUM = 100;
var QSUM = 100;
if (Math.abs(x) < 1e-16)
{
if (upper == 0)
return 0;
else
return 1;
}
if (x <= a)
{
if ((Math.abs(x) < 10) & (Math.abs(a) < 10) )
{
for (k=0; k < NUMSERIES; k++)
{
if (a+k+1 < 170)
acc += Math.pow(x,k+0.0)*((land_ios_gamma(a+1)+0.0)/land_ios_gamma(k+a+1));
else
{
log_diff = land_lgamma_stirling(a+1) - land_lgamma_stirling(k+a+1) + (k * log(x));
acc += Math.exp(log_diff);
}
}
Pax = xeg * acc;
if (upper == 0)
return Pax;
else
return (1-Pax);
}
else
{
lambda = x/a;
eta = Math.sqrt(2*(lambda - 1 - Math.log(lambda)));
if ((lambda - 1) < 0)
eta = -1 * eta;
// For large a, gamma_star approaches unity..
if (a > 140)
gamma_star = 1.0;
else
gamma_star = land_ios_gamma(a)/(Math.sqrt(2*Math.PI) * Math.exp(-a) * Math.pow(a,a-0.5));
for (k=0;k<VNUM;k++)
f[k] = 0.0;
f[0] = 1;
f[1] = -1.0/3.0;
f[2] = 1.0/12.0;
f[3] = -2.0/135.0;
for (m=4; m < VNUM; m++)
{
var1 = ((m - 1.0)/(3.0*m))* f[m-1];
acc_f = 0.0;
for (j=3; j < m; j++)
{
acc_f = acc_f + (f[j-1] * f[m+1-j])/(m+2-j);
}
f[m] = -((m+1.0)/(m+2.0))*(var1 + acc_f);
}
for (k=0; k < VNUM-1; k++)
b[k] = 0.0;
b[VNUM-2] = f[VNUM-1];
b[VNUM-3] = f[VNUM-2];
for (m = VNUM-3; m > 0; m--)
b[m-1] = f[m] + (((m + 1.0)/a)*b[m+1]);
SaEta = 0.0;
for (m=0; m < b.length; m++)
SaEta = SaEta + (b[m] * Math.pow(eta,m+0.0));
SaEta = SaEta/gamma_star;
RaEta = (SaEta/Math.sqrt(2*Math.PI*a))*Math.exp(-0.5*a*eta*eta);
Qax = (0.5 * (1 - erf_taylor(eta*Math.sqrt(a/2.0)))) + RaEta;
Pax = (0.5 * (1 - erf_taylor(-eta*Math.sqrt(a/2.0)))) - RaEta;
if (upper == 1)
return Qax;
else
return Pax;
// EndOf Section that details modification (and improvement)
// to Gautschi's algorithm
}
}
else
{
if (x <= 1)
{
xeg = Math.pow(x,a)/land_ios_gamma(a);
acc_f = 0.0;
for (k=0; k < QSUM; k++)
acc_f += (Math.pow(-1.0,k)*Math.pow(x,k))/((a+k)*sFact(k));
tmp = xeg * acc_f;
if (upper == 1)
return 1 - tmp;
else
return tmp;
}
else
{
var g1 = (x -a + 1);
for (k=0; k < QSUM; k++)
ak_vec[k] = (k*(a - k))/((x-a+(2*k)-1)*(x-a+(2*k)+1));
for (k=0; k < QSUM; k++)
{
s_vec[k] = 0;
t_vec[k] = 0;
rho_vec[k] = 0;
}
for (k=0; k < QSUM; k++)
{
if (k == 0)
{
s_vec[k] = 0;
rho_vec[k] = 0;
t_vec[k] = 1;
}
else
{
var ak = ak_vec[k];
var rhokm1 = rho_vec[k-1];
rho_vec[k] =(-(ak)*(1 + rhokm1))/(1 + (ak*(1 + rhokm1)));
var rhok = rho_vec[k];
var tkm1 = t_vec[k-1];
t_vec[k] = rhok*tkm1;
var skm1 = s_vec[k-1];
s_vec[k] = skm1 + tkm1;
// [rho_vec[k] = (-(ak_vec[k])*(1 + rho_vec[k-1]))
// /(1 + (ak_vec[k]*(1 + rho_vec[k-1])));
// t_vec[k] = rho_vec[k]*t_vec[k-1];
// s_vec[k] = s_vec[k-1] + t_vec[k-1];
}
}
var slast = s_vec[QSUM-1];
var tmp_scale = (Math.pow(x,a)*Math.exp(-x)/land_ios_gamma(a));
var lga1 = a * Math.log(x);
var lga2 = (0.5 - a) * Math.log(a);
var lga3 = 0.5 * Math.log(2*Math.PI);
var lgtot = lga1 + (a - x) + lga2 - lga3;
if (a > 140)
tmp_scale = Math.exp(lgtot);
var newres;
if (land_ios_gamma(a) == 0.0)
newres = 0;
else
newres = (tmp_scale*slast)/g1;
if (upper == 1)
return newres;
else
return (1 - newres);
// EndOf Gautschi's routine
}
}
}
function find_inverse_gamma(a, p, q)
{
//
// In order to understand what's going on here, you will
// need to refer to:
//
// Computation of the Incomplete Gamma Function Ratios and their Inverse
// ARMIDO R. DIDONATO and ALFRED H. MORRIS, JR.
// ACM Transactions on Mathematical Software, Vol. 12, No. 4,
// December 1986, Pages 377-393.
//
var result;
if(a == 1)
{
result = -Math.log(q);
}
else if(a < 1)
{
var g = land_ios_gamma(a);
var b = q * g;
if((b > 0.6) || ((b >= 0.45) && (a >= 0.3)))
{
// DiDonato & Morris Eq 21:
//
// There is a slight variation from DiDonato and Morris here:
// the first form given here is unstable when p is close to 1,
// making it impossible to compute the inverse of Q(a,x) for small
// q. Fortunately the second form works perfectly well in this case.
//
var u;
if((b * q > 1e-8) && (q > 1e-5))
{
u = Math.pow(p * g * a, 1 / a);
}
else
{
u = Math.exp((-q / a) - 0.5772156649);
}
result = u / (1 - (u / (a + 1)));
}
else if((a < 0.3) && (b >= 0.35))
{
// DiDonato & Morris Eq 22:
var t = Math.exp(-0.5772156649 - b);
var u = t * Math.exp(t);
result = t * Math.exp(u);
}
else if((b > 0.15) || (a >= 0.3))
{
// DiDonato & Morris Eq 23:
var y = -Math.log(b);
var u = y - (1 - a) * Math.log(y);
result = y - (1 - a) * Math.log(u) - Math.log(1 + (1 - a) / (1 + u));
}
else if (b > 0.1)
{
// DiDonato & Morris Eq 24:
var y = -log(b);
var u = y - (1 - a) * log(y);
result = y - (1 - a) * log(u) - log((u * u + 2 * (3 - a) * u + (2 - a) * (3 - a)) / (u * u + (5 - a) * u + 2));
}
else
{
// DiDonato & Morris Eq 25:
var y = -Math.log(b);
var c1 = (a - 1) * Math.log(y);
var c1_2 = c1 * c1;
var c1_3 = c1_2 * c1;
var c1_4 = c1_2 * c1_2;
var a_2 = a * a;
var a_3 = a_2 * a;
var c2 = (a - 1) * (1 + c1);
var c3 = (a - 1) * (-(c1_2 / 2) + (a - 2) * c1 + (3 * a - 5) / 2);
var c4 = (a - 1) * ((c1_3 / 3)
- (3 * a - 5) * c1_2 / 2 + (a_2 - 6 * a + 7) * c1 + (11 * a_2 - 46 * a + 47) / 6);
var c5 = (a - 1) * (-(c1_4 / 4)
+ (11 * a - 17) * c1_3 / 6
+ (-3 * a_2 + 13 * a -13) * c1_2
+ (2 * a_3 - 25 * a_2 + 72 * a - 61) * c1 / 2
+ (25 * a_3 - 195 * a_2 + 477 * a - 379) / 12);
var y_2 = y * y;
var y_3 = y_2 * y;
var y_4 = y_2 * y_2;
result = y + c1 + (c2 / y) + (c3 / y_2) + (c4 / y_3) + (c5 / y_4);
}
}
else
{
// DiDonato and Morris Eq 31:
var s = find_inverse_s(p, q);
var s_2 = s * s;
var s_3 = s_2 * s;
var s_4 = s_2 * s_2;
var s_5 = s_4 * s;
var ra = Math.sqrt(a);
var w = a + s * ra + (s * s -1) / 3;
w += (s_3 - 7 * s) / (36 * ra);
w -= (3 * s_4 + 7 * s_2 - 16) / (810 * a);
w += (9 * s_5 + 256 * s_3 - 433 * s) / (38880 * a * ra);
if((a >= 500) && (Math.abs(1 - w / a) < 1e-6))
{
result = w;
}
else if (p > 0.5)
{
if(w < 3 * a)
{
result = w;
}
else
{
var D = Math.max((2), (a * (a - 1)));
var lg = 0;
var a2 = a*a;
var a3 = a*a2;
var a4 = a*a3;
var err_term = 1 + (1.0/(12*a))+(1.0/(288*a2))-(139/(51840*a3))
- (571/(2488320*a4));
if (a > 171)
lg = 0.5*(Math.log(2*Math,PI) - Math.log(a)) + (a * Math.log(a)) - a + Math.log(err_term);
else
lg = Math.log(land_ios_gamma(a));
var lb = Math.log(q) + lg;
if(lb < -D * 2.3)
{
// DiDonato and Morris Eq 25:
var y = -lb;
var c1 = (a - 1) * Math.log(y);
var c1_2 = c1 * c1;
var c1_3 = c1_2 * c1;
var c1_4 = c1_2 * c1_2;
var a_2 = a * a;
var a_3 = a_2 * a;
var c2 = (a - 1) * (1 + c1);
var c3 = (a - 1) * (-(c1_2 / 2) + (a - 2) * c1 + (3 * a - 5) / 2);
var c4 = (a - 1) * ((c1_3 / 3)
- (3 * a - 5) * c1_2 / 2 + (a_2 - 6 * a + 7) * c1 + (11 * a_2 - 46 * a + 47) / 6);
var c5 = (a - 1) * (-(c1_4 / 4)
+ (11 * a - 17) * c1_3 / 6
+ (-3 * a_2 + 13 * a -13) * c1_2
+ (2 * a_3 - 25 * a_2 + 72 * a - 61) * c1 / 2
+ (25 * a_3 - 195 * a_2 + 477 * a - 379) / 12);
var y_2 = y * y;
var y_3 = y_2 * y;
var y_4 = y_2 * y_2;
result = y + c1 + (c2 / y) + (c3 / y_2) + (c4 / y_3) + (c5 / y_4);
}
else
{
// DiDonato and Morris Eq 33:
var u = -lb + (a - 1) * Math.log(w) - Math.log(1 + (1 - a) / (1 + w));
result = -lb + (a - 1) * Math.log(u) - Math.log(1 + (1 - a) / (1 + u));
}
}
}
else
{
// DiDonato and Morris Eq 35:
var z = didonato_FN(p, a, w, 0, 0);
z = didonato_FN(p, a, z, 2, 0.0);
z = didonato_FN(p, a, z, 2, 0.0);
z = didonato_FN(p, a, z, 3, 0.0);
if((z <= 0.01 * (a + 1)) || (z > 0.7 * (a + 1)))
{
result = z;
}
else
{
// DiDonato and Morris Eq 36:
var zb = didonato_FN(p, a, z, 100, 1e-4);
var lg = 0;
var ap = a + 1;
var ap2 = ap*ap;
var ap3 = ap*ap2;
var ap4 = ap*ap3;
var err_term = 1 + (1.0/(12*ap))+(1.0/(288*ap2))-(139/(51840*ap3))
- (571/(2488320*ap4));
if (ap > 171)
lg = 0.5*(Math.log(2*Math.PI) - Math.log(ap)) + (ap * Math.log(ap)) - a + Math.log(err_term);
else
lg = Math.log(land_ios_gamma(ap));
// double u = log(p) + log(gamma(a + 1));
var u = Math.log(p) + lg;
result = zb * (1 - (a * Math.log(zb) - zb - u + Math.log(didonato_SN(a, z, 100, 1e-4))) / (a - zb));
}
}
}
// Do a couple of Halley iterations
var f, df, d2f, result_h;
df = 0.0; d2f = 0.0;
f = 0.0;
result_h = result;
var ga = 1.0/land_ios_gamma(a);
var lg = 0;
var a2 = a*a;
var a3 = a*a2;
var a4 = a*a3;
var err_term = 1 + (1.0/(12*a))+(1.0/(288*a2))-(139/(51840*a3))
- (571/(2488320*a4));
if (a > 171)
lg = 0.5*(Math.log(2*Math.PI) - Math.log(a)) + (a * Math.log(a)) - a + Math.log(err_term);
else
lg = Math.log(land_ios_gamma(a));
for (m =0; m < 20; m++)
{
f = land_gammainc_eval(result_h,a,0) - p;
df = ga * Math.exp( (a - 1)*Math.log(result_h) - result_h);
d2f = 1 * (((a - 1.0)/result_h) - 1.0) * df;
var delta = ((a - 1.0)/result_h) - 1;
df = Math.exp((a - 1)*Math.log(result_h) - result_h - lg);
result_h = result_h - (2*f)/(2*df - (f*delta));
}
return result_h;
}
// DiDonato based functions
function didonato_FN(p, a, x, N, tolerance)
{
//
// Computation of the Incomplete Gamma Function Ratios and their Inverse
// ARMIDO R. DIDONATO and ALFRED H. MORRIS, JR.
// ACM Transactions on Mathematical Software, Vol. 12, No. 4,
// December 1986, Pages 377-393.
//
// See equation 34.
//
var lg_res = 0;
var ap = a + 1;
var ap2 = ap*ap;
var ap3 = ap*ap2;
var ap4 = ap*ap3;
var err_term = 1 + (1.0/(12*ap))+(1.0/(288*ap2))-(139/(51840*ap3))
- (571/(2488320*ap4));
if (ap > 171)
{
lg_res = 0.5*(Math.log(2*Math.PI) - Math.log(ap)) + (ap * Math.log(ap)) - ap + Math.log(err_term);
}
else
{
lg_res = Math.log(land_ios_gamma(ap));
}
// double u = log(p) + log(gamma(a + 1));
var u = Math.log(p) + lg_res;
return Math.exp((u + x - Math.log(didonato_SN(a, x, N, tolerance))) / a);
}
function didonato_SN(a, x, N, tolerance)
{
//
// Computation of the Incomplete Gamma Function Ratios and their Inverse
// ARMIDO R. DIDONATO and ALFRED H. MORRIS, JR.
// ACM Transactions on Mathematical Software, Vol. 12, No. 4,
// December 1986, Pages 377-393.
//
// See equation 34.
//
var sum = 1;
if(N >= 1)
{
var partial = x / (a + 1);
sum = sum + partial;
for(i = 2; i <= N; ++i)
{
partial = partial * (x / (a + i));
sum = sum + partial;
if(partial < tolerance)
break;
}
}
return sum;
}
function find_inverse_s(p, q)
{
//
// Computation of the Incomplete Gamma Function Ratios and their Inverse
// ARMIDO R. DIDONATO and ALFRED H. MORRIS, JR.
// ACM Transactions on Mathematical Software, Vol. 12, No. 4,
// December 1986, Pages 377-393.
//
// See equation 32.
//
var t;
if(p < 0.5)
{
t = Math.sqrt(-2 * Math.log(p));
}
else
{
t = Math.sqrt(-2 * Math.log(q));
}
var a = new Array();
a[0] = 3.31125922108741;
a[1] = 11.6616720288968;
a[2] = 4.28342155967104;
a[3] = 0.213623493715853;
var b = new Array();
b[0] = 1;
b[1] = 6.61053765625462;
b[2] = 6.40691597760039;
b[3] = 1.27364489782223;
b[4] = 0.3611708101884203e-1;
var t2 = t * t;
var t3 = t2 * t;
var t4 = t2 * t2;
var num_sum = (a[0]*t3) + (a[1]*t2) + (a[2]*t) + a[3];
var den_sum = (b[0]*t4) + (b[1]*t3) + (b[2]*t2) + (b[3]*t) + b[4];
var s = t - (num_sum/den_sum);
if(p < 0.5)
s = -s;
return s;
}
function erf_taylor(x)
{
var res = 0;
var c = 2.0/Math.sqrt(Math.PI);
for (n = 0; n < 100; n++)
res = res + (Math.pow(-1,n)*Math.pow(x,2*n + 1))/(sFact(n) * ((2*n)+1))
res = c * res
return res
}
function chisquared_pdf(x,k)
{
var g_a;
var a = k/2.0;
if (a >= 171)
g_a = land_lgamma_stirling(a);
else
g_a = Math.log(land_ios_gamma(a));
var bt = Math.exp(-((a*Math.log(2))) - g_a + ((a - 1)*Math.log(x)) - (x/2));
return bt;
}
function bin_cdf(inp,n,p)
{
var res;
res = betainc_num(1-p, n-inp, 1+inp);
return res;
}
function betainc_num(x, a, b)
{
var bt;
var g_ab;
var g_a;
var g_b;
if ((a + b) >= 171)
g_ab = land_lgamma_stirling(a+b);
else
g_ab = Math.log(land_ios_gamma(a+b));
if (a >= 171)
g_a = land_lgamma_stirling(a);
else
g_a = Math.log(land_ios_gamma(a));
if (b >= 171)
g_b = land_lgamma_stirling(b);
else
g_b = Math.log(land_ios_gamma(b));
bt = Math.exp(g_ab - g_a - g_b + a*Math.log(x)+b*Math.log(1.0-x));
if (x == 0)
bt = 0;
if (x < ((a + 1.0)/(a + b + 2.0)))
return bt*betacf(a,b,x)/a;
else
return 1 - (bt*betacf(b,a,1.0-x)/b);
}
function betacf(a, b, x)
{
var maxit = 100;
var eps = 3e-16;
var fpmin = 1e-30;
var aa;
var c;
var d;
var del;
var h;
var qab;
var qam;
var qap;
qab = a + b;
qap = a + 1;
qam = a - 1;
c = 1.0;
d = 1.0 - qab*x/qap;
if (Math.abs(d)<fpmin)
d = fpmin;
d = 1.0/d;
h = d;
var m2;
for (m = 1; m < maxit; m++)
{
m2 = 2*m;
aa = m*(b-m)*x/((qam + m2)*(a + m2));
d = 1.0 + aa*d;
if (Math.abs(d)<fpmin)
d = fpmin;
c = 1.0 + aa/c;
if (Math.abs(c)<fpmin)
c = fpmin;
d = 1.0/d;
h = h*d*c;
aa = -(a + m)*(qab + m)*x/((a+m2)*(qap+m2));
d = 1.0 + aa*d;
if (Math.abs(d)<fpmin)
d = fpmin;
c = 1.0 + aa/c;
if (Math.abs(c)<fpmin)
c = fpmin;
d = 1.0/d;
del = d*c;
h = h*del;
if (Math.abs(del-1.0)< eps)
{
// std::cout << "Breaking out at iter " << m << std::endl;
break;
}
}
// std::cout << " h is " << h << std::endl;
return h;
}
function land_ios_gamma(x)
{
var g = 7;
var y;
var t;
var res_fr;
var p = new Array()
p[0] = 0.99999999999980993;
p[1] = 676.5203681218851;
p[2] = -1259.1392167224028;
p[3] = 771.32342877765313;
p[4] = -176.61502916214059;
p[5] = 12.507343278686905;
p[6] = -0.13857109526572012;
p[7] = 9.9843695780195716e-6;
p[8] = 1.5056327351493116e-7;
if (Math.abs(x - Math.floor(x)) < 1e-16)
{
if ( x > 1)
return sFact(x - 1);
else if (x == 1)
return 1;
else
return 1/0.0;
}
else
{
x -= 1;
y = p[0];
for (i=1; i < g+2; i++)
{
y = y + p[i]/(x + i);
}
t = x + g + 0.5;
res_fr = Math.sqrt(2*Math.PI) * Math.exp(((x+0.5)*Math.log(t))-t)*y;
return res_fr;
}
}
function land_lgamma_stirling(x)
{
var t = 0.5*Math.log(2*Math.PI) - 0.5*Math.log(x) + x*(Math.log(x))-x;
var x2 = x * x;
var x3 = x2 * x;
var x4 = x3 * x;
var err_term = Math.log(1 + (1.0/(12*x)) + (1.0/(288*x2)) - (139.0/(51840*x3))
- (571.0/(2488320*x4)));
var res = t + err_term;
return res;
}
function sFact(num)
{
var rval=1;
for (var i = 2; i <= num; i++)
rval = rval * i;
return rval;
}
function inverse_beta(p, alpha, beta)
{
var x = 0;
var a = 0;
var b = 1;
var precision = 1e-15;
var iter_num = 0;
while (((b - a) > precision) & (iter_num < 100))
{
x = (a + b) / 2;
if (betainc_num(x,alpha,beta) > p)
b = x;
else
a = x;
iter_num = iter_num + 1;
}
return x;
}
</script>Alijah Ahmedhttp://www.blogger.com/profile/01151589526613233918noreply@blogger.com17tag:blogger.com,1999:blog-8441309013858171051.post-67952138032915347182013-12-08T02:06:00.003-08:002023-12-11T14:10:21.069-08:00Levene's Test: Equality of variances <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<script language="Javascript">
/**
* jQuery-csv (jQuery Plugin)
*
* This document is licensed as free software under the terms of the
* MIT License: http://www.opensource.org/licenses/mit-license.php
*
* Acknowledgements:
* The original design and influence to implement this library as a jquery
* plugin is influenced by jquery-json (http://code.google.com/p/jquery-json/).
* If you're looking to use native JSON.Stringify but want additional backwards
* compatibility for browsers that don't support it, I highly recommend you
* check it out.
*
* A special thanks goes out to rwk@acm.org for providing a lot of valuable
* feedback to the project including the core for the new FSM
* (Finite State Machine) parsers. If you're looking for a stable TSV parser
* be sure to take a look at jquery-tsv (http://code.google.com/p/jquery-tsv/).
* For legal purposes I'll include the "NO WARRANTY EXPRESSED OR IMPLIED.
* USE AT YOUR OWN RISK.". Which, in 'layman's terms' means, by using this
* library you are accepting responsibility if it breaks your code.
*
* Legal jargon aside, I will do my best to provide a useful and stable core
* that can effectively be built on.
*
* Copyrighted 2012 by Evan Plaice.
*/
RegExp.escape= function(s) {
return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
};
(function (undefined) {
'use strict';
var $;
// to keep backwards compatibility
if (typeof jQuery !== 'undefined' && jQuery) {
$ = jQuery;
} else {
$ = {};
}
/**
* jQuery.csv.defaults
* Encapsulates the method paramater defaults for the CSV plugin module.
*/
$.csv = {
defaults: {
separator:',',
delimiter:'"',
headers:true
},
hooks: {
castToScalar: function(value, state) {
var hasDot = /\./;
if (isNaN(value)) {
return value;
} else {
if (hasDot.test(value)) {
return parseFloat(value);
} else {
var integer = parseInt(value);
if(isNaN(integer)) {
return null;
} else {
return integer;
}
}
}
}
},
parsers: {
parse: function(csv, options) {
// cache settings
var separator = options.separator;
var delimiter = options.delimiter;
// set initial state if it's missing
if(!options.state.rowNum) {
options.state.rowNum = 1;
}
if(!options.state.colNum) {
options.state.colNum = 1;
}
// clear initial state
var data = [];
var entry = [];
var state = 0;
var value = '';
var exit = false;
function endOfEntry() {
// reset the state
state = 0;
value = '';
// if 'start' hasn't been met, don't output
if(options.start && options.state.rowNum < options.start) {
// update global state
entry = [];
options.state.rowNum++;
options.state.colNum = 1;
return;
}
if(options.onParseEntry === undefined) {
// onParseEntry hook not set
data.push(entry);
} else {
var hookVal = options.onParseEntry(entry, options.state); // onParseEntry Hook
// false skips the row, configurable through a hook
if(hookVal !== false) {
data.push(hookVal);
}
}
//console.log('entry:' + entry);
// cleanup
entry = [];
// if 'end' is met, stop parsing
if(options.end && options.state.rowNum >= options.end) {
exit = true;
}
// update global state
options.state.rowNum++;
options.state.colNum = 1;
}
function endOfValue() {
if(options.onParseValue === undefined) {
// onParseValue hook not set
entry.push(value);
} else {
var hook = options.onParseValue(value, options.state); // onParseValue Hook
// false skips the row, configurable through a hook
if(hook !== false) {
entry.push(hook);
}
}
//console.log('value:' + value);
// reset the state
value = '';
state = 0;
// update global state
options.state.colNum++;
}
// escape regex-specific control chars
var escSeparator = RegExp.escape(separator);
var escDelimiter = RegExp.escape(delimiter);
// compile the regEx str using the custom delimiter/separator
var match = /(D|S|\r\n|\n|\r|[^DS\r\n]+)/;
var matchSrc = match.source;
matchSrc = matchSrc.replace(/S/g, escSeparator);
matchSrc = matchSrc.replace(/D/g, escDelimiter);
match = new RegExp(matchSrc, 'gm');
// put on your fancy pants...
// process control chars individually, use look-ahead on non-control chars
csv.replace(match, function (m0) {
if(exit) {
return;
}
switch (state) {
// the start of a value
case 0:
// null last value
if (m0 === separator) {
value += '';
endOfValue();
break;
}
// opening delimiter
if (m0 === delimiter) {
state = 1;
break;
}
// null last value
if (/^(\r\n|\n|\r)$/.test(m0)) {
endOfValue();
endOfEntry();
break;
}
// un-delimited value
value += m0;
state = 3;
break;
// delimited input
case 1:
// second delimiter? check further
if (m0 === delimiter) {
state = 2;
break;
}
// delimited data
value += m0;
state = 1;
break;
// delimiter found in delimited input
case 2:
// escaped delimiter?
if (m0 === delimiter) {
value += m0;
state = 1;
break;
}
// null value
if (m0 === separator) {
endOfValue();
break;
}
// end of entry
if (/^(\r\n|\n|\r)$/.test(m0)) {
endOfValue();
endOfEntry();
break;
}
// broken paser?
throw new Error('CSVDataError: Illegal State [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
// un-delimited input
case 3:
// null last value
if (m0 === separator) {
endOfValue();
break;
}
// end of entry
if (/^(\r\n|\n|\r)$/.test(m0)) {
endOfValue();
endOfEntry();
break;
}
if (m0 === delimiter) {
// non-compliant data
throw new Error('CSVDataError: Illegal Quote [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
}
// broken parser?
throw new Error('CSVDataError: Illegal Data [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
default:
// shenanigans
throw new Error('CSVDataError: Unknown State [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
}
//console.log('val:' + m0 + ' state:' + state);
});
// submit the last entry
// ignore null last line
if(entry.length !== 0) {
endOfValue();
endOfEntry();
}
return data;
},
// a csv-specific line splitter
splitLines: function(csv, options) {
// cache settings
var separator = options.separator;
var delimiter = options.delimiter;
// set initial state if it's missing
if(!options.state.rowNum) {
options.state.rowNum = 1;
}
// clear initial state
var entries = [];
var state = 0;
var entry = '';
var exit = false;
function endOfLine() {
// reset the state
state = 0;
// if 'start' hasn't been met, don't output
if(options.start && options.state.rowNum < options.start) {
// update global state
entry = '';
options.state.rowNum++;
return;
}
if(options.onParseEntry === undefined) {
// onParseEntry hook not set
entries.push(entry);
} else {
var hookVal = options.onParseEntry(entry, options.state); // onParseEntry Hook
// false skips the row, configurable through a hook
if(hookVal !== false) {
entries.push(hookVal);
}
}
// cleanup
entry = '';
// if 'end' is met, stop parsing
if(options.end && options.state.rowNum >= options.end) {
exit = true;
}
// update global state
options.state.rowNum++;
}
// escape regex-specific control chars
var escSeparator = RegExp.escape(separator);
var escDelimiter = RegExp.escape(delimiter);
// compile the regEx str using the custom delimiter/separator
var match = /(D|S|\n|\r|[^DS\r\n]+)/;
var matchSrc = match.source;
matchSrc = matchSrc.replace(/S/g, escSeparator);
matchSrc = matchSrc.replace(/D/g, escDelimiter);
match = new RegExp(matchSrc, 'gm');
// put on your fancy pants...
// process control chars individually, use look-ahead on non-control chars
csv.replace(match, function (m0) {
if(exit) {
return;
}
switch (state) {
// the start of a value/entry
case 0:
// null value
if (m0 === separator) {
entry += m0;
state = 0;
break;
}
// opening delimiter
if (m0 === delimiter) {
entry += m0;
state = 1;
break;
}
// end of line
if (m0 === '\n') {
endOfLine();
break;
}
// phantom carriage return
if (/^\r$/.test(m0)) {
break;
}
// un-delimit value
entry += m0;
state = 3;
break;
// delimited input
case 1:
// second delimiter? check further
if (m0 === delimiter) {
entry += m0;
state = 2;
break;
}
// delimited data
entry += m0;
state = 1;
break;
// delimiter found in delimited input
case 2:
// escaped delimiter?
var prevChar = entry.substr(entry.length - 1);
if (m0 === delimiter && prevChar === delimiter) {
entry += m0;
state = 1;
break;
}
// end of value
if (m0 === separator) {
entry += m0;
state = 0;
break;
}
// end of line
if (m0 === '\n') {
endOfLine();
break;
}
// phantom carriage return
if (m0 === '\r') {
break;
}
// broken paser?
throw new Error('CSVDataError: Illegal state [Row:' + options.state.rowNum + ']');
// un-delimited input
case 3:
// null value
if (m0 === separator) {
entry += m0;
state = 0;
break;
}
// end of line
if (m0 === '\n') {
endOfLine();
break;
}
// phantom carriage return
if (m0 === '\r') {
break;
}
// non-compliant data
if (m0 === delimiter) {
throw new Error('CSVDataError: Illegal quote [Row:' + options.state.rowNum + ']');
}
// broken parser?
throw new Error('CSVDataError: Illegal state [Row:' + options.state.rowNum + ']');
default:
// shenanigans
throw new Error('CSVDataError: Unknown state [Row:' + options.state.rowNum + ']');
}
//console.log('val:' + m0 + ' state:' + state);
});
// submit the last entry
// ignore null last line
if(entry !== '') {
endOfLine();
}
return entries;
},
// a csv entry parser
parseEntry: function(csv, options) {
// cache settings
var separator = options.separator;
var delimiter = options.delimiter;
// set initial state if it's missing
if(!options.state.rowNum) {
options.state.rowNum = 1;
}
if(!options.state.colNum) {
options.state.colNum = 1;
}
// clear initial state
var entry = [];
var state = 0;
var value = '';
function endOfValue() {
if(options.onParseValue === undefined) {
// onParseValue hook not set
entry.push(value);
} else {
var hook = options.onParseValue(value, options.state); // onParseValue Hook
// false skips the value, configurable through a hook
if(hook !== false) {
entry.push(hook);
}
}
// reset the state
value = '';
state = 0;
// update global state
options.state.colNum++;
}
// checked for a cached regEx first
if(!options.match) {
// escape regex-specific control chars
var escSeparator = RegExp.escape(separator);
var escDelimiter = RegExp.escape(delimiter);
// compile the regEx str using the custom delimiter/separator
var match = /(D|S|\n|\r|[^DS\r\n]+)/;
var matchSrc = match.source;
matchSrc = matchSrc.replace(/S/g, escSeparator);
matchSrc = matchSrc.replace(/D/g, escDelimiter);
options.match = new RegExp(matchSrc, 'gm');
}
// put on your fancy pants...
// process control chars individually, use look-ahead on non-control chars
csv.replace(options.match, function (m0) {
switch (state) {
// the start of a value
case 0:
// null last value
if (m0 === separator) {
value += '';
endOfValue();
break;
}
// opening delimiter
if (m0 === delimiter) {
state = 1;
break;
}
// skip un-delimited new-lines
if (m0 === '\n' || m0 === '\r') {
break;
}
// un-delimited value
value += m0;
state = 3;
break;
// delimited input
case 1:
// second delimiter? check further
if (m0 === delimiter) {
state = 2;
break;
}
// delimited data
value += m0;
state = 1;
break;
// delimiter found in delimited input
case 2:
// escaped delimiter?
if (m0 === delimiter) {
value += m0;
state = 1;
break;
}
// null value
if (m0 === separator) {
endOfValue();
break;
}
// skip un-delimited new-lines
if (m0 === '\n' || m0 === '\r') {
break;
}
// broken paser?
throw new Error('CSVDataError: Illegal State [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
// un-delimited input
case 3:
// null last value
if (m0 === separator) {
endOfValue();
break;
}
// skip un-delimited new-lines
if (m0 === '\n' || m0 === '\r') {
break;
}
// non-compliant data
if (m0 === delimiter) {
throw new Error('CSVDataError: Illegal Quote [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
}
// broken parser?
throw new Error('CSVDataError: Illegal Data [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
default:
// shenanigans
throw new Error('CSVDataError: Unknown State [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
}
//console.log('val:' + m0 + ' state:' + state);
});
// submit the last value
endOfValue();
return entry;
}
},
helpers: {
/**
* $.csv.helpers.collectPropertyNames(objectsArray)
* Collects all unique property names from all passed objects.
*
* @param {Array} objects Objects to collect properties from.
*
* Returns an array of property names (array will be empty,
* if objects have no own properties).
*/
collectPropertyNames: function (objects) {
var o, propName, props = [];
for (o in objects) {
for (propName in objects[o]) {
if ((objects[o].hasOwnProperty(propName)) &&
(props.indexOf(propName) < 0) &&
(typeof objects[o][propName] !== 'function')) {
props.push(propName);
}
}
}
return props;
}
},
/**
* $.csv.toArray(csv)
* Converts a CSV entry string to a javascript array.
*
* @param {Array} csv The string containing the CSV data.
* @param {Object} [options] An object containing user-defined options.
* @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
* @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
*
* This method deals with simple CSV strings only. It's useful if you only
* need to parse a single entry. If you need to parse more than one line,
* use $.csv2Array instead.
*/
toArray: function(csv, options, callback) {
options = (options !== undefined ? options : {});
var config = {};
config.callback = ((callback !== undefined && typeof(callback) === 'function') ? callback : false);
config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator;
config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter;
var state = (options.state !== undefined ? options.state : {});
// setup
options = {
delimiter: config.delimiter,
separator: config.separator,
onParseEntry: options.onParseEntry,
onParseValue: options.onParseValue,
state: state
};
var entry = $.csv.parsers.parseEntry(csv, options);
// push the value to a callback if one is defined
if(!config.callback) {
return entry;
} else {
config.callback('', entry);
}
},
/**
* $.csv.toArrays(csv)
* Converts a CSV string to a javascript array.
*
* @param {String} csv The string containing the raw CSV data.
* @param {Object} [options] An object containing user-defined options.
* @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
* @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
*
* This method deals with multi-line CSV. The breakdown is simple. The first
* dimension of the array represents the line (or entry/row) while the second
* dimension contains the values (or values/columns).
*/
toArrays: function(csv, options, callback) {
options = (options !== undefined ? options : {});
var config = {};
config.callback = ((callback !== undefined && typeof(callback) === 'function') ? callback : false);
config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator;
config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter;
// setup
var data = [];
options = {
delimiter: config.delimiter,
separator: config.separator,
onPreParse: options.onPreParse,
onParseEntry: options.onParseEntry,
onParseValue: options.onParseValue,
onPostParse: options.onPostParse,
start: options.start,
end: options.end,
state: {
rowNum: 1,
colNum: 1
}
};
// onPreParse hook
if(options.onPreParse !== undefined) {
options.onPreParse(csv, options.state);
}
// parse the data
data = $.csv.parsers.parse(csv, options);
// onPostParse hook
if(options.onPostParse !== undefined) {
options.onPostParse(data, options.state);
}
// push the value to a callback if one is defined
if(!config.callback) {
return data;
} else {
config.callback('', data);
}
},
/**
* $.csv.toObjects(csv)
* Converts a CSV string to a javascript object.
* @param {String} csv The string containing the raw CSV data.
* @param {Object} [options] An object containing user-defined options.
* @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
* @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
* @param {Boolean} [headers] Indicates whether the data contains a header line. Defaults to true.
*
* This method deals with multi-line CSV strings. Where the headers line is
* used as the key for each value per entry.
*/
toObjects: function(csv, options, callback) {
options = (options !== undefined ? options : {});
var config = {};
config.callback = ((callback !== undefined && typeof(callback) === 'function') ? callback : false);
config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator;
config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter;
config.headers = 'headers' in options ? options.headers : $.csv.defaults.headers;
options.start = 'start' in options ? options.start : 1;
// account for headers
if(config.headers) {
options.start++;
}
if(options.end && config.headers) {
options.end++;
}
// setup
var lines = [];
var data = [];
options = {
delimiter: config.delimiter,
separator: config.separator,
onPreParse: options.onPreParse,
onParseEntry: options.onParseEntry,
onParseValue: options.onParseValue,
onPostParse: options.onPostParse,
start: options.start,
end: options.end,
state: {
rowNum: 1,
colNum: 1
},
match: false,
transform: options.transform
};
// fetch the headers
var headerOptions = {
delimiter: config.delimiter,
separator: config.separator,
start: 1,
end: 1,
state: {
rowNum:1,
colNum:1
}
};
// onPreParse hook
if(options.onPreParse !== undefined) {
options.onPreParse(csv, options.state);
}
// parse the csv
var headerLine = $.csv.parsers.splitLines(csv, headerOptions);
var headers = $.csv.toArray(headerLine[0], options);
// fetch the data
lines = $.csv.parsers.splitLines(csv, options);
// reset the state for re-use
options.state.colNum = 1;
if(headers){
options.state.rowNum = 2;
} else {
options.state.rowNum = 1;
}
// convert data to objects
for(var i=0, len=lines.length; i<len; i++) {
var entry = $.csv.toArray(lines[i], options);
var object = {};
for(var j=0; j <headers.length; j++) {
object[headers[j]] = entry[j];
}
if (options.transform !== undefined) {
data.push(options.transform.call(undefined, object));
} else {
data.push(object);
}
// update row state
options.state.rowNum++;
}
// onPostParse hook
if(options.onPostParse !== undefined) {
options.onPostParse(data, options.state);
}
// push the value to a callback if one is defined
if(!config.callback) {
return data;
} else {
config.callback('', data);
}
},
/**
* $.csv.fromArrays(arrays)
* Converts a javascript array to a CSV String.
*
* @param {Array} arrays An array containing an array of CSV entries.
* @param {Object} [options] An object containing user-defined options.
* @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
* @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
*
* This method generates a CSV file from an array of arrays (representing entries).
*/
fromArrays: function(arrays, options, callback) {
options = (options !== undefined ? options : {});
var config = {};
config.callback = ((callback !== undefined && typeof(callback) === 'function') ? callback : false);
config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator;
config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter;
var output = '',
line,
lineValues,
i, j;
for (i = 0; i < arrays.length; i++) {
line = arrays[i];
lineValues = [];
for (j = 0; j < line.length; j++) {
var strValue = (line[j] === undefined || line[j] === null) ? '' : line[j].toString();
if (strValue.indexOf(config.delimiter) > -1) {
strValue = strValue.replace(config.delimiter, config.delimiter + config.delimiter);
}
var escMatcher = '\n|\r|S|D';
escMatcher = escMatcher.replace('S', config.separator);
escMatcher = escMatcher.replace('D', config.delimiter);
if (strValue.search(escMatcher) > -1) {
strValue = config.delimiter + strValue + config.delimiter;
}
lineValues.push(strValue);
}
output += lineValues.join(config.separator) + '\r\n';
}
// push the value to a callback if one is defined
if(!config.callback) {
return output;
} else {
config.callback('', output);
}
},
/**
* $.csv.fromObjects(objects)
* Converts a javascript dictionary to a CSV string.
*
* @param {Object} objects An array of objects containing the data.
* @param {Object} [options] An object containing user-defined options.
* @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
* @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
* @param {Character} [sortOrder] Sort order of columns (named after
* object properties). Use 'alpha' for alphabetic. Default is 'declare',
* which means, that properties will _probably_ appear in order they were
* declared for the object. But without any guarantee.
* @param {Character or Array} [manualOrder] Manually order columns. May be
* a strin in a same csv format as an output or an array of header names
* (array items won't be parsed). All the properties, not present in
* `manualOrder` will be appended to the end in accordance with `sortOrder`
* option. So the `manualOrder` always takes preference, if present.
*
* This method generates a CSV file from an array of objects (name:value pairs).
* It starts by detecting the headers and adding them as the first line of
* the CSV file, followed by a structured dump of the data.
*/
fromObjects: function(objects, options, callback) {
options = (options !== undefined ? options : {});
var config = {};
config.callback = ((callback !== undefined && typeof(callback) === 'function') ? callback : false);
config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator;
config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter;
config.headers = 'headers' in options ? options.headers : $.csv.defaults.headers;
config.sortOrder = 'sortOrder' in options ? options.sortOrder : 'declare';
config.manualOrder = 'manualOrder' in options ? options.manualOrder : [];
config.transform = options.transform;
if (typeof config.manualOrder === 'string') {
config.manualOrder = $.csv.toArray(config.manualOrder, config);
}
if (config.transform !== undefined) {
var origObjects = objects;
objects = [];
var i;
for (i = 0; i < origObjects.length; i++) {
objects.push(config.transform.call(undefined, origObjects[i]));
}
}
var props = $.csv.helpers.collectPropertyNames(objects);
if (config.sortOrder === 'alpha') {
props.sort();
} // else {} - nothing to do for 'declare' order
if (config.manualOrder.length > 0) {
var propsManual = [].concat(config.manualOrder);
var p;
for (p = 0; p < props.length; p++) {
if (propsManual.indexOf( props[p] ) < 0) {
propsManual.push( props[p] );
}
}
props = propsManual;
}
var o, p, line, output = [], propName;
if (config.headers) {
output.push(props);
}
for (o = 0; o < objects.length; o++) {
line = [];
for (p = 0; p < props.length; p++) {
propName = props[p];
if (propName in objects[o] && typeof objects[o][propName] !== 'function') {
line.push(objects[o][propName]);
} else {
line.push('');
}
}
output.push(line);
}
// push the value to a callback if one is defined
return $.csv.fromArrays(output, options, config.callback);
}
};
// Maintenance code to maintain backward-compatibility
// Will be removed in release 1.0
$.csvEntry2Array = $.csv.toArray;
$.csv2Array = $.csv.toArrays;
$.csv2Dictionary = $.csv.toObjects;
// CommonJS module is defined
if (typeof module !== 'undefined' && module.exports) {
module.exports = $.csv;
}
}).call( this );
</script>
<script>
$(document).ready(function() {
if(isAPIAvailable()) {
$('#files').bind('change', handleFileSelect);
}
});
function isAPIAvailable() {
// Check for the various File API support.
if (window.File && window.FileReader && window.FileList && window.Blob) {
// Great success! All the File APIs are supported.
return true;
} else {
// source: File API availability - http://caniuse.com/#feat=fileapi
// source: <output> availability - http://html5doctor.com/the-output-element/
document.writeln('The HTML5 APIs used in this form are only available in the following browsers:<br />');
// 6.0 File API & 13.0 <output>
document.writeln(' - Google Chrome: 13.0 or later<br />');
// 3.6 File API & 6.0 <output>
document.writeln(' - Mozilla Firefox: 6.0 or later<br />');
// 10.0 File API & 10.0 <output>
document.writeln(' - Internet Explorer: Not supported (partial support expected in 10.0)<br />');
// ? File API & 5.1 <output>
document.writeln(' - Safari: Not supported<br />');
// ? File API & 9.2 <output>
document.writeln(' - Opera: Not supported');
return false;
}
}
function handleFileSelect(evt) {
var files = evt.target.files; // FileList object
var file = files[0];
// read the file metadata
var output = ''
output += '<span style="font-weight:bold;">' + escape(file.name) + '</span><br />\n';
output += ' - FileType: ' + (file.type || 'n/a') + '<br />\n';
output += ' - FileSize: ' + file.size + ' bytes<br />\n';
output += ' - LastModified: ' + (file.lastModifiedDate ? file.lastModifiedDate.toLocaleDateString() : 'n/a') + '<br />\n';
// read the file contents
printTextArea(file);
// post the results
$('#list').append(output);
}
function printTextArea(file) {
var container = document.getElementById("area");
var inps = container.getElementsByTagName("input");
var kgr = inps.length;
var idx_selected = 0;
for (k=0; k < kgr; k++)
{
if (inps.item(k).value == "")
{
idx_selected = k;
break;
}
}
var reader = new FileReader();
reader.readAsText(file);
reader.onload = function(event){
var csv = event.target.result;
var data = $.csv.toArrays(csv);
var multi_col = 0;
var mystr = new Array();
for (k=0;k<kgr;k++)
mystr[k] = '';
for(var row in data) {
if (!isNaN(Number(data[row][0])))
{
if (data[row].length > 1)
{
multi_col = 1;
for (k=0; k < kgr; k++)
{
mystr[k] = (mystr[k]).concat((Number(data[row][k]).toFixed(6)).toString());
mystr[k] = (mystr[k]).concat(",");
}
}
else
{
mystr[0] = (mystr[0]).concat((Number(data[row][0]).toFixed(6)).toString());
mystr[0] = (mystr[0]).concat(",");
}
}
}
if (multi_col == 0)
{
mystr[0] = (mystr[0]).substring(0, mystr[0].length - 1)
inps.item(idx_selected).value = mystr[0];
}
else
{
for (k=0; k < kgr; k++)
{
mystr[k] = (mystr[k]).substring(0, mystr[k].length - 1)
inps.item(k).value = mystr[k];
}
}
};
reader.onerror = function(){ alert('Unable to read ' + file.fileName); };
}
</script>
<!--[if IE ]>
<script type="text/javascript" src="https://firebasestorage.googleapis.com/v0/b/js-static-assets.appspot.com/o/excanvas_min.js?alt=media&token=9dd78ad2-f09b-45dd-8a09-8db2b2546d1d"></script>
<![endif]-->
<script type="text/javascript" src="https://firebasestorage.googleapis.com/v0/b/js-static-assets.appspot.com/o/jquery_flot.js?alt=media&token=bde69a50-3df9-4794-b7d8-dd4e840472cd"></script>
</head>
<body>
<a onclick='addTextBox()' href='#'>Please click to add a dataset group - need at least three</a>
<br/>
<br/>
<!-- Textbox will be added in followng DIV -->
<div id='area'></div>
<div id=inputs class=clearfix>
<input type=file id=files name=files[] multiple />
</div>
<p style="text-align:justify"> There are statistical tests that involve three or more groups or datasets, and that require the variances of all the groups to be the same. If the datasets do not have a Normal distribution, Levene's test can be used to test for equality (more precisely, homogeneity) of variances between groups. However, for Normal distributed data, Bartlett's test will yield a better value.</p>
<p style="text-align:justify"> For methods of calculating the test statistic, please have a look at the National Institute of Standards and Technology (NIST) <a href="http://www.itl.nist.gov/div898/handbook/eda/section3/eda35a.htm" target="_blank">website</a> - the calculator here uses the median method out of the three detailed in the website linked. </p>
<p style="text-align:justify"> Simply click on the link at the top of this post to text boxes. Each text box stores a single group/dataset and needs to be filled in with comma separated numbers. Alternatively, you can choose two file entry methods:-
<ol>
<li>Select multiple single column CSV files to populate the text boxes by repeatedly pressing the Choose File button - there must be one distinct (and differently named) file for each text box i.e. one file per group. Each file can have a different number of samples.</li>
<li>Select a single multi-column CSV file by pressing the Choose File button once, where the number of columns equals the number of groups - all groups need to have the same number of samples.</li>
</ol>
</p>
<p style="text-align:justify"> There is a graph at the bottom which will display scatter plots for all the groups, once the calculate button is pressed.</p>
<!-- Just call function 'addTextBox()' to add textbox -->
<br>
<input name="b1" onclick="calculate_levene()" style="margin-left: 0px;" type="Button" value="Calculate.." />
<p id="p1">Results pending...</p>
<input name="b1" onclick="clearReset()" style="margin-left: 0px;" type="Button" value="Reset Results log" />
<br>
<table style="text-align: center;">
<tbody>
<tr>
<td></td>
<td>Scatter plot of all the groups</td>
<td></td>
</tr>
<tr>
<td>Sample values</td>
<td><div id='chart1' style='height:300px;width:500px;'>
</div>
</td>
<td></td>
</tr>
<tr>
<td></td>
<td>Group number</td>
<td></td>
</tr>
</tbody></table>
</body>
</html>
<script language="javascript">
var series1 = [ [0,0], [1,0], [2,0], [3,0], [4,0] ];
var data = [ series1 ];
$.plot($("#chart1"), data);
var inival=0; // Initialise starting element number
function calculate_levene() {
var res_str = '';
var tmp_str = '';
var str = inival.toString();
var container = document.getElementById("area");
var inps = container.getElementsByTagName("input");
var kgr = inps.length;
res_str = "Number of groups is " + kgr;
var N = 0;
var Ni = new Array();
var Zi = new Array();
var Zall = 0;
var denom = 0;
var Yi = new Array();
var mu;
var std2;
var nsamp;
var datavec = new Array();
var ent_set = new Array();
var gidx = 0;
var numer = 0;
// Calculate medians
for (var k = 0; k < kgr; k++)
{
datavec = (inps.item(k).value).split(',');
N = N + datavec.length;
var tmp_vec = new Array();
for (m=0; m < datavec.length; m++)
tmp_vec[m] = Number(datavec[m]);
tmp_vec.sort(compare);
if ((datavec.length % 2) == 0)
Yi[k] = 0.5 * (tmp_vec[(datavec.length/2) - 1] + tmp_vec[(datavec.length/2)]);
else
Yi[k] = tmp_vec[(datavec.length - 1)/2];
for (m=0; m < datavec.length; m++)
Zall = Zall + Math.abs(Number(datavec[m]) - Yi[k]);
}
Zall = Zall/N;
for (k = 0; k < kgr; k++)
{
datavec = (inps.item(k).value).split(',');
nsamp = datavec.length;
Ni[k] = nsamp;
Zi[k] = 0;
for (m=0; m < nsamp; m++)
{
Zi[k] = Zi[k] + Math.abs(Number(datavec[m]) - Yi[k]);
ent_set[gidx] = [k+1,Number(datavec[m])];
gidx = gidx + 1;
}
Zi[k] = Zi[k]/nsamp;
numer = numer + (nsamp * (Zi[k] - Zall) * (Zi[k] - Zall));
// Calculate the median
for (m = 0; m < nsamp; m++)
{
var tmpval = Math.abs(Number(datavec[m]) - Yi[k]) - Zi[k];
denom = denom + (tmpval * tmpval);
}
var idx = k+1;
res_str = res_str.concat("<br><br>");
tmp_str = "Median of Group " + idx + " is " + Yi[k].toFixed(6);
res_str = res_str.concat(tmp_str);
}
var numer = 0;
for (k = 0; k < kgr; k++)
{
numer = numer + (Ni[k] * (Zi[k] - Zall)*(Zi[k] - Zall));
}
var W = ((N - kgr)/(kgr - 1)) * (numer/denom);
tmp_str = "<br><br>W statistic is " + W.toFixed(6) + "<br>";
res_str = res_str.concat(tmp_str);
var p_val = 1 - f_cdf(W,kgr - 1,N - kgr);
tmp_str = "Corresponding p-value is " + p_val.toFixed(6) + "<br>";
res_str = res_str.concat(tmp_str);
var W_critical = f_icdf(0.95,kgr - 1, N - kgr);
tmp_str = "Critical value W statistic is " + W_critical.toFixed(6) + "<br>";
res_str = res_str.concat(tmp_str);
var reject = 0
if (W < W_critical)
tmp_str = "Accept Null Hypothesis that variances are equal for all groups";
else
{
reject = 1;
tmp_str = "Reject Null Hypothesis that variances are equal - result is significant";
}
res_str = res_str.concat(tmp_str);
res_str = res_str.concat("<br><br>");
if (reject == 1)
res_str = res_str.fontcolor("red");
else
res_str = res_str.fontcolor("white");
document.getElementById("p1").innerHTML=res_str;
var ser1 = {
label: "",
data: ent_set,
color: "#0000ff",
points: { show: true },
lines: { show: false },
bars: { show: false },
yaxis: 1
};
var data2 = [ser1];
$.plot($("#chart1"), data2);
}
function ClearReset()
{
document.getElementById("p1").innerHTML="Results pending..";
}
// Call this function to add textbox
function addTextBox()
{
var newArea = add_New_Element();
var str = inival.toString();
var htcontents = "Group ";
htcontents = htcontents.concat(inival.toString());
htcontents = htcontents.concat(": <input type='textArea' style='width:300px;' name='textbx[]' />");
htcontents = htcontents.concat("<br><br>");
document.getElementById(newArea).innerHTML = htcontents; // You can any other elements in place of 'htcontents'
}
function add_New_Element() {
inival=inival+1; // Increment element number by 1
var ni = document.getElementById('area');
var newdiv = document.createElement('div'); // Create dynamic element
var divIdName = 'my'+inival+'Div';
newdiv.setAttribute('id',divIdName);
ni.appendChild(newdiv);
return divIdName;
}
function compare(a,b) {
if (a < b)
return -1;
if (a > b)
return 1;
return 0;
}
function f_cdf(inp,p1,p2)
{
var res;
res = betainc_num((p1*inp)/((p1*inp)+p2), p1/2, p2/2);
return res;
}
function f_icdf(prob,p1,p2)
{
var res;
res = inverse_beta(prob, p1/2, p2/2);
res = (p2*res)/(p1 - (p1*res));
if (prob == 0)
res = 0;
return res;
}
function land_gammainc_eval(x, a, upper)
{
var tmp;
var xeg = ((1 + 0.0)/land_ios_gamma(a+1))*Math.pow(x+0.0,a)*Math.exp(-x);
var acc = 0.0;
var Pax;
var Qax;
var lambda;
var eta;
var gamma_star;
var var1;
var acc_f;
var SaEta,RaEta;
var f = new Array();
var b = new Array();
var ak_vec = new Array();
var s_vec = new Array();
var t_vec = new Array();
var rho_vec = new Array();
var log_diff;
var NUMSERIES = 100;
var VNUM = 100;
var QSUM = 100;
if (Math.abs(x) < 1e-16)
{
if (upper == 0)
return 0;
else
return 1;
}
if (x <= a)
{
if ((Math.abs(x) < 10) & (Math.abs(a) < 10) )
{
for (k=0; k < NUMSERIES; k++)
{
if (a+k+1 < 170)
acc += Math.pow(x,k+0.0)*((land_ios_gamma(a+1)+0.0)/land_ios_gamma(k+a+1));
else
{
log_diff = land_lgamma_stirling(a+1) - land_lgamma_stirling(k+a+1) + (k * log(x));
acc += Math.exp(log_diff);
}
}
Pax = xeg * acc;
if (upper == 0)
return Pax;
else
return (1-Pax);
}
else
{
lambda = x/a;
eta = Math.sqrt(2*(lambda - 1 - Math.log(lambda)));
if ((lambda - 1) < 0)
eta = -1 * eta;
// For large a, gamma_star approaches unity..
if (a > 140)
gamma_star = 1.0;
else
gamma_star = land_ios_gamma(a)/(Math.sqrt(2*Math.PI) * Math.exp(-a) * Math.pow(a,a-0.5));
for (k=0;k<VNUM;k++)
f[k] = 0.0;
f[0] = 1;
f[1] = -1.0/3.0;
f[2] = 1.0/12.0;
f[3] = -2.0/135.0;
for (m=4; m < VNUM; m++)
{
var1 = ((m - 1.0)/(3.0*m))* f[m-1];
acc_f = 0.0;
for (j=3; j < m; j++)
{
acc_f = acc_f + (f[j-1] * f[m+1-j])/(m+2-j);
}
f[m] = -((m+1.0)/(m+2.0))*(var1 + acc_f);
}
for (k=0; k < VNUM-1; k++)
b[k] = 0.0;
b[VNUM-2] = f[VNUM-1];
b[VNUM-3] = f[VNUM-2];
for (m = VNUM-3; m > 0; m--)
b[m-1] = f[m] + (((m + 1.0)/a)*b[m+1]);
SaEta = 0.0;
for (m=0; m < b.length; m++)
SaEta = SaEta + (b[m] * Math.pow(eta,m+0.0));
SaEta = SaEta/gamma_star;
RaEta = (SaEta/Math.sqrt(2*Math.PI*a))*Math.exp(-0.5*a*eta*eta);
Qax = (0.5 * (1 - erf_taylor(eta*Math.sqrt(a/2.0)))) + RaEta;
Pax = (0.5 * (1 - erf_taylor(-eta*Math.sqrt(a/2.0)))) - RaEta;
if (upper == 1)
return Qax;
else
return Pax;
// EndOf Section that details modification (and improvement)
// to Gautschi's algorithm
}
}
else
{
if (x <= 1)
{
xeg = Math.pow(x,a)/land_ios_gamma(a);
acc_f = 0.0;
for (k=0; k < QSUM; k++)
acc_f += (Math.pow(-1.0,k)*Math.pow(x,k))/((a+k)*Sfact(k));
tmp = xeg * acc_f;
if (upper == 1)
return 1 - tmp;
else
return tmp;
}
else
{
var g1 = (x -a + 1);
for (k=0; k < QSUM; k++)
ak_vec[k] = (k*(a - k))/((x-a+(2*k)-1)*(x-a+(2*k)+1));
for (k=0; k < QSUM; k++)
{
s_vec[k] = 0;
t_vec[k] = 0;
rho_vec[k] = 0;
}
for (k=0; k < QSUM; k++)
{
if (k == 0)
{
s_vec[k] = 0;
rho_vec[k] = 0;
t_vec[k] = 1;
}
else
{
var ak = ak_vec[k];
var rhokm1 = rho_vec[k-1];
rho_vec[k] =(-(ak)*(1 + rhokm1))/(1 + (ak*(1 + rhokm1)));
var rhok = rho_vec[k];
var tkm1 = t_vec[k-1];
t_vec[k] = rhok*tkm1;
var skm1 = s_vec[k-1];
s_vec[k] = skm1 + tkm1;
// [rho_vec[k] = (-(ak_vec[k])*(1 + rho_vec[k-1]))
// /(1 + (ak_vec[k]*(1 + rho_vec[k-1])));
// t_vec[k] = rho_vec[k]*t_vec[k-1];
// s_vec[k] = s_vec[k-1] + t_vec[k-1];
}
}
var slast = s_vec[QSUM-1];
var tmp_scale = (Math.pow(x,a)*Math.exp(-x)/land_ios_gamma(a));
var lga1 = a * Math.log(x);
var lga2 = (0.5 - a) * Math.log(a);
var lga3 = 0.5 * Math.log(2*Math.PI);
var lgtot = lga1 + (a - x) + lga2 - lga3;
if (a > 140)
tmp_scale = Math.exp(lgtot);
var newres;
if (land_ios_gamma(a) == 0.0)
newres = 0;
else
newres = (tmp_scale*slast)/g1;
if (upper == 1)
return newres;
else
return (1 - newres);
// EndOf Gautschi's routine
}
}
}
function find_inverse_gamma(a, p, q)
{
//
// In order to understand what's going on here, you will
// need to refer to:
//
// Computation of the Incomplete Gamma Function Ratios and their Inverse
// ARMIDO R. DIDONATO and ALFRED H. MORRIS, JR.
// ACM Transactions on Mathematical Software, Vol. 12, No. 4,
// December 1986, Pages 377-393.
//
var result;
if(a == 1)
{
result = -Math.log(q);
}
else if(a < 1)
{
var g = land_ios_gamma(a);
var b = q * g;
if((b > 0.6) || ((b >= 0.45) && (a >= 0.3)))
{
// DiDonato & Morris Eq 21:
//
// There is a slight variation from DiDonato and Morris here:
// the first form given here is unstable when p is close to 1,
// making it impossible to compute the inverse of Q(a,x) for small
// q. Fortunately the second form works perfectly well in this case.
//
var u;
if((b * q > 1e-8) && (q > 1e-5))
{
u = Math.pow(p * g * a, 1 / a);
}
else
{
u = Math.exp((-q / a) - 0.5772156649);
}
result = u / (1 - (u / (a + 1)));
}
else if((a < 0.3) && (b >= 0.35))
{
// DiDonato & Morris Eq 22:
var t = Math.exp(-0.5772156649 - b);
var u = t * Math.exp(t);
result = t * Math.exp(u);
}
else if((b > 0.15) || (a >= 0.3))
{
// DiDonato & Morris Eq 23:
var y = -Math.log(b);
var u = y - (1 - a) * Math.log(y);
result = y - (1 - a) * Math.log(u) - Math.log(1 + (1 - a) / (1 + u));
}
else if (b > 0.1)
{
// DiDonato & Morris Eq 24:
var y = -log(b);
var u = y - (1 - a) * log(y);
result = y - (1 - a) * log(u) - log((u * u + 2 * (3 - a) * u + (2 - a) * (3 - a)) / (u * u + (5 - a) * u + 2));
}
else
{
// DiDonato & Morris Eq 25:
var y = -Math.log(b);
var c1 = (a - 1) * Math.log(y);
var c1_2 = c1 * c1;
var c1_3 = c1_2 * c1;
var c1_4 = c1_2 * c1_2;
var a_2 = a * a;
var a_3 = a_2 * a;
var c2 = (a - 1) * (1 + c1);
var c3 = (a - 1) * (-(c1_2 / 2) + (a - 2) * c1 + (3 * a - 5) / 2);
var c4 = (a - 1) * ((c1_3 / 3)
- (3 * a - 5) * c1_2 / 2 + (a_2 - 6 * a + 7) * c1 + (11 * a_2 - 46 * a + 47) / 6);
var c5 = (a - 1) * (-(c1_4 / 4)
+ (11 * a - 17) * c1_3 / 6
+ (-3 * a_2 + 13 * a -13) * c1_2
+ (2 * a_3 - 25 * a_2 + 72 * a - 61) * c1 / 2
+ (25 * a_3 - 195 * a_2 + 477 * a - 379) / 12);
var y_2 = y * y;
var y_3 = y_2 * y;
var y_4 = y_2 * y_2;
result = y + c1 + (c2 / y) + (c3 / y_2) + (c4 / y_3) + (c5 / y_4);
}
}
else
{
// DiDonato and Morris Eq 31:
var s = find_inverse_s(p, q);
var s_2 = s * s;
var s_3 = s_2 * s;
var s_4 = s_2 * s_2;
var s_5 = s_4 * s;
var ra = Math.sqrt(a);
var w = a + s * ra + (s * s -1) / 3;
w += (s_3 - 7 * s) / (36 * ra);
w -= (3 * s_4 + 7 * s_2 - 16) / (810 * a);
w += (9 * s_5 + 256 * s_3 - 433 * s) / (38880 * a * ra);
if((a >= 500) && (Math.abs(1 - w / a) < 1e-6))
{
result = w;
}
else if (p > 0.5)
{
if(w < 3 * a)
{
result = w;
}
else
{
var D = Math.max((2), (a * (a - 1)));
var lg = 0;
var a2 = a*a;
var a3 = a*a2;
var a4 = a*a3;
var err_term = 1 + (1.0/(12*a))+(1.0/(288*a2))-(139/(51840*a3))
- (571/(2488320*a4));
if (a > 171)
lg = 0.5*(Math.log(2*Math,PI) - Math.log(a)) + (a * Math.log(a)) - a + Math.log(err_term);
else
lg = Math.log(land_ios_gamma(a));
var lb = Math.log(q) + lg;
if(lb < -D * 2.3)
{
// DiDonato and Morris Eq 25:
var y = -lb;
var c1 = (a - 1) * Math.log(y);
var c1_2 = c1 * c1;
var c1_3 = c1_2 * c1;
var c1_4 = c1_2 * c1_2;
var a_2 = a * a;
var a_3 = a_2 * a;
var c2 = (a - 1) * (1 + c1);
var c3 = (a - 1) * (-(c1_2 / 2) + (a - 2) * c1 + (3 * a - 5) / 2);
var c4 = (a - 1) * ((c1_3 / 3)
- (3 * a - 5) * c1_2 / 2 + (a_2 - 6 * a + 7) * c1 + (11 * a_2 - 46 * a + 47) / 6);
var c5 = (a - 1) * (-(c1_4 / 4)
+ (11 * a - 17) * c1_3 / 6
+ (-3 * a_2 + 13 * a -13) * c1_2
+ (2 * a_3 - 25 * a_2 + 72 * a - 61) * c1 / 2
+ (25 * a_3 - 195 * a_2 + 477 * a - 379) / 12);
var y_2 = y * y;
var y_3 = y_2 * y;
var y_4 = y_2 * y_2;
result = y + c1 + (c2 / y) + (c3 / y_2) + (c4 / y_3) + (c5 / y_4);
}
else
{
// DiDonato and Morris Eq 33:
var u = -lb + (a - 1) * Math.log(w) - Math.log(1 + (1 - a) / (1 + w));
result = -lb + (a - 1) * Math.log(u) - Math.log(1 + (1 - a) / (1 + u));
}
}
}
else
{
// DiDonato and Morris Eq 35:
var z = didonato_FN(p, a, w, 0, 0);
z = didonato_FN(p, a, z, 2, 0.0);
z = didonato_FN(p, a, z, 2, 0.0);
z = didonato_FN(p, a, z, 3, 0.0);
if((z <= 0.01 * (a + 1)) || (z > 0.7 * (a + 1)))
{
result = z;
}
else
{
// DiDonato and Morris Eq 36:
var zb = didonato_FN(p, a, z, 100, 1e-4);
var lg = 0;
var ap = a + 1;
var ap2 = ap*ap;
var ap3 = ap*ap2;
var ap4 = ap*ap3;
var err_term = 1 + (1.0/(12*ap))+(1.0/(288*ap2))-(139/(51840*ap3))
- (571/(2488320*ap4));
if (ap > 171)
lg = 0.5*(Math.log(2*Math.PI) - Math.log(ap)) + (ap * Math.log(ap)) - a + Math.log(err_term);
else
lg = Math.log(land_ios_gamma(ap));
// double u = log(p) + log(gamma(a + 1));
var u = Math.log(p) + lg;
result = zb * (1 - (a * Math.log(zb) - zb - u + Math.log(didonato_SN(a, z, 100, 1e-4))) / (a - zb));
}
}
}
// Do a couple of Halley iterations
var f, df, d2f, result_h;
df = 0.0; d2f = 0.0;
f = 0.0;
result_h = result;
var ga = 1.0/land_ios_gamma(a);
var lg = 0;
var a2 = a*a;
var a3 = a*a2;
var a4 = a*a3;
var err_term = 1 + (1.0/(12*a))+(1.0/(288*a2))-(139/(51840*a3))
- (571/(2488320*a4));
if (a > 171)
lg = 0.5*(Math.log(2*Math.PI) - Math.log(a)) + (a * Math.log(a)) - a + Math.log(err_term);
else
lg = Math.log(land_ios_gamma(a));
for (m =0; m < 20; m++)
{
f = land_gammainc_eval(result_h,a,0) - p;
df = ga * Math.exp( (a - 1)*Math.log(result_h) - result_h);
d2f = 1 * (((a - 1.0)/result_h) - 1.0) * df;
var delta = ((a - 1.0)/result_h) - 1;
df = Math.exp((a - 1)*Math.log(result_h) - result_h - lg);
result_h = result_h - (2*f)/(2*df - (f*delta));
}
return result_h;
}
// DiDonato based functions
function didonato_FN(p, a, x, N, tolerance)
{
//
// Computation of the Incomplete Gamma Function Ratios and their Inverse
// ARMIDO R. DIDONATO and ALFRED H. MORRIS, JR.
// ACM Transactions on Mathematical Software, Vol. 12, No. 4,
// December 1986, Pages 377-393.
//
// See equation 34.
//
var lg_res = 0;
var ap = a + 1;
var ap2 = ap*ap;
var ap3 = ap*ap2;
var ap4 = ap*ap3;
var err_term = 1 + (1.0/(12*ap))+(1.0/(288*ap2))-(139/(51840*ap3))
- (571/(2488320*ap4));
if (ap > 171)
{
lg_res = 0.5*(Math.log(2*Math.PI) - Math.log(ap)) + (ap * Math.log(ap)) - ap + Math.log(err_term);
}
else
{
lg_res = Math.log(land_ios_gamma(ap));
}
// double u = log(p) + log(gamma(a + 1));
var u = Math.log(p) + lg_res;
return Math.exp((u + x - Math.log(didonato_SN(a, x, N, tolerance))) / a);
}
function didonato_SN(a, x, N, tolerance)
{
//
// Computation of the Incomplete Gamma Function Ratios and their Inverse
// ARMIDO R. DIDONATO and ALFRED H. MORRIS, JR.
// ACM Transactions on Mathematical Software, Vol. 12, No. 4,
// December 1986, Pages 377-393.
//
// See equation 34.
//
var sum = 1;
if(N >= 1)
{
var partial = x / (a + 1);
sum = sum + partial;
for(i = 2; i <= N; ++i)
{
partial = partial * (x / (a + i));
sum = sum + partial;
if(partial < tolerance)
break;
}
}
return sum;
}
function find_inverse_s(p, q)
{
//
// Computation of the Incomplete Gamma Function Ratios and their Inverse
// ARMIDO R. DIDONATO and ALFRED H. MORRIS, JR.
// ACM Transactions on Mathematical Software, Vol. 12, No. 4,
// December 1986, Pages 377-393.
//
// See equation 32.
//
var t;
if(p < 0.5)
{
t = Math.sqrt(-2 * Math.log(p));
}
else
{
t = Math.sqrt(-2 * Math.log(q));
}
var a = new Array();
a[0] = 3.31125922108741;
a[1] = 11.6616720288968;
a[2] = 4.28342155967104;
a[3] = 0.213623493715853;
var b = new Array();
b[0] = 1;
b[1] = 6.61053765625462;
b[2] = 6.40691597760039;
b[3] = 1.27364489782223;
b[4] = 0.3611708101884203e-1;
var t2 = t * t;
var t3 = t2 * t;
var t4 = t2 * t2;
var num_sum = (a[0]*t3) + (a[1]*t2) + (a[2]*t) + a[3];
var den_sum = (b[0]*t4) + (b[1]*t3) + (b[2]*t2) + (b[3]*t) + b[4];
var s = t - (num_sum/den_sum);
if(p < 0.5)
s = -s;
return s;
}
function erf_taylor(x)
{
var res = 0;
var c = 2.0/Math.sqrt(Math.PI);
for (n = 0; n < 100; n++)
res = res + (Math.pow(-1,n)*Math.pow(x,2*n + 1))/(sFact(n) * ((2*n)+1))
res = c * res
return res
}
function f_pdf(x, a, b)
{
var bt;
var g_ab;
var g_a;
var g_b;
if ((a + b) >= 171)
g_ab = land_lgamma_stirling((a/2)+(b/2));
else
g_ab = Math.log(land_ios_gamma((a/2)+(b/2)));
if (a >= 171)
g_a = land_lgamma_stirling(a/2);
else
g_a = Math.log(land_ios_gamma(a/2));
if (b >= 171)
g_b = land_lgamma_stirling(b/2);
else
g_b = Math.log(land_ios_gamma(b/2));
var lbt = g_ab - g_a - g_b - Math.log(x);
var l2 = a*Math.log(a*x) + b*Math.log(b) - (a+b)*Math.log((a*x)+b);
l2 = l2 * 0.5;
bt = Math.exp(l2 + lbt);
return bt;
}
function betainc_num(x, a, b)
{
var bt;
var g_ab;
var g_a;
var g_b;
if ((a + b) >= 171)
g_ab = land_lgamma_stirling(a+b);
else
g_ab = Math.log(land_ios_gamma(a+b));
if (a >= 171)
g_a = land_lgamma_stirling(a);
else
g_a = Math.log(land_ios_gamma(a));
if (b >= 171)
g_b = land_lgamma_stirling(b);
else
g_b = Math.log(land_ios_gamma(b));
bt = Math.exp(g_ab - g_a - g_b + a*Math.log(x)+b*Math.log(1.0-x));
if (x == 0)
bt = 0;
if (x < ((a + 1.0)/(a + b + 2.0)))
return bt*betacf(a,b,x)/a;
else
return 1 - (bt*betacf(b,a,1.0-x)/b);
}
function betacf(a, b, x)
{
var maxit = 100;
var eps = 3e-16;
var fpmin = 1e-30;
var aa;
var c;
var d;
var del;
var h;
var qab;
var qam;
var qap;
qab = a + b;
qap = a + 1;
qam = a - 1;
c = 1.0;
d = 1.0 - qab*x/qap;
if (Math.abs(d)<fpmin)
d = fpmin;
d = 1.0/d;
h = d;
var m2;
for (m = 1; m < maxit; m++)
{
m2 = 2*m;
aa = m*(b-m)*x/((qam + m2)*(a + m2));
d = 1.0 + aa*d;
if (Math.abs(d)<fpmin)
d = fpmin;
c = 1.0 + aa/c;
if (Math.abs(c)<fpmin)
c = fpmin;
d = 1.0/d;
h = h*d*c;
aa = -(a + m)*(qab + m)*x/((a+m2)*(qap+m2));
d = 1.0 + aa*d;
if (Math.abs(d)<fpmin)
d = fpmin;
c = 1.0 + aa/c;
if (Math.abs(c)<fpmin)
c = fpmin;
d = 1.0/d;
del = d*c;
h = h*del;
if (Math.abs(del-1.0)< eps)
{
// std::cout << "Breaking out at iter " << m << std::endl;
break;
}
}
// std::cout << " h is " << h << std::endl;
return h;
}
function land_ios_gamma(x)
{
var g = 7;
var y;
var t;
var res_fr;
var p = new Array()
p[0] = 0.99999999999980993;
p[1] = 676.5203681218851;
p[2] = -1259.1392167224028;
p[3] = 771.32342877765313;
p[4] = -176.61502916214059;
p[5] = 12.507343278686905;
p[6] = -0.13857109526572012;
p[7] = 9.9843695780195716e-6;
p[8] = 1.5056327351493116e-7;
if (Math.abs(x - Math.floor(x)) < 1e-16)
{
if ( x > 1)
return sFact(x - 1);
else if (x == 1)
return 1;
else
return 1/0.0;
}
else
{
x -= 1;
y = p[0];
for (i=1; i < g+2; i++)
{
y = y + p[i]/(x + i);
}
t = x + g + 0.5;
res_fr = Math.sqrt(2*Math.PI) * Math.exp(((x+0.5)*Math.log(t))-t)*y;
return res_fr;
}
}
function land_lgamma_stirling(x)
{
var t = 0.5*Math.log(2*Math.PI) - 0.5*Math.log(x) + x*(Math.log(x))-x;
var x2 = x * x;
var x3 = x2 * x;
var x4 = x3 * x;
var err_term = Math.log(1 + (1.0/(12*x)) + (1.0/(288*x2)) - (139.0/(51840*x3))
- (571.0/(2488320*x4)));
var res = t + err_term;
return res;
}
function sFact(num)
{
var rval=1;
for (var i = 2; i <= num; i++)
rval = rval * i;
return rval;
}
function inverse_beta(p, alpha, beta)
{
var x = 0;
var a = 0;
var b = 1;
var precision = 1e-15;
var iter_num = 0;
while (((b - a) > precision) & (iter_num < 100))
{
x = (a + b) / 2;
if (betainc_num(x,alpha,beta) > p)
b = x;
else
a = x;
iter_num = iter_num + 1;
}
return x;
}
</script>Alijah Ahmedhttp://www.blogger.com/profile/01151589526613233918noreply@blogger.com20tag:blogger.com,1999:blog-8441309013858171051.post-31295769397522373602013-12-07T11:03:00.002-08:002023-12-10T05:26:58.995-08:00EBM Systematic Review Calculator<form name="frmOne">
<p style="text-align:justify"> This blog post implements an online Evidence Based Medicine (EBM) Systematic Review Calculator on a 2 by 2 Contingency table. Parameters including sensitivity, specificity and likelihood ratios are calculated. This calculator should be used for guidance purposes only.</p>
<p style="text-align:justify"> For an authoritative explanation of the calculated parameters please refer to <a href="http://www.cebm.net/?o=1158" target="_blank">http://www.cebm.net/?o=1158</a>, which is the website of the Centre for Evidence Based Medicine at the University of Oxford.</p>
<p style="text-align:justify"> Please fill in the four text areas with integers greater than or equal to zero.</p>
<style>
textarea {
min-height: 80px;
padding: 10px;
font-size: 16px;
line-height: 1.5;
resize: none; /* disable resizing */
}
</style>
<table border="1" class="gradienttable" style="width: 100%">
<tbody>
<tr>
<td></td>
<td>Disorder present: </td>
<td>Disorder absent: </td>
<td>Row totals: </td>
</tr>
<tr>
<td>Diagnostic Test positive: </td>
<td><input name="txtANumber" size="15" style="font-size: 18px" type="TextArea" value="" </td>
<td><input name="txtBNumber" size="15" style="font-size: 18px" type="TextArea" value="" /></td>
<td><p id="r1">Row Sum 1</p></td>
</tr>
<tr>
<td>Diagnostic Test negative: </td>
<td><input name="txtCNumber" size="15" style="font-size: 18px" type="TextArea" value="" /></td>
<td><input name="txtDNumber" size="15" style="font-size: 18px" type="TextArea" value="" /></td>
<td><p id="r2">Row Sum 2</p></td>
</tr>
<td>Column totals</td>
<td><p id="c1">Column Sum 1</p></td>
<td><p id="c2">Column Sum 2</p></td>
<td><p id="t1">Total</p></td>
</tbody>
</table>
<br>
<br>
<input name="b1" onclick="calculate_ebm()" type="Button" value="Calculate parameters" />
<br>
<br>
<p id="p1">Results pending...</p>
</form>
<script language="JavaScript">
function calculate_ebm() {
var a = Number(document.frmOne.txtANumber.value);
var b = Number(document.frmOne.txtBNumber.value);
var c = Number(document.frmOne.txtCNumber.value);
var d = Number(document.frmOne.txtDNumber.value);
var r1 = a + b;
var r2 = c + d;
var c1 = a + c;
var c2 = b + d;
document.getElementById("r1").innerHTML = r1.toString();
document.getElementById("r2").innerHTML = r2.toString();
document.getElementById("c1").innerHTML = c1.toString();
document.getElementById("c2").innerHTML = c2.toString();
var N = a + b + c + d;
document.getElementById("t1").innerHTML = N.toString();
var res_str = '';
var tmp_str = '';
tmp_str = "Let a = " + a + "<br>";
res_str = res_str.concat(tmp_str);
tmp_str = "Let b = " + b + "<br>";
res_str = res_str.concat(tmp_str);
tmp_str = "Let c = " + c + "<br>";
res_str = res_str.concat(tmp_str);
tmp_str = "Let d = " + d + "<br><br>";
res_str = res_str.concat(tmp_str);
var sens = a/(a + c);
tmp_str = "Sensitivity = a/(a + c) = " + sens.toFixed(6) + "<br><br>";
res_str = res_str.concat(tmp_str);
var spec = d/(b + d);
tmp_str = "Specificity = d/(b + d) = " + spec.toFixed(6) + "<br><br>";
res_str = res_str.concat(tmp_str);
var lrplus = sens/(1 - spec);
tmp_str = "LR+ = sensitivity/(1 - specificity) = " + lrplus.toFixed(6) + "<br><br>";
res_str = res_str.concat(tmp_str);
var lrminus = (1 - sens)/spec;
tmp_str = "LR- = (1 - sensitivity)/specificity = " + lrminus.toFixed(6) + "<br><br>";
res_str = res_str.concat(tmp_str);
var ppv = a/(a + b);
tmp_str = "Positive Predictive Value = a/(a + b) = " + ppv.toFixed(6) + "<br><br>";
res_str = res_str.concat(tmp_str);
var npv = d/(c + d);
tmp_str = "Negative Predictive Value = d/(c + d) = " + npv.toFixed(6) + "<br><br>";
res_str = res_str.concat(tmp_str);
var prev = (a + c)/(a + b + c + d);
tmp_str = "Prevalence = (a + c)/(a + b + c + d) = " + prev.toFixed(6) + "<br><br>";
res_str = res_str.concat(tmp_str);
var pto = prev/(1 - prev);
tmp_str = "Pre-test odds = prevalence/(1 - prevalence) = " + pto.toFixed(6) + "<br><br>";
res_str = res_str.concat(tmp_str);
var postto = pto * lrplus;
tmp_str = "Post-test odds = (Pre-test odds) * LR+ = " + postto.toFixed(6) + "<br><br>";
res_str = res_str.concat(tmp_str);
var ptp = postto/(1 + postto);
tmp_str = "Post-test probability = (Post-test odds)/(1 + (post-test odds)) = " + ptp.toFixed(6) + "<br><br>";
res_str = res_str.concat(tmp_str);
res_str = res_str.fontcolor("white");
document.getElementById("p1").innerHTML = res_str;
}
</script>Alijah Ahmedhttp://www.blogger.com/profile/01151589526613233918noreply@blogger.comtag:blogger.com,1999:blog-8441309013858171051.post-36581356851902556282013-12-01T09:15:00.000-08:002023-12-21T06:05:07.257-08:00Cochran's Q Test Calculator with Post-hoc Analysis<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
<script language="Javascript">
/**
* jQuery-csv (jQuery Plugin)
*
* This document is licensed as free software under the terms of the
* MIT License: http://www.opensource.org/licenses/mit-license.php
*
* Acknowledgements:
* The original design and influence to implement this library as a jquery
* plugin is influenced by jquery-json (http://code.google.com/p/jquery-json/).
* If you're looking to use native JSON.Stringify but want additional backwards
* compatibility for browsers that don't support it, I highly recommend you
* check it out.
*
* A special thanks goes out to rwk@acm.org for providing a lot of valuable
* feedback to the project including the core for the new FSM
* (Finite State Machine) parsers. If you're looking for a stable TSV parser
* be sure to take a look at jquery-tsv (http://code.google.com/p/jquery-tsv/).
* For legal purposes I'll include the "NO WARRANTY EXPRESSED OR IMPLIED.
* USE AT YOUR OWN RISK.". Which, in 'layman's terms' means, by using this
* library you are accepting responsibility if it breaks your code.
*
* Legal jargon aside, I will do my best to provide a useful and stable core
* that can effectively be built on.
*
* Copyrighted 2012 by Evan Plaice.
*/
RegExp.escape= function(s) {
return s.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
};
(function (undefined) {
'use strict';
var $;
// to keep backwards compatibility
if (typeof jQuery !== 'undefined' && jQuery) {
$ = jQuery;
} else {
$ = {};
}
/**
* jQuery.csv.defaults
* Encapsulates the method paramater defaults for the CSV plugin module.
*/
$.csv = {
defaults: {
separator:',',
delimiter:'"',
headers:true
},
hooks: {
castToScalar: function(value, state) {
var hasDot = /\./;
if (isNaN(value)) {
return value;
} else {
if (hasDot.test(value)) {
return parseFloat(value);
} else {
var integer = parseInt(value);
if(isNaN(integer)) {
return null;
} else {
return integer;
}
}
}
}
},
parsers: {
parse: function(csv, options) {
// cache settings
var separator = options.separator;
var delimiter = options.delimiter;
// set initial state if it's missing
if(!options.state.rowNum) {
options.state.rowNum = 1;
}
if(!options.state.colNum) {
options.state.colNum = 1;
}
// clear initial state
var data = [];
var entry = [];
var state = 0;
var value = '';
var exit = false;
function endOfEntry() {
// reset the state
state = 0;
value = '';
// if 'start' hasn't been met, don't output
if(options.start && options.state.rowNum < options.start) {
// update global state
entry = [];
options.state.rowNum++;
options.state.colNum = 1;
return;
}
if(options.onParseEntry === undefined) {
// onParseEntry hook not set
data.push(entry);
} else {
var hookVal = options.onParseEntry(entry, options.state); // onParseEntry Hook
// false skips the row, configurable through a hook
if(hookVal !== false) {
data.push(hookVal);
}
}
//console.log('entry:' + entry);
// cleanup
entry = [];
// if 'end' is met, stop parsing
if(options.end && options.state.rowNum >= options.end) {
exit = true;
}
// update global state
options.state.rowNum++;
options.state.colNum = 1;
}
function endOfValue() {
if(options.onParseValue === undefined) {
// onParseValue hook not set
entry.push(value);
} else {
var hook = options.onParseValue(value, options.state); // onParseValue Hook
// false skips the row, configurable through a hook
if(hook !== false) {
entry.push(hook);
}
}
//console.log('value:' + value);
// reset the state
value = '';
state = 0;
// update global state
options.state.colNum++;
}
// escape regex-specific control chars
var escSeparator = RegExp.escape(separator);
var escDelimiter = RegExp.escape(delimiter);
// compile the regEx str using the custom delimiter/separator
var match = /(D|S|\r\n|\n|\r|[^DS\r\n]+)/;
var matchSrc = match.source;
matchSrc = matchSrc.replace(/S/g, escSeparator);
matchSrc = matchSrc.replace(/D/g, escDelimiter);
match = new RegExp(matchSrc, 'gm');
// put on your fancy pants...
// process control chars individually, use look-ahead on non-control chars
csv.replace(match, function (m0) {
if(exit) {
return;
}
switch (state) {
// the start of a value
case 0:
// null last value
if (m0 === separator) {
value += '';
endOfValue();
break;
}
// opening delimiter
if (m0 === delimiter) {
state = 1;
break;
}
// null last value
if (/^(\r\n|\n|\r)$/.test(m0)) {
endOfValue();
endOfEntry();
break;
}
// un-delimited value
value += m0;
state = 3;
break;
// delimited input
case 1:
// second delimiter? check further
if (m0 === delimiter) {
state = 2;
break;
}
// delimited data
value += m0;
state = 1;
break;
// delimiter found in delimited input
case 2:
// escaped delimiter?
if (m0 === delimiter) {
value += m0;
state = 1;
break;
}
// null value
if (m0 === separator) {
endOfValue();
break;
}
// end of entry
if (/^(\r\n|\n|\r)$/.test(m0)) {
endOfValue();
endOfEntry();
break;
}
// broken paser?
throw new Error('CSVDataError: Illegal State [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
// un-delimited input
case 3:
// null last value
if (m0 === separator) {
endOfValue();
break;
}
// end of entry
if (/^(\r\n|\n|\r)$/.test(m0)) {
endOfValue();
endOfEntry();
break;
}
if (m0 === delimiter) {
// non-compliant data
throw new Error('CSVDataError: Illegal Quote [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
}
// broken parser?
throw new Error('CSVDataError: Illegal Data [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
default:
// shenanigans
throw new Error('CSVDataError: Unknown State [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
}
//console.log('val:' + m0 + ' state:' + state);
});
// submit the last entry
// ignore null last line
if(entry.length !== 0) {
endOfValue();
endOfEntry();
}
return data;
},
// a csv-specific line splitter
splitLines: function(csv, options) {
// cache settings
var separator = options.separator;
var delimiter = options.delimiter;
// set initial state if it's missing
if(!options.state.rowNum) {
options.state.rowNum = 1;
}
// clear initial state
var entries = [];
var state = 0;
var entry = '';
var exit = false;
function endOfLine() {
// reset the state
state = 0;
// if 'start' hasn't been met, don't output
if(options.start && options.state.rowNum < options.start) {
// update global state
entry = '';
options.state.rowNum++;
return;
}
if(options.onParseEntry === undefined) {
// onParseEntry hook not set
entries.push(entry);
} else {
var hookVal = options.onParseEntry(entry, options.state); // onParseEntry Hook
// false skips the row, configurable through a hook
if(hookVal !== false) {
entries.push(hookVal);
}
}
// cleanup
entry = '';
// if 'end' is met, stop parsing
if(options.end && options.state.rowNum >= options.end) {
exit = true;
}
// update global state
options.state.rowNum++;
}
// escape regex-specific control chars
var escSeparator = RegExp.escape(separator);
var escDelimiter = RegExp.escape(delimiter);
// compile the regEx str using the custom delimiter/separator
var match = /(D|S|\n|\r|[^DS\r\n]+)/;
var matchSrc = match.source;
matchSrc = matchSrc.replace(/S/g, escSeparator);
matchSrc = matchSrc.replace(/D/g, escDelimiter);
match = new RegExp(matchSrc, 'gm');
// put on your fancy pants...
// process control chars individually, use look-ahead on non-control chars
csv.replace(match, function (m0) {
if(exit) {
return;
}
switch (state) {
// the start of a value/entry
case 0:
// null value
if (m0 === separator) {
entry += m0;
state = 0;
break;
}
// opening delimiter
if (m0 === delimiter) {
entry += m0;
state = 1;
break;
}
// end of line
if (m0 === '\n') {
endOfLine();
break;
}
// phantom carriage return
if (/^\r$/.test(m0)) {
break;
}
// un-delimit value
entry += m0;
state = 3;
break;
// delimited input
case 1:
// second delimiter? check further
if (m0 === delimiter) {
entry += m0;
state = 2;
break;
}
// delimited data
entry += m0;
state = 1;
break;
// delimiter found in delimited input
case 2:
// escaped delimiter?
var prevChar = entry.substr(entry.length - 1);
if (m0 === delimiter && prevChar === delimiter) {
entry += m0;
state = 1;
break;
}
// end of value
if (m0 === separator) {
entry += m0;
state = 0;
break;
}
// end of line
if (m0 === '\n') {
endOfLine();
break;
}
// phantom carriage return
if (m0 === '\r') {
break;
}
// broken paser?
throw new Error('CSVDataError: Illegal state [Row:' + options.state.rowNum + ']');
// un-delimited input
case 3:
// null value
if (m0 === separator) {
entry += m0;
state = 0;
break;
}
// end of line
if (m0 === '\n') {
endOfLine();
break;
}
// phantom carriage return
if (m0 === '\r') {
break;
}
// non-compliant data
if (m0 === delimiter) {
throw new Error('CSVDataError: Illegal quote [Row:' + options.state.rowNum + ']');
}
// broken parser?
throw new Error('CSVDataError: Illegal state [Row:' + options.state.rowNum + ']');
default:
// shenanigans
throw new Error('CSVDataError: Unknown state [Row:' + options.state.rowNum + ']');
}
//console.log('val:' + m0 + ' state:' + state);
});
// submit the last entry
// ignore null last line
if(entry !== '') {
endOfLine();
}
return entries;
},
// a csv entry parser
parseEntry: function(csv, options) {
// cache settings
var separator = options.separator;
var delimiter = options.delimiter;
// set initial state if it's missing
if(!options.state.rowNum) {
options.state.rowNum = 1;
}
if(!options.state.colNum) {
options.state.colNum = 1;
}
// clear initial state
var entry = [];
var state = 0;
var value = '';
function endOfValue() {
if(options.onParseValue === undefined) {
// onParseValue hook not set
entry.push(value);
} else {
var hook = options.onParseValue(value, options.state); // onParseValue Hook
// false skips the value, configurable through a hook
if(hook !== false) {
entry.push(hook);
}
}
// reset the state
value = '';
state = 0;
// update global state
options.state.colNum++;
}
// checked for a cached regEx first
if(!options.match) {
// escape regex-specific control chars
var escSeparator = RegExp.escape(separator);
var escDelimiter = RegExp.escape(delimiter);
// compile the regEx str using the custom delimiter/separator
var match = /(D|S|\n|\r|[^DS\r\n]+)/;
var matchSrc = match.source;
matchSrc = matchSrc.replace(/S/g, escSeparator);
matchSrc = matchSrc.replace(/D/g, escDelimiter);
options.match = new RegExp(matchSrc, 'gm');
}
// put on your fancy pants...
// process control chars individually, use look-ahead on non-control chars
csv.replace(options.match, function (m0) {
switch (state) {
// the start of a value
case 0:
// null last value
if (m0 === separator) {
value += '';
endOfValue();
break;
}
// opening delimiter
if (m0 === delimiter) {
state = 1;
break;
}
// skip un-delimited new-lines
if (m0 === '\n' || m0 === '\r') {
break;
}
// un-delimited value
value += m0;
state = 3;
break;
// delimited input
case 1:
// second delimiter? check further
if (m0 === delimiter) {
state = 2;
break;
}
// delimited data
value += m0;
state = 1;
break;
// delimiter found in delimited input
case 2:
// escaped delimiter?
if (m0 === delimiter) {
value += m0;
state = 1;
break;
}
// null value
if (m0 === separator) {
endOfValue();
break;
}
// skip un-delimited new-lines
if (m0 === '\n' || m0 === '\r') {
break;
}
// broken paser?
throw new Error('CSVDataError: Illegal State [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
// un-delimited input
case 3:
// null last value
if (m0 === separator) {
endOfValue();
break;
}
// skip un-delimited new-lines
if (m0 === '\n' || m0 === '\r') {
break;
}
// non-compliant data
if (m0 === delimiter) {
throw new Error('CSVDataError: Illegal Quote [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
}
// broken parser?
throw new Error('CSVDataError: Illegal Data [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
default:
// shenanigans
throw new Error('CSVDataError: Unknown State [Row:' + options.state.rowNum + '][Col:' + options.state.colNum + ']');
}
//console.log('val:' + m0 + ' state:' + state);
});
// submit the last value
endOfValue();
return entry;
}
},
helpers: {
/**
* $.csv.helpers.collectPropertyNames(objectsArray)
* Collects all unique property names from all passed objects.
*
* @param {Array} objects Objects to collect properties from.
*
* Returns an array of property names (array will be empty,
* if objects have no own properties).
*/
collectPropertyNames: function (objects) {
var o, propName, props = [];
for (o in objects) {
for (propName in objects[o]) {
if ((objects[o].hasOwnProperty(propName)) &&
(props.indexOf(propName) < 0) &&
(typeof objects[o][propName] !== 'function')) {
props.push(propName);
}
}
}
return props;
}
},
/**
* $.csv.toArray(csv)
* Converts a CSV entry string to a javascript array.
*
* @param {Array} csv The string containing the CSV data.
* @param {Object} [options] An object containing user-defined options.
* @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
* @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
*
* This method deals with simple CSV strings only. It's useful if you only
* need to parse a single entry. If you need to parse more than one line,
* use $.csv2Array instead.
*/
toArray: function(csv, options, callback) {
options = (options !== undefined ? options : {});
var config = {};
config.callback = ((callback !== undefined && typeof(callback) === 'function') ? callback : false);
config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator;
config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter;
var state = (options.state !== undefined ? options.state : {});
// setup
options = {
delimiter: config.delimiter,
separator: config.separator,
onParseEntry: options.onParseEntry,
onParseValue: options.onParseValue,
state: state
};
var entry = $.csv.parsers.parseEntry(csv, options);
// push the value to a callback if one is defined
if(!config.callback) {
return entry;
} else {
config.callback('', entry);
}
},
/**
* $.csv.toArrays(csv)
* Converts a CSV string to a javascript array.
*
* @param {String} csv The string containing the raw CSV data.
* @param {Object} [options] An object containing user-defined options.
* @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
* @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
*
* This method deals with multi-line CSV. The breakdown is simple. The first
* dimension of the array represents the line (or entry/row) while the second
* dimension contains the values (or values/columns).
*/
toArrays: function(csv, options, callback) {
options = (options !== undefined ? options : {});
var config = {};
config.callback = ((callback !== undefined && typeof(callback) === 'function') ? callback : false);
config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator;
config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter;
// setup
var data = [];
options = {
delimiter: config.delimiter,
separator: config.separator,
onPreParse: options.onPreParse,
onParseEntry: options.onParseEntry,
onParseValue: options.onParseValue,
onPostParse: options.onPostParse,
start: options.start,
end: options.end,
state: {
rowNum: 1,
colNum: 1
}
};
// onPreParse hook
if(options.onPreParse !== undefined) {
options.onPreParse(csv, options.state);
}
// parse the data
data = $.csv.parsers.parse(csv, options);
// onPostParse hook
if(options.onPostParse !== undefined) {
options.onPostParse(data, options.state);
}
// push the value to a callback if one is defined
if(!config.callback) {
return data;
} else {
config.callback('', data);
}
},
/**
* $.csv.toObjects(csv)
* Converts a CSV string to a javascript object.
* @param {String} csv The string containing the raw CSV data.
* @param {Object} [options] An object containing user-defined options.
* @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
* @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
* @param {Boolean} [headers] Indicates whether the data contains a header line. Defaults to true.
*
* This method deals with multi-line CSV strings. Where the headers line is
* used as the key for each value per entry.
*/
toObjects: function(csv, options, callback) {
options = (options !== undefined ? options : {});
var config = {};
config.callback = ((callback !== undefined && typeof(callback) === 'function') ? callback : false);
config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator;
config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter;
config.headers = 'headers' in options ? options.headers : $.csv.defaults.headers;
options.start = 'start' in options ? options.start : 1;
// account for headers
if(config.headers) {
options.start++;
}
if(options.end && config.headers) {
options.end++;
}
// setup
var lines = [];
var data = [];
options = {
delimiter: config.delimiter,
separator: config.separator,
onPreParse: options.onPreParse,
onParseEntry: options.onParseEntry,
onParseValue: options.onParseValue,
onPostParse: options.onPostParse,
start: options.start,
end: options.end,
state: {
rowNum: 1,
colNum: 1
},
match: false,
transform: options.transform
};
// fetch the headers
var headerOptions = {
delimiter: config.delimiter,
separator: config.separator,
start: 1,
end: 1,
state: {
rowNum:1,
colNum:1
}
};
// onPreParse hook
if(options.onPreParse !== undefined) {
options.onPreParse(csv, options.state);
}
// parse the csv
var headerLine = $.csv.parsers.splitLines(csv, headerOptions);
var headers = $.csv.toArray(headerLine[0], options);
// fetch the data
lines = $.csv.parsers.splitLines(csv, options);
// reset the state for re-use
options.state.colNum = 1;
if(headers){
options.state.rowNum = 2;
} else {
options.state.rowNum = 1;
}
// convert data to objects
for(var i=0, len=lines.length; i<len; i++) {
var entry = $.csv.toArray(lines[i], options);
var object = {};
for(var j=0; j <headers.length; j++) {
object[headers[j]] = entry[j];
}
if (options.transform !== undefined) {
data.push(options.transform.call(undefined, object));
} else {
data.push(object);
}
// update row state
options.state.rowNum++;
}
// onPostParse hook
if(options.onPostParse !== undefined) {
options.onPostParse(data, options.state);
}
// push the value to a callback if one is defined
if(!config.callback) {
return data;
} else {
config.callback('', data);
}
},
/**
* $.csv.fromArrays(arrays)
* Converts a javascript array to a CSV String.
*
* @param {Array} arrays An array containing an array of CSV entries.
* @param {Object} [options] An object containing user-defined options.
* @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
* @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
*
* This method generates a CSV file from an array of arrays (representing entries).
*/
fromArrays: function(arrays, options, callback) {
options = (options !== undefined ? options : {});
var config = {};
config.callback = ((callback !== undefined && typeof(callback) === 'function') ? callback : false);
config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator;
config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter;
var output = '',
line,
lineValues,
i, j;
for (i = 0; i < arrays.length; i++) {
line = arrays[i];
lineValues = [];
for (j = 0; j < line.length; j++) {
var strValue = (line[j] === undefined || line[j] === null) ? '' : line[j].toString();
if (strValue.indexOf(config.delimiter) > -1) {
strValue = strValue.replace(config.delimiter, config.delimiter + config.delimiter);
}
var escMatcher = '\n|\r|S|D';
escMatcher = escMatcher.replace('S', config.separator);
escMatcher = escMatcher.replace('D', config.delimiter);
if (strValue.search(escMatcher) > -1) {
strValue = config.delimiter + strValue + config.delimiter;
}
lineValues.push(strValue);
}
output += lineValues.join(config.separator) + '\r\n';
}
// push the value to a callback if one is defined
if(!config.callback) {
return output;
} else {
config.callback('', output);
}
},
/**
* $.csv.fromObjects(objects)
* Converts a javascript dictionary to a CSV string.
*
* @param {Object} objects An array of objects containing the data.
* @param {Object} [options] An object containing user-defined options.
* @param {Character} [separator] An override for the separator character. Defaults to a comma(,).
* @param {Character} [delimiter] An override for the delimiter character. Defaults to a double-quote(").
* @param {Character} [sortOrder] Sort order of columns (named after
* object properties). Use 'alpha' for alphabetic. Default is 'declare',
* which means, that properties will _probably_ appear in order they were
* declared for the object. But without any guarantee.
* @param {Character or Array} [manualOrder] Manually order columns. May be
* a strin in a same csv format as an output or an array of header names
* (array items won't be parsed). All the properties, not present in
* `manualOrder` will be appended to the end in accordance with `sortOrder`
* option. So the `manualOrder` always takes preference, if present.
*
* This method generates a CSV file from an array of objects (name:value pairs).
* It starts by detecting the headers and adding them as the first line of
* the CSV file, followed by a structured dump of the data.
*/
fromObjects: function(objects, options, callback) {
options = (options !== undefined ? options : {});
var config = {};
config.callback = ((callback !== undefined && typeof(callback) === 'function') ? callback : false);
config.separator = 'separator' in options ? options.separator : $.csv.defaults.separator;
config.delimiter = 'delimiter' in options ? options.delimiter : $.csv.defaults.delimiter;
config.headers = 'headers' in options ? options.headers : $.csv.defaults.headers;
config.sortOrder = 'sortOrder' in options ? options.sortOrder : 'declare';
config.manualOrder = 'manualOrder' in options ? options.manualOrder : [];
config.transform = options.transform;
if (typeof config.manualOrder === 'string') {
config.manualOrder = $.csv.toArray(config.manualOrder, config);
}
if (config.transform !== undefined) {
var origObjects = objects;
objects = [];
var i;
for (i = 0; i < origObjects.length; i++) {
objects.push(config.transform.call(undefined, origObjects[i]));
}
}
var props = $.csv.helpers.collectPropertyNames(objects);
if (config.sortOrder === 'alpha') {
props.sort();
} // else {} - nothing to do for 'declare' order
if (config.manualOrder.length > 0) {
var propsManual = [].concat(config.manualOrder);
var p;
for (p = 0; p < props.length; p++) {
if (propsManual.indexOf( props[p] ) < 0) {
propsManual.push( props[p] );
}
}
props = propsManual;
}
var o, p, line, output = [], propName;
if (config.headers) {
output.push(props);
}
for (o = 0; o < objects.length; o++) {
line = [];
for (p = 0; p < props.length; p++) {
propName = props[p];
if (propName in objects[o] && typeof objects[o][propName] !== 'function') {
line.push(objects[o][propName]);
} else {
line.push('');
}
}
output.push(line);
}
// push the value to a callback if one is defined
return $.csv.fromArrays(output, options, config.callback);
}
};
// Maintenance code to maintain backward-compatibility
// Will be removed in release 1.0
$.csvEntry2Array = $.csv.toArray;
$.csv2Array = $.csv.toArrays;
$.csv2Dictionary = $.csv.toObjects;
// CommonJS module is defined
if (typeof module !== 'undefined' && module.exports) {
module.exports = $.csv;
}
}).call( this );
</script>
<script>
$(document).ready(function() {
if(isAPIAvailable()) {
$('#files').bind('change', handleFileSelect);
}
});
function isAPIAvailable() {
// Check for the various File API support.
if (window.File && window.FileReader && window.FileList && window.Blob) {
// Great success! All the File APIs are supported.
return true;
} else {
// source: File API availability - http://caniuse.com/#feat=fileapi
// source: <output> availability - http://html5doctor.com/the-output-element/
document.writeln('The HTML5 APIs used in this form are only available in the following browsers:<br />');
// 6.0 File API & 13.0 <output>
document.writeln(' - Google Chrome: 13.0 or later<br />');
// 3.6 File API & 6.0 <output>
document.writeln(' - Mozilla Firefox: 6.0 or later<br />');
// 10.0 File API & 10.0 <output>
document.writeln(' - Internet Explorer: Not supported (partial support expected in 10.0)<br />');
// ? File API & 5.1 <output>
document.writeln(' - Safari: Not supported<br />');
// ? File API & 9.2 <output>
document.writeln(' - Opera: Not supported');
return false;
}
}
function handleFileSelect(evt) {
var files = evt.target.files; // FileList object
var file = files[0];
// read the file metadata
var output = ''
output += '<span style="font-weight:bold;">' + escape(file.name) + '</span><br />\n';
output += ' - FileType: ' + (file.type || 'n/a') + '<br />\n';
output += ' - FileSize: ' + file.size + ' bytes<br />\n';
output += ' - LastModified: ' + (file.lastModifiedDate ? file.lastModifiedDate.toLocaleDateString() : 'n/a') + '<br />\n';
// read the file contents
printTextArea(file);
// post the results
$('#list').append(output);
}
function printTextArea(file) {
var container = document.getElementById("area");
var inps = container.getElementsByTagName("input");
var kgr = inps.length;
var idx_selected = 0;
for (k=0; k < kgr; k++)
{
if (inps.item(k).value == "")
{
idx_selected = k;
break;
}
}
var reader = new FileReader();
reader.readAsText(file);
reader.onload = function(event){
var csv = event.target.result;
var data = $.csv.toArrays(csv);
var multi_col = 0;
var mystr = new Array();
for (k=0;k<kgr;k++)
mystr[k] = '';
for(var row in data) {
if (!isNaN(Number(data[row][0])))
{
if (data[row].length > 1)
{
multi_col = 1;
for (k=0; k < kgr; k++)
{
mystr[k] = (mystr[k]).concat((Number(data[row][k]).toFixed(3)).toString());
mystr[k] = (mystr[k]).concat(",");
}
}
else
{
mystr[0] = (mystr[0]).concat((Number(data[row][0]).toFixed(3)).toString());
mystr[0] = (mystr[0]).concat(",");
}
}
}
if (multi_col == 0)
{
mystr[0] = (mystr[0]).substring(0, mystr[0].length - 1)
inps.item(idx_selected).value = mystr[0];
}
else
{
for (k=0; k < kgr; k++)
{
mystr[k] = (mystr[k]).substring(0, mystr[k].length - 1)
inps.item(k).value = mystr[k];
}
}
};
reader.onerror = function(){ alert('Unable to read ' + file.fileName); };
}
</script>
<!--[if IE ]>
<script type="text/javascript" src="https://firebasestorage.googleapis.com/v0/b/js-static-assets.appspot.com/o/excanvas_min.js?alt=media&token=9dd78ad2-f09b-45dd-8a09-8db2b2546d1d"></script>
<![endif]-->
<script type="text/javascript" src="https://firebasestorage.googleapis.com/v0/b/js-static-assets.appspot.com/o/jquery_flot.js?alt=media&token=bde69a50-3df9-4794-b7d8-dd4e840472cd"></script>
</head>
<body>
<!-- Just call function 'addTextBox()' to add textbox -->
<a onclick='addTextBox()' href='#'>Please click to add a treatment.</a>
<br/>
<br/>
<!-- Textbox will be added in followng DIV -->
<div id='area'></div>
<div id=inputs class=clearfix>
<input type=file id=files name=files[] multiple />
</div>
<p style="text-align:justify"> Cochran's Q test is a non-parametric statistical test used to test whether a number of dependent treatments have identical effects, and is used for dichotomous data (expressible as numbers with a value of either 0 or 1). This test can be regarded as an extension of the McNemar test.</p>
<p style="text-align:justify">Should the calculated p-value be less that 0.05, a cursory post-hoc analysis is carried out by performing pairwise McNemar tests on the treatments. Any resulting p-value less than 0.05 is highlighted by two asterisks.</p>
<p style="text-align:justify"> Simply click on the link near the top to add text boxes. Each text box stores a single treatment and needs to be filled in with comma separated numbers, which take value 0 or 1. All treatments need to have the same number of samples, which is equal to the number of blocks.</p>
<p style="text-align:justify">Alternatively, you can choose two file entry methods:-
<ol>
<li>Select multiple single column CSV files to populate the text boxes by repeatedly pressing the Choose File button - there must be one distinct (and differently named) file for each text box i.e. one file per group. Each file can have a different number of samples.</li>
<li>Select a single multi-column CSV file by pressing the Choose File button once, where the number of columns equals the number of groups - all groups need to have the same number of samples.</li>
</ol>
</p>
<input name="b1" onclick="calculate_cochrane()" style="margin-left: 0px;" type="Button" value="Calculate.." />
<p id="p1">Results pending...</p>
<input name="b1" onclick="clearReset()" style="margin-left: 0px;" type="Button" value="Reset Results log" />
<br>
</body>
</html>
<script language="javascript">
var inival=0; // Initialise starting element number
function calculate_cochrane() {
var cochrane_msg = '';
var tmp_str = '';
var str = inival.toString();
var container = document.getElementById("area");
var inps = container.getElementsByTagName("input");
var kgr = inps.length;
tmp_str = "Number of treatment groups is " + kgr + "<br>";
cochrane_msg = cochrane_msg.concat(tmp_str);
var dataSet;
var datavec = new Array();
var b = 0;
var first_len;
var all_same_len = 1;
for (k=0; k < kgr; k++)
{
if (inps.item(k).value.split(',').length > b)
b = inps.item(k).value.split(',').length;
if (k == 0)
first_len = inps.item(k).value.split(',').length;
if (inps.item(k).value.split(',').length != first_len)
all_same_len = 0;
}
if (all_same_len == 0)
{
alert("All treatments must have same number of samples");
return;
}
tmp_str = "Number of blocks is " + first_len + "<br>";
cochrane_msg = cochrane_msg.concat(tmp_str);
var datamat = [];
for(var i=0; i< kgr ; i++)
{
datamat[i] = [];
for(var j=0; j< b; j++) {
datamat[i][j] = 0;
}
}
for (k=0; k < kgr; k++)
{
dataSet = inps.item(k).value;
datavec = dataSet.split(',');
if (datavec.length > b)
b = datavec.length;
for (m=0; m < datavec.length; m++)
{
datamat[k][m] = Number(datavec[m]);
}
}
var N = 0;
for (k = 0; k < kgr; k++)
{
for (m = 0; m < b; m++)
N = N + datamat[k][m];
}
var xpj = new Array();
var xip = new Array();
for (k = 0; k < kgr; k++)
{
xpj[k] = 0;
for (m = 0; m < b; m++)
xpj[k] = xpj[k] + datamat[k][m];
}
for (k = 0; k < b; k++)
{
xip[k] = 0;
for (m = 0; m < kgr; m++)
xip[k] = xip[k] + datamat[m][k];
}
var vsub = N/kgr;
var numer = 0;
for (k = 0; k < xpj.length; k++)
numer = numer + ((xpj[k] - vsub) * (xpj[k] - vsub));
var denom = 0;
for (k = 0; k < xip.length; k++)
denom = denom + (xip[k] * (kgr - xip[k]));
// The statistic
var T = (kgr - 1) * kgr * (numer/denom);
var pval = 1 - chisquared_cdf(T,kgr - 1);
var a1 = new Array();
var a2 = new Array();
for (r = 0; r < b; r++)
{
a1[r] = 0;
a2[r] = 0;
}
// Debug array printing..
/*
for (k=0; k < kgr; k++)
{
tmp_str = '';
for (m = 0; m < b; m++)
{
var in_str = datamat[k][m] + ","
tmp_str = tmp_str.concat(in_str);
}
tmp_str = tmp_str.concat("<br>");
cochrane_msg = cochrane_msg.concat(tmp_str);
}
*/
tmp_str = "Cochrane's Q test statistic T is " + T.toFixed(6) + "<br>";
cochrane_msg = cochrane_msg.concat(tmp_str);
var kdf = kgr - 1;
tmp_str = "Corresponding p-value for Chi-squared distn of df " + kdf + " is " + pval.toFixed(6) + "<br>";
cochrane_msg = cochrane_msg.concat(tmp_str);
if (pval < 0.05)
{
tmp_str = "<br><br>Post-hoc analysis: Carrying out pairwise McNemar tests as p-value is less than 0.05<br>"
cochrane_msg = cochrane_msg.concat(tmp_str);
for (var k = 0; k < kgr - 1; k++)
{
for (var m = k+1; m < kgr; m++)
{
for (z = 0; z < b; z++)
{
a1[z] = datamat[k][z];
a2[z] = datamat[m][z];
}
var mc_res = mcnemar_test(a1, a2);
var kp = k + 1;
var mp = m + 1;
if (mc_res < 0.05)
{
tmp_str = "Treatments " + kp + " and " + mp + " yields "
+ " p-value of " + mc_res.toFixed(6) + " **<br>";
}
else
{
tmp_str = "Treatments " + kp + " and " + mp + " yields "
+ " p-value of " + mc_res.toFixed(6) + "<br>";
}
cochrane_msg = cochrane_msg.concat(tmp_str);
}
}
}
cochrane_msg = cochrane_msg.fontcolor("white");
document.getElementById("p1").innerHTML=cochrane_msg;
}
function mcnemar_test(inp1,inp2)
{
var b = 0;
var c = 0;
var mcs_yates2 = ((Math.abs(b - c) - 1) * (Math.abs(b - c) - 1))/(b + c);
for (k = 0; k < inp1.length; k++)
{
if ((inp1[k] == 1) && (inp2[k] == 0))
b = b + 1;
if ((inp1[k] == 0) && (inp2[k] == 1))
c = c + 1;
}
var mcs_yates2;
if ((b + c) != 0)
mcs_yates2 = ((b - c) * (b - c))/(b + c);
else
mcs_yates2 = 0;
var p_res = 1 - chisquared_cdf(mcs_yates2,1);
var bin_res = 2 * bin_cdf(b,b+c,0.5);
if (bin_res > 1)
bin_res = 2 - bin_res;
if ((b + c) == 0)
bin_res = 1;
return p_res;
}
// Call this function to add textbox
function addTextBox()
{
var newArea = add_New_Element();
var str = inival.toString();
var htcontents = "Treatment ";
htcontents = htcontents.concat(inival.toString());
htcontents = htcontents.concat(": <input type='textArea' style='width:300px;' name='textbx[]' />");
htcontents = htcontents.concat("<br><br>");
document.getElementById(newArea).innerHTML = htcontents; // You can any other elements in place of 'htcontents'
}
function add_New_Element() {
inival=inival+1; // Increment element number by 1
var ni = document.getElementById('area');
var newdiv = document.createElement('div'); // Create dynamic element
var divIdName = 'my'+inival+'Div';
newdiv.setAttribute('id',divIdName);
ni.appendChild(newdiv);
return divIdName;
}
function clearReset() {
document.getElementById("p1").innerHTML="Results pending..";
}
function compare(a,b) {
if (a[0] < b[0])
return -1;
if (a[0] > b[0])
return 1;
return
}
function rankify(x,idx,Xlen)
{
var lmatch = new Array()
var match_size = 0
var lrank = new Array()
for (k=0; k < Xlen; k++)
lrank[k] = k + 1;
for (k=0; k < Xlen; k++)
{
if (k == 0)
match_size = 0;
else
{
var mytmp1 = x[k]
var mytmp2 = x[k-1]
if (Math.abs(mytmp1[idx] - mytmp2[idx]) < 1e-15)
{
match_size++;
}
else
{
match_size = 0;
}
}
lmatch[k] = match_size;
}
for (k = 0; k < Xlen; k++)
{
if (k < Xlen - 1)
{
if ((lmatch[k] != 0) &
(lmatch[k+1] == 0))
{
sum = 0.0;
for (m = k; m >= k - lmatch[k]; m--)
{
sum = sum + lrank[m];
}
sum = sum/(lmatch[k] + 1.0);
for (m = k; m >= k - lmatch[k]; m--)
{
lrank[m] = sum;
}
}
}
else
{
if (lmatch[k] != 0)
{
sum = 0.0;
for (m = k; m >= k - lmatch[k]; m--)
{
sum = sum + lrank[m];
}
sum = sum/(lmatch[k] + 1.0);
for (m = k; m >= k - lmatch[k]; m--)
{
lrank[m] = sum
}
}
}
}
return lrank;
}
function chisquared_cdf(inp,p1)
{
var res;
res = land_gammainc_eval(inp/2.0, p1/2.0, 0);
return res;
}
function chisquared_icdf(prob,p1)
{
var res;
res = find_inverse_gamma(p1/2.0, prob, 1.0 - prob);
res = res*2;
if (prob == 0)
res = 0;
return res;
}
function land_gammainc_eval(x, a, upper)
{
var tmp;
var xeg = ((1 + 0.0)/land_ios_gamma(a+1))*Math.pow(x+0.0,a)*Math.exp(-x);
var acc = 0.0;
var Pax;
var Qax;
var lambda;
var eta;
var gamma_star;
var var1;
var acc_f;
var SaEta,RaEta;
var f = new Array();
var b = new Array();
var ak_vec = new Array();
var s_vec = new Array();
var t_vec = new Array();
var rho_vec = new Array();
var log_diff;
var NUMSERIES = 100;
var VNUM = 100;
var QSUM = 100;
if (Math.abs(x) < 1e-16)
{
if (upper == 0)
return 0;
else
return 1;
}
if (x <= a)
{
if ((Math.abs(x) < 10) & (Math.abs(a) < 10) )
{
for (k=0; k < NUMSERIES; k++)
{
if (a+k+1 < 170)
acc += Math.pow(x,k+0.0)*((land_ios_gamma(a+1)+0.0)/land_ios_gamma(k+a+1));
else
{
log_diff = land_lgamma_stirling(a+1) - land_lgamma_stirling(k+a+1) + (k * log(x));
acc += Math.exp(log_diff);
}
}
Pax = xeg * acc;
if (upper == 0)
return Pax;
else
return (1-Pax);
}
else
{
lambda = x/a;
eta = Math.sqrt(2*(lambda - 1 - Math.log(lambda)));
if ((lambda - 1) < 0)
eta = -1 * eta;
// For large a, gamma_star approaches unity..
if (a > 140)
gamma_star = 1.0;
else
gamma_star = land_ios_gamma(a)/(Math.sqrt(2*Math.PI) * Math.exp(-a) * Math.pow(a,a-0.5));
for (k=0;k<VNUM;k++)
f[k] = 0.0;
f[0] = 1;
f[1] = -1.0/3.0;
f[2] = 1.0/12.0;
f[3] = -2.0/135.0;
for (m=4; m < VNUM; m++)
{
var1 = ((m - 1.0)/(3.0*m))* f[m-1];
acc_f = 0.0;
for (j=3; j < m; j++)
{
acc_f = acc_f + (f[j-1] * f[m+1-j])/(m+2-j);
}
f[m] = -((m+1.0)/(m+2.0))*(var1 + acc_f);
}
for (k=0; k < VNUM-1; k++)
b[k] = 0.0;
b[VNUM-2] = f[VNUM-1];
b[VNUM-3] = f[VNUM-2];
for (m = VNUM-3; m > 0; m--)
b[m-1] = f[m] + (((m + 1.0)/a)*b[m+1]);
SaEta = 0.0;
for (m=0; m < b.length; m++)
SaEta = SaEta + (b[m] * Math.pow(eta,m+0.0));
SaEta = SaEta/gamma_star;
RaEta = (SaEta/Math.sqrt(2*Math.PI*a))*Math.exp(-0.5*a*eta*eta);
Qax = (0.5 * (1 - erf_taylor(eta*Math.sqrt(a/2.0)))) + RaEta;
Pax = (0.5 * (1 - erf_taylor(-eta*Math.sqrt(a/2.0)))) - RaEta;
if (upper == 1)
return Qax;
else
return Pax;
// EndOf Section that details modification (and improvement)
// to Gautschi's algorithm
}
}
else
{
if (x <= 1)
{
xeg = Math.pow(x,a)/land_ios_gamma(a);
acc_f = 0.0;
for (k=0; k < QSUM; k++)
acc_f += (Math.pow(-1.0,k)*Math.pow(x,k))/((a+k)*sFact(k));
tmp = xeg * acc_f;
if (upper == 1)
return 1 - tmp;
else
return tmp;
}
else
{
var g1 = (x -a + 1);
for (k=0; k < QSUM; k++)
ak_vec[k] = (k*(a - k))/((x-a+(2*k)-1)*(x-a+(2*k)+1));
for (k=0; k < QSUM; k++)
{
s_vec[k] = 0;
t_vec[k] = 0;
rho_vec[k] = 0;
}
for (k=0; k < QSUM; k++)
{
if (k == 0)
{
s_vec[k] = 0;
rho_vec[k] = 0;
t_vec[k] = 1;
}
else
{
var ak = ak_vec[k];
var rhokm1 = rho_vec[k-1];
rho_vec[k] =(-(ak)*(1 + rhokm1))/(1 + (ak*(1 + rhokm1)));
var rhok = rho_vec[k];
var tkm1 = t_vec[k-1];
t_vec[k] = rhok*tkm1;
var skm1 = s_vec[k-1];
s_vec[k] = skm1 + tkm1;
// [rho_vec[k] = (-(ak_vec[k])*(1 + rho_vec[k-1]))
// /(1 + (ak_vec[k]*(1 + rho_vec[k-1])));
// t_vec[k] = rho_vec[k]*t_vec[k-1];
// s_vec[k] = s_vec[k-1] + t_vec[k-1];
}
}
var slast = s_vec[QSUM-1];
var tmp_scale = (Math.pow(x,a)*Math.exp(-x)/land_ios_gamma(a));
var lga1 = a * Math.log(x);
var lga2 = (0.5 - a) * Math.log(a);
var lga3 = 0.5 * Math.log(2*Math.PI);
var lgtot = lga1 + (a - x) + lga2 - lga3;
if (a > 140)
tmp_scale = Math.exp(lgtot);
var newres;
if (land_ios_gamma(a) == 0.0)
newres = 0;
else
newres = (tmp_scale*slast)/g1;
if (upper == 1)
return newres;
else
return (1 - newres);
// EndOf Gautschi's routine
}
}
}
function find_inverse_gamma(a, p, q)
{
//
// In order to understand what's going on here, you will
// need to refer to:
//
// Computation of the Incomplete Gamma Function Ratios and their Inverse
// ARMIDO R. DIDONATO and ALFRED H. MORRIS, JR.
// ACM Transactions on Mathematical Software, Vol. 12, No. 4,
// December 1986, Pages 377-393.
//
var result;
if(a == 1)
{
result = -Math.log(q);
}
else if(a < 1)
{
var g = land_ios_gamma(a);
var b = q * g;
if((b > 0.6) || ((b >= 0.45) && (a >= 0.3)))
{
// DiDonato & Morris Eq 21:
//
// There is a slight variation from DiDonato and Morris here:
// the first form given here is unstable when p is close to 1,
// making it impossible to compute the inverse of Q(a,x) for small
// q. Fortunately the second form works perfectly well in this case.
//
var u;
if((b * q > 1e-8) && (q > 1e-5))
{
u = Math.pow(p * g * a, 1 / a);
}
else
{
u = Math.exp((-q / a) - 0.5772156649);
}
result = u / (1 - (u / (a + 1)));
}
else if((a < 0.3) && (b >= 0.35))
{
// DiDonato & Morris Eq 22:
var t = Math.exp(-0.5772156649 - b);
var u = t * Math.exp(t);
result = t * Math.exp(u);
}
else if((b > 0.15) || (a >= 0.3))
{
// DiDonato & Morris Eq 23:
var y = -Math.log(b);
var u = y - (1 - a) * Math.log(y);
result = y - (1 - a) * Math.log(u) - Math.log(1 + (1 - a) / (1 + u));
}
else if (b > 0.1)
{
// DiDonato & Morris Eq 24:
var y = -log(b);
var u = y - (1 - a) * log(y);
result = y - (1 - a) * log(u) - log((u * u + 2 * (3 - a) * u + (2 - a) * (3 - a)) / (u * u + (5 - a) * u + 2));
}
else
{
// DiDonato & Morris Eq 25:
var y = -Math.log(b);
var c1 = (a - 1) * Math.log(y);
var c1_2 = c1 * c1;
var c1_3 = c1_2 * c1;
var c1_4 = c1_2 * c1_2;
var a_2 = a * a;
var a_3 = a_2 * a;
var c2 = (a - 1) * (1 + c1);
var c3 = (a - 1) * (-(c1_2 / 2) + (a - 2) * c1 + (3 * a - 5) / 2);
var c4 = (a - 1) * ((c1_3 / 3)
- (3 * a - 5) * c1_2 / 2 + (a_2 - 6 * a + 7) * c1 + (11 * a_2 - 46 * a + 47) / 6);
var c5 = (a - 1) * (-(c1_4 / 4)
+ (11 * a - 17) * c1_3 / 6
+ (-3 * a_2 + 13 * a -13) * c1_2
+ (2 * a_3 - 25 * a_2 + 72 * a - 61) * c1 / 2
+ (25 * a_3 - 195 * a_2 + 477 * a - 379) / 12);
var y_2 = y * y;
var y_3 = y_2 * y;
var y_4 = y_2 * y_2;
result = y + c1 + (c2 / y) + (c3 / y_2) + (c4 / y_3) + (c5 / y_4);
}
}
else
{
// DiDonato and Morris Eq 31:
var s = find_inverse_s(p, q);
var s_2 = s * s;
var s_3 = s_2 * s;
var s_4 = s_2 * s_2;
var s_5 = s_4 * s;
var ra = Math.sqrt(a);
var w = a + s * ra + (s * s -1) / 3;
w += (s_3 - 7 * s) / (36 * ra);
w -= (3 * s_4 + 7 * s_2 - 16) / (810 * a);
w += (9 * s_5 + 256 * s_3 - 433 * s) / (38880 * a * ra);
if((a >= 500) && (Math.abs(1 - w / a) < 1e-6))
{
result = w;
}
else if (p > 0.5)
{
if(w < 3 * a)
{
result = w;
}
else
{
var D = Math.max((2), (a * (a - 1)));
var lg = 0;
var a2 = a*a;
var a3 = a*a2;
var a4 = a*a3;
var err_term = 1 + (1.0/(12*a))+(1.0/(288*a2))-(139/(51840*a3))
- (571/(2488320*a4));
if (a > 171)
lg = 0.5*(Math.log(2*Math,PI) - Math.log(a)) + (a * Math.log(a)) - a + Math.log(err_term);
else
lg = Math.log(land_ios_gamma(a));
var lb = Math.log(q) + lg;
if(lb < -D * 2.3)
{
// DiDonato and Morris Eq 25:
var y = -lb;
var c1 = (a - 1) * Math.log(y);
var c1_2 = c1 * c1;
var c1_3 = c1_2 * c1;
var c1_4 = c1_2 * c1_2;
var a_2 = a * a;
var a_3 = a_2 * a;
var c2 = (a - 1) * (1 + c1);
var c3 = (a - 1) * (-(c1_2 / 2) + (a - 2) * c1 + (3 * a - 5) / 2);
var c4 = (a - 1) * ((c1_3 / 3)
- (3 * a - 5) * c1_2 / 2 + (a_2 - 6 * a + 7) * c1 + (11 * a_2 - 46 * a + 47) / 6);
var c5 = (a - 1) * (-(c1_4 / 4)
+ (11 * a - 17) * c1_3 / 6
+ (-3 * a_2 + 13 * a -13) * c1_2
+ (2 * a_3 - 25 * a_2 + 72 * a - 61) * c1 / 2
+ (25 * a_3 - 195 * a_2 + 477 * a - 379) / 12);
var y_2 = y * y;
var y_3 = y_2 * y;
var y_4 = y_2 * y_2;
result = y + c1 + (c2 / y) + (c3 / y_2) + (c4 / y_3) + (c5 / y_4);
}
else
{
// DiDonato and Morris Eq 33:
var u = -lb + (a - 1) * Math.log(w) - Math.log(1 + (1 - a) / (1 + w));
result = -lb + (a - 1) * Math.log(u) - Math.log(1 + (1 - a) / (1 + u));
}
}
}
else
{
// DiDonato and Morris Eq 35:
var z = didonato_FN(p, a, w, 0, 0);
z = didonato_FN(p, a, z, 2, 0.0);
z = didonato_FN(p, a, z, 2, 0.0);
z = didonato_FN(p, a, z, 3, 0.0);
if((z <= 0.01 * (a + 1)) || (z > 0.7 * (a + 1)))
{
result = z;
}
else
{
// DiDonato and Morris Eq 36:
var zb = didonato_FN(p, a, z, 100, 1e-4);
var lg = 0;
var ap = a + 1;
var ap2 = ap*ap;
var ap3 = ap*ap2;
var ap4 = ap*ap3;
var err_term = 1 + (1.0/(12*ap))+(1.0/(288*ap2))-(139/(51840*ap3))
- (571/(2488320*ap4));
if (ap > 171)
lg = 0.5*(Math.log(2*Math.PI) - Math.log(ap)) + (ap * Math.log(ap)) - a + Math.log(err_term);
else
lg = Math.log(land_ios_gamma(ap));
// double u = log(p) + log(gamma(a + 1));
var u = Math.log(p) + lg;
result = zb * (1 - (a * Math.log(zb) - zb - u + Math.log(didonato_SN(a, z, 100, 1e-4))) / (a - zb));
}
}
}
// Do a couple of Halley iterations
var f, df, d2f, result_h;
df = 0.0; d2f = 0.0;
f = 0.0;
result_h = result;
var ga = 1.0/land_ios_gamma(a);
var lg = 0;
var a2 = a*a;
var a3 = a*a2;
var a4 = a*a3;
var err_term = 1 + (1.0/(12*a))+(1.0/(288*a2))-(139/(51840*a3))
- (571/(2488320*a4));
if (a > 171)
lg = 0.5*(Math.log(2*Math.PI) - Math.log(a)) + (a * Math.log(a)) - a + Math.log(err_term);
else
lg = Math.log(land_ios_gamma(a));
for (m =0; m < 20; m++)
{
f = land_gammainc_eval(result_h,a,0) - p;
df = ga * Math.exp( (a - 1)*Math.log(result_h) - result_h);
d2f = 1 * (((a - 1.0)/result_h) - 1.0) * df;
var delta = ((a - 1.0)/result_h) - 1;
df = Math.exp((a - 1)*Math.log(result_h) - result_h - lg);
result_h = result_h - (2*f)/(2*df - (f*delta));
}
return result_h;
}
// DiDonato based functions
function didonato_FN(p, a, x, N, tolerance)
{
//
// Computation of the Incomplete Gamma Function Ratios and their Inverse
// ARMIDO R. DIDONATO and ALFRED H. MORRIS, JR.
// ACM Transactions on Mathematical Software, Vol. 12, No. 4,
// December 1986, Pages 377-393.
//
// See equation 34.
//
var lg_res = 0;
var ap = a + 1;
var ap2 = ap*ap;
var ap3 = ap*ap2;
var ap4 = ap*ap3;
var err_term = 1 + (1.0/(12*ap))+(1.0/(288*ap2))-(139/(51840*ap3))
- (571/(2488320*ap4));
if (ap > 171)
{
lg_res = 0.5*(Math.log(2*Math.PI) - Math.log(ap)) + (ap * Math.log(ap)) - ap + Math.log(err_term);
}
else
{
lg_res = Math.log(land_ios_gamma(ap));
}
// double u = log(p) + log(gamma(a + 1));
var u = Math.log(p) + lg_res;
return Math.exp((u + x - Math.log(didonato_SN(a, x, N, tolerance))) / a);
}
function didonato_SN(a, x, N, tolerance)
{
//
// Computation of the Incomplete Gamma Function Ratios and their Inverse
// ARMIDO R. DIDONATO and ALFRED H. MORRIS, JR.
// ACM Transactions on Mathematical Software, Vol. 12, No. 4,
// December 1986, Pages 377-393.
//
// See equation 34.
//
var sum = 1;
if(N >= 1)
{
var partial = x / (a + 1);
sum = sum + partial;
for(i = 2; i <= N; ++i)
{
partial = partial * (x / (a + i));
sum = sum + partial;
if(partial < tolerance)
break;
}
}
return sum;
}
function find_inverse_s(p, q)
{
//
// Computation of the Incomplete Gamma Function Ratios and their Inverse
// ARMIDO R. DIDONATO and ALFRED H. MORRIS, JR.
// ACM Transactions on Mathematical Software, Vol. 12, No. 4,
// December 1986, Pages 377-393.
//
// See equation 32.
//
var t;
if(p < 0.5)
{
t = Math.sqrt(-2 * Math.log(p));
}
else
{
t = Math.sqrt(-2 * Math.log(q));
}
var a = new Array();
a[0] = 3.31125922108741;
a[1] = 11.6616720288968;
a[2] = 4.28342155967104;
a[3] = 0.213623493715853;
var b = new Array();
b[0] = 1;
b[1] = 6.61053765625462;
b[2] = 6.40691597760039;
b[3] = 1.27364489782223;
b[4] = 0.3611708101884203e-1;
var t2 = t * t;
var t3 = t2 * t;
var t4 = t2 * t2;
var num_sum = (a[0]*t3) + (a[1]*t2) + (a[2]*t) + a[3];
var den_sum = (b[0]*t4) + (b[1]*t3) + (b[2]*t2) + (b[3]*t) + b[4];
var s = t - (num_sum/den_sum);
if(p < 0.5)
s = -s;
return s;
}
function erf_taylor(x)
{
var res = 0;
var c = 2.0/Math.sqrt(Math.PI);
for (n = 0; n < 100; n++)
res = res + (Math.pow(-1,n)*Math.pow(x,2*n + 1))/(sFact(n) * ((2*n)+1))
res = c * res
return res
}
function chisquared_pdf(x,k)
{
var g_a;
var a = k/2.0;
if (a >= 171)
g_a = land_lgamma_stirling(a);
else
g_a = Math.log(land_ios_gamma(a));
var bt = Math.exp(-((a*Math.log(2))) - g_a + ((a - 1)*Math.log(x)) - (x/2));
return bt;
}
function bin_cdf(inp,n,p)
{
var res;
res = betainc_num(1-p, n-inp, 1+inp);
return res;
}
function betainc_num(x, a, b)
{
var bt;
var g_ab;
var g_a;
var g_b;
if ((a + b) >= 171)
g_ab = land_lgamma_stirling(a+b);
else
g_ab = Math.log(land_ios_gamma(a+b));
if (a >= 171)
g_a = land_lgamma_stirling(a);
else
g_a = Math.log(land_ios_gamma(a));
if (b >= 171)
g_b = land_lgamma_stirling(b);
else
g_b = Math.log(land_ios_gamma(b));
bt = Math.exp(g_ab - g_a - g_b + a*Math.log(x)+b*Math.log(1.0-x));
if (x == 0)
bt = 0;
if (x < ((a + 1.0)/(a + b + 2.0)))
return bt*betacf(a,b,x)/a;
else
return 1 - (bt*betacf(b,a,1.0-x)/b);
}
function betacf(a, b, x)
{
var maxit = 100;
var eps = 3e-16;
var fpmin = 1e-30;
var aa;
var c;
var d;
var del;
var h;
var qab;
var qam;
var qap;
qab = a + b;
qap = a + 1;
qam = a - 1;
c = 1.0;
d = 1.0 - qab*x/qap;
if (Math.abs(d)<fpmin)
d = fpmin;
d = 1.0/d;
h = d;
var m2;
for (m = 1; m < maxit; m++)
{
m2 = 2*m;
aa = m*(b-m)*x/((qam + m2)*(a + m2));
d = 1.0 + aa*d;
if (Math.abs(d)<fpmin)
d = fpmin;
c = 1.0 + aa/c;
if (Math.abs(c)<fpmin)
c = fpmin;
d = 1.0/d;
h = h*d*c;
aa = -(a + m)*(qab + m)*x/((a+m2)*(qap+m2));
d = 1.0 + aa*d;
if (Math.abs(d)<fpmin)
d = fpmin;
c = 1.0 + aa/c;
if (Math.abs(c)<fpmin)
c = fpmin;
d = 1.0/d;
del = d*c;
h = h*del;
if (Math.abs(del-1.0)< eps)
{
// std::cout << "Breaking out at iter " << m << std::endl;
break;
}
}
// std::cout << " h is " << h << std::endl;
return h;
}
function land_ios_gamma(x)
{
var g = 7;
var y;
var t;
var res_fr;
var p = new Array()
p[0] = 0.99999999999980993;
p[1] = 676.5203681218851;
p[2] = -1259.1392167224028;
p[3] = 771.32342877765313;
p[4] = -176.61502916214059;
p[5] = 12.507343278686905;
p[6] = -0.13857109526572012;
p[7] = 9.9843695780195716e-6;
p[8] = 1.5056327351493116e-7;
if (Math.abs(x - Math.floor(x)) < 1e-16)
{
if ( x > 1)
return sFact(x - 1);
else if (x == 1)
return 1;
else
return 1/0.0;
}
else
{
x -= 1;
y = p[0];
for (i=1; i < g+2; i++)
{
y = y + p[i]/(x + i);
}
t = x + g + 0.5;
res_fr = Math.sqrt(2*Math.PI) * Math.exp(((x+0.5)*Math.log(t))-t)*y;
return res_fr;
}
}
function land_lgamma_stirling(x)
{
var t = 0.5*Math.log(2*Math.PI) - 0.5*Math.log(x) + x*(Math.log(x))-x;
var x2 = x * x;
var x3 = x2 * x;
var x4 = x3 * x;
var err_term = Math.log(1 + (1.0/(12*x)) + (1.0/(288*x2)) - (139.0/(51840*x3))
- (571.0/(2488320*x4)));
var res = t + err_term;
return res;
}
function sFact(num)
{
var rval=1;
for (var i = 2; i <= num; i++)
rval = rval * i;
return rval;
}
function inverse_beta(p, alpha, beta)
{
var x = 0;
var a = 0;
var b = 1;
var precision = 1e-15;
var iter_num = 0;
while (((b - a) > precision) & (iter_num < 100))
{
x = (a + b) / 2;
if (betainc_num(x,alpha,beta) > p)
b = x;
else
a = x;
iter_num = iter_num + 1;
}
return x;
}
</script>Alijah Ahmedhttp://www.blogger.com/profile/01151589526613233918noreply@blogger.comtag:blogger.com,1999:blog-8441309013858171051.post-47356353538339545642013-11-30T11:02:00.002-08:002018-09-13T12:04:47.516-07:00McNemar's Test Calculator<form name="frmOne">
<p style="text-align:justify"> This blog post implements an online McNemar's Test on a 2 by 2 contingency table.</p>
<p style="text-align:justify"> Referring to the contingency table below, the Null Hypothesis for the McNemar test is that the marginal probability of Test 1 being positive is equal to the marginal probability of Test 2 being positive as well. The Null Hypothesis is also that the marginal probability of Test 1 being negative is equal to the marginal probability of Test 2 being negative. If we denote the following:
<ol>
<li>$p_a$=Probability of Test 1 being positive and Test 2 being positive</li>
<li>$p_b$=Probability of Test 1 being positive and Test 2 being negative</li>
<li>$p_c$=Probability of Test 1 being negative and Test 2 being positive</li>
<li>$p_d$=Probability of Test 1 being negative and Test 2 being negative</li>
</ol>
</p>
<p style="text-align:justify">The Null Hypothesis is $p_a+p_b=p_a+p_c$ or $p_b+p_d=p_c+p_d$, which leads to $p_b=p_c$. In other words, the Null Hypothesis depends on only the off-diagonal terms of the contingency table.
</p>
<p style="text-align:justify">A low value of the calculated p-value (< 0.05) could be considered a significant result, rejecting the Null Hypothesis.</p>
<p style="text-align:justify"> Please fill in the four text areas with integers greater than or equal to zero.</p>
<table border="1" class="gradienttable" style="width: 100%">
<tbody>
<tr>
<td></td>
<td>Test 2 positive: </td>
<td>Test 2 negative: </td>
</tr>
<tr>
<td>Test 1 positive: </td>
<td><input name="txtANumber" size="20" type="TextArea" value="" /></td>
<td><input name="txtBNumber" size="20" type="TextArea" value="" /></td>
<td><p id="r1">Row Sum 1</p></td>
</tr>
<tr>
<td>Test 1 negative: </td>
<td><input name="txtCNumber" size="20" type="TextArea" value="" /></td>
<td><input name="txtDNumber" size="20" type="TextArea" value="" /></td>
<td><p id="r2">Row Sum 2</p></td>
</tr>
<td></td>
<td><p id="c1">Column Sum 1</p></td>
<td><p id="c2">Column Sum 2</p></td>
<td><p id="t1">Total</p></td>
</tbody>
</table>
<br>
<br>
<input name="b1" onclick="calculate_mcnemar()" type="Button" value="Perform McNemar's test" />
<br>
<br>
<p id="p1">Results pending...</p>
</form>
<script language="JavaScript">
function calculate_mcnemar() {
var a = Number(document.frmOne.txtANumber.value);
var b = Number(document.frmOne.txtBNumber.value);
var c = Number(document.frmOne.txtCNumber.value);
var d = Number(document.frmOne.txtDNumber.value);
var r1n = a + b;
var r2n = c + d;
var c1n = a + c;
var c2n = b + d;
var Ntot = a + b + c + d;
document.getElementById("r1").innerHTML = r1n.toString();
document.getElementById("r2").innerHTML = r2n.toString();
document.getElementById("c1").innerHTML = c1n.toString();
document.getElementById("c2").innerHTML = c2n.toString();
document.getElementById("t1").innerHTML = Ntot.toString();
var mcs = ((b - c) * (b - c))/(b + c);
var prob_1 = 1 - chisquared_cdf(mcs,1);
var mcs_yates = ((Math.abs(b - c) - 0.5) * (Math.abs(b - c) - 0.5))/(b + c);
var prob_2 = 1 - chisquared_cdf(mcs_yates,1);
var mcs_yates2 = ((Math.abs(b - c) - 1) * (Math.abs(b - c) - 1))/(b + c);
var prob_3 = 1 - chisquared_cdf(mcs_yates2,1);
var res_str = '';
tmp_str = "McNemar chi-squared statistic is " + mcs.toFixed(6) + "<br>";
res_str = tmp_str;
tmp_str = "Corresponding p-value is " + prob_1.toFixed(6) + "<br><br>"
res_str = res_str.concat(tmp_str);
tmp_str = "McNemar chi-squared statistic with Yates correction of 0.5 is " + mcs_yates.toFixed(6) + "<br>";
res_str = res_str.concat(tmp_str);
tmp_str = "Corresponding p-value is " + prob_2.toFixed(6) + "<br><br>"
res_str = res_str.concat(tmp_str);
tmp_str = "McNemar chi-squared statistic with Yates correction of 1.0 is " + mcs_yates2.toFixed(6) + "<br>";
res_str = res_str.concat(tmp_str);
tmp_str = "Corresponding p-value is " + prob_3.toFixed(6) + "<br><br>"
res_str = res_str.concat(tmp_str);
var res_bin_str = '';
var bin_res = 0;
bin_res = 2 * bin_cdf(b,b+c,0.5);
if (bin_res > 1)
bin_res = 2 - bin_res;
// Corner case , when lower off-diagonal entry is 0
if (c == 0)
{
bin_res = 2 * Math.pow(0.5,b+c);
}
tmp_str = "Result using binomial exact test is " + bin_res.toFixed(6) + "<br><br>";
res_str = res_str.concat(tmp_str);
var odds_ratio = b/c;
var se = Math.sqrt((1/b) + (1/c));
var log_odds_ratio = Math.log(odds_ratio);
var low_lim = Math.exp(log_odds_ratio - (1.96 * se));
var upp_lim = Math.exp(log_odds_ratio + (1.96 * se));
tmp_str = "Odds ratio equals " + odds_ratio.toFixed(6) + " with 95% Confidence interval from " + low_lim.toFixed(6) + " to " + upp_lim.toFixed(6) + "<br>";
res_str = res_str.concat(tmp_str);
res_str = res_str.fontcolor("blue");
document.getElementById("p1").innerHTML = res_str;
}
// Chi-squared CDF evaluation
function chisquared_cdf(inp,p1)
{
var res;
res = land_gammainc_eval(inp/2.0, p1/2.0, 0);
return res;
}
function chisquared_icdf(prob,p1)
{
var res;
res = find_inverse_gamma(p1/2.0, prob, 1.0 - prob);
res = res*2;
if (prob == 0)
res = 0;
return res;
}
function land_gammainc_eval(x, a, upper)
{
var tmp;
var xeg = ((1 + 0.0)/land_ios_gamma(a+1))*Math.pow(x+0.0,a)*Math.exp(-x);
var acc = 0.0;
var Pax;
var Qax;
var lambda;
var eta;
var gamma_star;
var var1;
var acc_f;
var SaEta,RaEta;
var f = new Array();
var b = new Array();
var ak_vec = new Array();
var s_vec = new Array();
var t_vec = new Array();
var rho_vec = new Array();
var log_diff;
var NUMSERIES = 100;
var VNUM = 100;
var QSUM = 100;
if (Math.abs(x) < 1e-16)
{
if (upper == 0)
return 0;
else
return 1;
}
if (x <= a)
{
if ((Math.abs(x) < 10) & (Math.abs(a) < 10) )
{
for (k=0; k < NUMSERIES; k++)
{
if (a+k+1 < 170)
acc += Math.pow(x,k+0.0)*((land_ios_gamma(a+1)+0.0)/land_ios_gamma(k+a+1));
else
{
log_diff = land_lgamma_stirling(a+1) - land_lgamma_stirling(k+a+1) + (k * log(x));
acc += Math.exp(log_diff);
}
}
Pax = xeg * acc;
if (upper == 0)
return Pax;
else
return (1-Pax);
}
else
{
lambda = x/a;
eta = Math.sqrt(2*(lambda - 1 - Math.log(lambda)));
if ((lambda - 1) < 0)
eta = -1 * eta;
// For large a, gamma_star approaches unity..
if (a > 140)
gamma_star = 1.0;
else
gamma_star = land_ios_gamma(a)/(Math.sqrt(2*Math.PI) * Math.exp(-a) * Math.pow(a,a-0.5));
for (k=0;k<VNUM;k++)
f[k] = 0.0;
f[0] = 1;
f[1] = -1.0/3.0;
f[2] = 1.0/12.0;
f[3] = -2.0/135.0;
for (m=4; m < VNUM; m++)
{
var1 = ((m - 1.0)/(3.0*m))* f[m-1];
acc_f = 0.0;
for (j=3; j < m; j++)
{
acc_f = acc_f + (f[j-1] * f[m+1-j])/(m+2-j);
}
f[m] = -((m+1.0)/(m+2.0))*(var1 + acc_f);
}
for (k=0; k < VNUM-1; k++)
b[k] = 0.0;
b[VNUM-2] = f[VNUM-1];
b[VNUM-3] = f[VNUM-2];
for (m = VNUM-3; m > 0; m--)
b[m-1] = f[m] + (((m + 1.0)/a)*b[m+1]);
SaEta = 0.0;
for (m=0; m < b.length; m++)
SaEta = SaEta + (b[m] * Math.pow(eta,m+0.0));
SaEta = SaEta/gamma_star;
RaEta = (SaEta/Math.sqrt(2*Math.PI*a))*Math.exp(-0.5*a*eta*eta);
Qax = (0.5 * (1 - erf_taylor(eta*Math.sqrt(a/2.0)))) + RaEta;
Pax = (0.5 * (1 - erf_taylor(-eta*Math.sqrt(a/2.0)))) - RaEta;
if (upper == 1)
return Qax;
else
return Pax;
// EndOf Section that details modification (and improvement)
// to Gautschi's algorithm
}
}
else
{
if (x <= 1)
{
xeg = Math.pow(x,a)/land_ios_gamma(a);
acc_f = 0.0;
for (k=0; k < QSUM; k++)
acc_f += (Math.pow(-1.0,k)*Math.pow(x,k))/((a+k)*sFact(k));
tmp = xeg * acc_f;
if (upper == 1)
return 1 - tmp;
else
return tmp;
}
else
{
var g1 = (x -a + 1);
for (k=0; k < QSUM; k++)
ak_vec[k] = (k*(a - k))/((x-a+(2*k)-1)*(x-a+(2*k)+1));
for (k=0; k < QSUM; k++)
{
s_vec[k] = 0;
t_vec[k] = 0;
rho_vec[k] = 0;
}
for (k=0; k < QSUM; k++)
{
if (k == 0)
{
s_vec[k] = 0;
rho_vec[k] = 0;
t_vec[k] = 1;
}
else
{
var ak = ak_vec[k];
var rhokm1 = rho_vec[k-1];
rho_vec[k] =(-(ak)*(1 + rhokm1))/(1 + (ak*(1 + rhokm1)));
var rhok = rho_vec[k];
var tkm1 = t_vec[k-1];
t_vec[k] = rhok*tkm1;
var skm1 = s_vec[k-1];
s_vec[k] = skm1 + tkm1;
// [rho_vec[k] = (-(ak_vec[k])*(1 + rho_vec[k-1]))
// /(1 + (ak_vec[k]*(1 + rho_vec[k-1])));
// t_vec[k] = rho_vec[k]*t_vec[k-1];
// s_vec[k] = s_vec[k-1] + t_vec[k-1];
}
}
var slast = s_vec[QSUM-1];
var tmp_scale = (Math.pow(x,a)*Math.exp(-x)/land_ios_gamma(a));
var lga1 = a * Math.log(x);
var lga2 = (0.5 - a) * Math.log(a);
var lga3 = 0.5 * Math.log(2*Math.PI);
var lgtot = lga1 + (a - x) + lga2 - lga3;
if (a > 140)
tmp_scale = Math.exp(lgtot);
var newres;
if (land_ios_gamma(a) == 0.0)
newres = 0;
else
newres = (tmp_scale*slast)/g1;
if (upper == 1)
return newres;
else
return (1 - newres);
// EndOf Gautschi's routine
}
}
}
function find_inverse_gamma(a, p, q)
{
//
// In order to understand what's going on here, you will
// need to refer to:
//
// Computation of the Incomplete Gamma Function Ratios and their Inverse
// ARMIDO R. DIDONATO and ALFRED H. MORRIS, JR.
// ACM Transactions on Mathematical Software, Vol. 12, No. 4,
// December 1986, Pages 377-393.
//
var result;
if(a == 1)
{
result = -Math.log(q);
}
else if(a < 1)
{
var g = land_ios_gamma(a);
var b = q * g;
if((b > 0.6) || ((b >= 0.45) && (a >= 0.3)))
{
// DiDonato & Morris Eq 21:
//
// There is a slight variation from DiDonato and Morris here:
// the first form given here is unstable when p is close to 1,
// making it impossible to compute the inverse of Q(a,x) for small
// q. Fortunately the second form works perfectly well in this case.
//
var u;
if((b * q > 1e-8) && (q > 1e-5))
{
u = Math.pow(p * g * a, 1 / a);
}
else
{
u = Math.exp((-q / a) - 0.5772156649);
}
result = u / (1 - (u / (a + 1)));
}
else if((a < 0.3) && (b >= 0.35))
{
// DiDonato & Morris Eq 22:
var t = Math.exp(-0.5772156649 - b);
var u = t * Math.exp(t);
result = t * Math.exp(u);
}
else if((b > 0.15) || (a >= 0.3))
{
// DiDonato & Morris Eq 23:
var y = -Math.log(b);
var u = y - (1 - a) * Math.log(y);
result = y - (1 - a) * Math.log(u) - Math.log(1 + (1 - a) / (1 + u));
}
else if (b > 0.1)
{
// DiDonato & Morris Eq 24:
var y = -log(b);
var u = y - (1 - a) * log(y);
result = y - (1 - a) * log(u) - log((u * u + 2 * (3 - a) * u + (2 - a) * (3 - a)) / (u * u + (5 - a) * u + 2));
}
else
{
// DiDonato & Morris Eq 25:
var y = -Math.log(b);
var c1 = (a - 1) * Math.log(y);
var c1_2 = c1 * c1;
var c1_3 = c1_2 * c1;
var c1_4 = c1_2 * c1_2;
var a_2 = a * a;
var a_3 = a_2 * a;
var c2 = (a - 1) * (1 + c1);
var c3 = (a - 1) * (-(c1_2 / 2) + (a - 2) * c1 + (3 * a - 5) / 2);
var c4 = (a - 1) * ((c1_3 / 3)
- (3 * a - 5) * c1_2 / 2 + (a_2 - 6 * a + 7) * c1 + (11 * a_2 - 46 * a + 47) / 6);
var c5 = (a - 1) * (-(c1_4 / 4)
+ (11 * a - 17) * c1_3 / 6
+ (-3 * a_2 + 13 * a -13) * c1_2
+ (2 * a_3 - 25 * a_2 + 72 * a - 61) * c1 / 2
+ (25 * a_3 - 195 * a_2 + 477 * a - 379) / 12);
var y_2 = y * y;
var y_3 = y_2 * y;
var y_4 = y_2 * y_2;
result = y + c1 + (c2 / y) + (c3 / y_2) + (c4 / y_3) + (c5 / y_4);
}
}
else
{
// DiDonato and Morris Eq 31:
var s = find_inverse_s(p, q);
var s_2 = s * s;
var s_3 = s_2 * s;
var s_4 = s_2 * s_2;
var s_5 = s_4 * s;
var ra = Math.sqrt(a);
var w = a + s * ra + (s * s -1) / 3;
w += (s_3 - 7 * s) / (36 * ra);
w -= (3 * s_4 + 7 * s_2 - 16) / (810 * a);
w += (9 * s_5 + 256 * s_3 - 433 * s) / (38880 * a * ra);
if((a >= 500) && (Math.abs(1 - w / a) < 1e-6))
{
result = w;
}
else if (p > 0.5)
{
if(w < 3 * a)
{
result = w;
}
else
{
var D = Math.max((2), (a * (a - 1)));
var lg = 0;
var a2 = a*a;
var a3 = a*a2;
var a4 = a*a3;
var err_term = 1 + (1.0/(12*a))+(1.0/(288*a2))-(139/(51840*a3))
- (571/(2488320*a4));
if (a > 171)
lg = 0.5*(Math.log(2*Math,PI) - Math.log(a)) + (a * Math.log(a)) - a + Math.log(err_term);
else
lg = Math.log(land_ios_gamma(a));
var lb = Math.log(q) + lg;
if(lb < -D * 2.3)
{
// DiDonato and Morris Eq 25:
var y = -lb;
var c1 = (a - 1) * Math.log(y);
var c1_2 = c1 * c1;
var c1_3 = c1_2 * c1;
var c1_4 = c1_2 * c1_2;
var a_2 = a * a;
var a_3 = a_2 * a;
var c2 = (a - 1) * (1 + c1);
var c3 = (a - 1) * (-(c1_2 / 2) + (a - 2) * c1 + (3 * a - 5) / 2);
var c4 = (a - 1) * ((c1_3 / 3)
- (3 * a - 5) * c1_2 / 2 + (a_2 - 6 * a + 7) * c1 + (11 * a_2 - 46 * a + 47) / 6);
var c5 = (a - 1) * (-(c1_4 / 4)
+ (11 * a - 17) * c1_3 / 6
+ (-3 * a_2 + 13 * a -13) * c1_2
+ (2 * a_3 - 25 * a_2 + 72 * a - 61) * c1 / 2
+ (25 * a_3 - 195 * a_2 + 477 * a - 379) / 12);
var y_2 = y * y;
var y_3 = y_2 * y;
var y_4 = y_2 * y_2;
result = y + c1 + (c2 / y) + (c3 / y_2) + (c4 / y_3) + (c5 / y_4);
}
else
{
// DiDonato and Morris Eq 33:
var u = -lb + (a - 1) * Math.log(w) - Math.log(1 + (1 - a) / (1 + w));
result = -lb + (a - 1) * Math.log(u) - Math.log(1 + (1 - a) / (1 + u));
}
}
}
else
{
// DiDonato and Morris Eq 35:
var z = didonato_FN(p, a, w, 0, 0);
z = didonato_FN(p, a, z, 2, 0.0);
z = didonato_FN(p, a, z, 2, 0.0);
z = didonato_FN(p, a, z, 3, 0.0);
if((z <= 0.01 * (a + 1)) || (z > 0.7 * (a + 1)))
{
result = z;
}
else
{
// DiDonato and Morris Eq 36:
var zb = didonato_FN(p, a, z, 100, 1e-4);
var lg = 0;
var ap = a + 1;
var ap2 = ap*ap;
var ap3 = ap*ap2;
var ap4 = ap*ap3;
var err_term = 1 + (1.0/(12*ap))+(1.0/(288*ap2))-(139/(51840*ap3))
- (571/(2488320*ap4));
if (ap > 171)
lg = 0.5*(Math.log(2*Math.PI) - Math.log(ap)) + (ap * Math.log(ap)) - a + Math.log(err_term);
else
lg = Math.log(land_ios_gamma(ap));
// double u = log(p) + log(gamma(a + 1));
var u = Math.log(p) + lg;
result = zb * (1 - (a * Math.log(zb) - zb - u + Math.log(didonato_SN(a, z, 100, 1e-4))) / (a - zb));
}
}
}
// Do a couple of Halley iterations
var f, df, d2f, result_h;
df = 0.0; d2f = 0.0;
f = 0.0;
result_h = result;
var ga = 1.0/land_ios_gamma(a);
var lg = 0;
var a2 = a*a;
var a3 = a*a2;
var a4 = a*a3;
var err_term = 1 + (1.0/(12*a))+(1.0/(288*a2))-(139/(51840*a3))
- (571/(2488320*a4));
if (a > 171)
lg = 0.5*(Math.log(2*Math.PI) - Math.log(a)) + (a * Math.log(a)) - a + Math.log(err_term);
else
lg = Math.log(land_ios_gamma(a));
for (m =0; m < 20; m++)
{
f = land_gammainc_eval(result_h,a,0) - p;
df = ga * Math.exp( (a - 1)*Math.log(result_h) - result_h);
d2f = 1 * (((a - 1.0)/result_h) - 1.0) * df;
var delta = ((a - 1.0)/result_h) - 1;
df = Math.exp((a - 1)*Math.log(result_h) - result_h - lg);
result_h = result_h - (2*f)/(2*df - (f*delta));
}
return result_h;
}
// DiDonato based functions
function didonato_FN(p, a, x, N, tolerance)
{
//
// Computation of the Incomplete Gamma Function Ratios and their Inverse
// ARMIDO R. DIDONATO and ALFRED H. MORRIS, JR.
// ACM Transactions on Mathematical Software, Vol. 12, No. 4,
// December 1986, Pages 377-393.
//
// See equation 34.
//
var lg_res = 0;
var ap = a + 1;
var ap2 = ap*ap;
var ap3 = ap*ap2;
var ap4 = ap*ap3;
var err_term = 1 + (1.0/(12*ap))+(1.0/(288*ap2))-(139/(51840*ap3))
- (571/(2488320*ap4));
if (ap > 171)
{
lg_res = 0.5*(Math.log(2*Math.PI) - Math.log(ap)) + (ap * Math.log(ap)) - ap + Math.log(err_term);
}
else
{
lg_res = Math.log(land_ios_gamma(ap));
}
// double u = log(p) + log(gamma(a + 1));
var u = Math.log(p) + lg_res;
return Math.exp((u + x - Math.log(didonato_SN(a, x, N, tolerance))) / a);
}
function didonato_SN(a, x, N, tolerance)
{
//
// Computation of the Incomplete Gamma Function Ratios and their Inverse
// ARMIDO R. DIDONATO and ALFRED H. MORRIS, JR.
// ACM Transactions on Mathematical Software, Vol. 12, No. 4,
// December 1986, Pages 377-393.
//
// See equation 34.
//
var sum = 1;
if(N >= 1)
{
var partial = x / (a + 1);
sum = sum + partial;
for(i = 2; i <= N; ++i)
{
partial = partial * (x / (a + i));
sum = sum + partial;
if(partial < tolerance)
break;
}
}
return sum;
}
function find_inverse_s(p, q)
{
//
// Computation of the Incomplete Gamma Function Ratios and their Inverse
// ARMIDO R. DIDONATO and ALFRED H. MORRIS, JR.
// ACM Transactions on Mathematical Software, Vol. 12, No. 4,
// December 1986, Pages 377-393.
//
// See equation 32.
//
var t;
if(p < 0.5)
{
t = Math.sqrt(-2 * Math.log(p));
}
else
{
t = Math.sqrt(-2 * Math.log(q));
}
var a = new Array();
a[0] = 3.31125922108741;
a[1] = 11.6616720288968;
a[2] = 4.28342155967104;
a[3] = 0.213623493715853;
var b = new Array();
b[0] = 1;
b[1] = 6.61053765625462;
b[2] = 6.40691597760039;
b[3] = 1.27364489782223;
b[4] = 0.3611708101884203e-1;
var t2 = t * t;
var t3 = t2 * t;
var t4 = t2 * t2;
var num_sum = (a[0]*t3) + (a[1]*t2) + (a[2]*t) + a[3];
var den_sum = (b[0]*t4) + (b[1]*t3) + (b[2]*t2) + (b[3]*t) + b[4];
var s = t - (num_sum/den_sum);
if(p < 0.5)
s = -s;
return s;
}
function erf_taylor(x)
{
var res = 0;
var c = 2.0/Math.sqrt(Math.PI);
for (n = 0; n < 100; n++)
res = res + (Math.pow(-1,n)*Math.pow(x,2*n + 1))/(sFact(n) * ((2*n)+1))
res = c * res
return res
}
function chisquared_pdf(x,k)
{
var g_a;
var a = k/2.0;
if (a >= 171)
g_a = land_lgamma_stirling(a);
else
g_a = Math.log(land_ios_gamma(a));
var bt = Math.exp(-((a*Math.log(2))) - g_a + ((a - 1)*Math.log(x)) - (x/2));
return bt;
}
function betainc_num(x, a, b)
{
var bt;
var g_ab;
var g_a;
var g_b;
if ((a + b) >= 171)
g_ab = land_lgamma_stirling(a+b);
else
g_ab = Math.log(land_ios_gamma(a+b));
if (a >= 171)
g_a = land_lgamma_stirling(a);
else
g_a = Math.log(land_ios_gamma(a));
if (b >= 171)
g_b = land_lgamma_stirling(b);
else
g_b = Math.log(land_ios_gamma(b));
bt = Math.exp(g_ab - g_a - g_b + a*Math.log(x)+b*Math.log(1.0-x));
if (x == 0)
bt = 0;
if (x < ((a + 1.0)/(a + b + 2.0)))
return bt*betacf(a,b,x)/a;
else
return 1 - (bt*betacf(b,a,1.0-x)/b);
}
function betacf(a, b, x)
{
var maxit = 100;
var eps = 3e-16;
var fpmin = 1e-30;
var aa;
var c;
var d;
var del;
var h;
var qab;
var qam;
var qap;
qab = a + b;
qap = a + 1;
qam = a - 1;
c = 1.0;
d = 1.0 - qab*x/qap;
if (Math.abs(d)<fpmin)
d = fpmin;
d = 1.0/d;
h = d;
var m2;
for (m = 1; m < maxit; m++)
{
m2 = 2*m;
aa = m*(b-m)*x/((qam + m2)*(a + m2));
d = 1.0 + aa*d;
if (Math.abs(d)<fpmin)
d = fpmin;
c = 1.0 + aa/c;
if (Math.abs(c)<fpmin)
c = fpmin;
d = 1.0/d;
h = h*d*c;
aa = -(a + m)*(qab + m)*x/((a+m2)*(qap+m2));
d = 1.0 + aa*d;
if (Math.abs(d)<fpmin)
d = fpmin;
c = 1.0 + aa/c;
if (Math.abs(c)<fpmin)
c = fpmin;
d = 1.0/d;
del = d*c;
h = h*del;
if (Math.abs(del-1.0)< eps)
{
// std::cout << "Breaking out at iter " << m << std::endl;
break;
}
}
// std::cout << " h is " << h << std::endl;
return h;
}
function land_ios_gamma(x)
{
var g = 7;
var y;
var t;
var res_fr;
var p = new Array()
p[0] = 0.99999999999980993;
p[1] = 676.5203681218851;
p[2] = -1259.1392167224028;
p[3] = 771.32342877765313;
p[4] = -176.61502916214059;
p[5] = 12.507343278686905;
p[6] = -0.13857109526572012;
p[7] = 9.9843695780195716e-6;
p[8] = 1.5056327351493116e-7;
if (Math.abs(x - Math.floor(x)) < 1e-16)
{
if ( x > 1)
return sFact(x - 1);
else if (x == 1)
return 1;
else
return 1/0.0;
}
else
{
x -= 1;
y = p[0];
for (i=1; i < g+2; i++)
{
y = y + p[i]/(x + i);
}
t = x + g + 0.5;
res_fr = Math.sqrt(2*Math.PI) * Math.exp(((x+0.5)*Math.log(t))-t)*y;
return res_fr;
}
}
function land_lgamma_stirling(x)
{
var t = 0.5*Math.log(2*Math.PI) - 0.5*Math.log(x) + x*(Math.log(x))-x;
var x2 = x * x;
var x3 = x2 * x;
var x4 = x3 * x;
var err_term = Math.log(1 + (1.0/(12*x)) + (1.0/(288*x2)) - (139.0/(51840*x3))
- (571.0/(2488320*x4)));
var res = t + err_term;
return res;
}
function sFact(num)
{
var rval=1;
for (var i = 2; i <= num; i++)
rval = rval * i;
return rval;
}
function bin_cdf(inp,n,p)
{
var res;
res = betainc_num(1-p, n-inp, 1+inp);
return res;
}
function inverse_beta(p, alpha, beta)
{
var x = 0;
var a = 0;
var b = 1;
var precision = 1e-15;
var iter_num = 0;
while (((b - a) > precision) & (iter_num < 100))
{
x = (a + b) / 2;
if (betainc_num(x,alpha,beta) > p)
b = x;
else
a = x;
iter_num = iter_num + 1;
}
return x;
}
</script>Alijah Ahmedhttp://www.blogger.com/profile/01151589526613233918noreply@blogger.com272