123 lines
5.5 KiB
JavaScript
123 lines
5.5 KiB
JavaScript
var bpCache = null;
|
|
|
|
export function loadBpReferenceData() {
|
|
if (bpCache) return Promise.resolve(bpCache);
|
|
return fetch('/data/calculators/bp-reference.json', { credentials: 'same-origin' })
|
|
.then(function(response) {
|
|
if (!response.ok) throw new Error('Failed to load BP reference data');
|
|
return response.json();
|
|
})
|
|
.then(function(data) {
|
|
bpCache = data;
|
|
return bpCache;
|
|
});
|
|
}
|
|
|
|
function normalCDF(z) {
|
|
var a1=0.254829592, a2=-0.284496736, a3=1.421413741, a4=-1.453152027, a5=1.061405429, p=0.3275911;
|
|
var sign = z < 0 ? -1 : 1;
|
|
z = Math.abs(z) / Math.sqrt(2);
|
|
var t = 1 / (1 + p * z);
|
|
var y = 1 - (((((a5*t+a4)*t)+a3)*t+a2)*t+a1)*t*Math.exp(-z*z);
|
|
return 0.5 * (1 + sign * y);
|
|
}
|
|
|
|
export function calcHeightPercentile(reference, age, sex, heightCm) {
|
|
var idx = Math.max(0, Math.min(217, Math.round(age * 12) - 24));
|
|
var table = reference.heightLms[sex === 'female' ? 'female' : 'male'];
|
|
var L = table.L[idx], M = table.M[idx], S = table.S[idx];
|
|
var z = (Math.pow(heightCm / M, L) - 1) / (L * S);
|
|
return { z: z, percentile: normalCDF(z) * 100 };
|
|
}
|
|
|
|
export function computeBPPercentile(reference, age, sex, heightCm, bpType, bpValue) {
|
|
var t1, t2, t3, t4, t5, ta1, ta2, ta3, ta4, ta5, tb1, tb2, tb3, tb4, tb5, w;
|
|
if (sex === 'female') {
|
|
t1=106.7; t2=140.7; t3=154.0; t4=160.5; t5=168.9;
|
|
ta1=5.00; ta2=10.70; ta3=13.16; ta4=14.51; ta5=17.33;
|
|
tb1=6.701; tb2=16.438; tb3=46.80; tb4=84.46; tb5=203.608;
|
|
w = (age - 10) * (heightCm - 147);
|
|
} else {
|
|
t1=107.8; t2=140.0; t3=154.5; t4=166.4; t5=179.1;
|
|
ta1=5.06; ta2=10.79; ta3=13.22; ta4=14.51; ta5=17.30;
|
|
tb1=-15; tb2=8.9; tb3=50.375; tb4=112.684; tb5=250.04;
|
|
w = (age - 10) * (heightCm - 150);
|
|
}
|
|
|
|
var x = heightCm, y = age;
|
|
|
|
var x2a = Math.max(0, x - t1); var x2b = Math.max(0, x - t4); var x2c = Math.max(0, x - t5);
|
|
var x2 = Math.pow(x2a,3) - Math.pow(x2b,3)*(t5-t1)/(t5-t4) + Math.pow(x2c,3)*(t4-t1)/(t5-t4);
|
|
var x3a = Math.max(0, x - t2);
|
|
var x3 = Math.pow(x3a,3) - Math.pow(x2b,3)*(t5-t2)/(t5-t4) + Math.pow(x2c,3)*(t4-t2)/(t5-t4);
|
|
var x4a = Math.max(0, x - t3);
|
|
var x4 = Math.pow(x4a,3) - Math.pow(x2b,3)*(t5-t3)/(t5-t4) + Math.pow(x2c,3)*(t4-t3)/(t5-t4);
|
|
var x2s = x2/100, x3s = x3/100, x4s = x4/100;
|
|
|
|
var y2a = Math.max(0, y - ta1); var y2b = Math.max(0, y - ta4); var y2c = Math.max(0, y - ta5);
|
|
var y2 = Math.pow(y2a,3) - Math.pow(y2b,3)*(ta5-ta1)/(ta5-ta4) + Math.pow(y2c,3)*(ta4-ta1)/(ta5-ta4);
|
|
var y3a = Math.max(0, y - ta2);
|
|
var y3 = Math.pow(y3a,3) - Math.pow(y2b,3)*(ta5-ta2)/(ta5-ta4) + Math.pow(y2c,3)*(ta4-ta2)/(ta5-ta4);
|
|
var y4a = Math.max(0, y - ta3);
|
|
var y4 = Math.pow(y4a,3) - Math.pow(y2b,3)*(ta5-ta3)/(ta5-ta4) + Math.pow(y2c,3)*(ta4-ta3)/(ta5-ta4);
|
|
var y2s = y2/100, y3s = y3/100, y4s = y4/100;
|
|
|
|
var w2a = Math.max(0, w - tb1); var w2b = Math.max(0, w - tb4); var w2c = Math.max(0, w - tb5);
|
|
var w2 = Math.pow(w2a,3) - Math.pow(w2b,3)*(tb5-tb1)/(tb5-tb4) + Math.pow(w2c,3)*(tb4-tb1)/(tb5-tb4);
|
|
var w3a = Math.max(0, w - tb2);
|
|
var w3 = Math.pow(w3a,3) - Math.pow(w2b,3)*(tb5-tb2)/(tb5-tb4) + Math.pow(w2c,3)*(tb4-tb2)/(tb5-tb4);
|
|
var w4a = Math.max(0, w - tb3);
|
|
var w4 = Math.pow(w4a,3) - Math.pow(w2b,3)*(tb5-tb3)/(tb5-tb4) + Math.pow(w2c,3)*(tb4-tb3)/(tb5-tb4);
|
|
var w2s = w2/10000, w3s = w3/10000, w4s = w4/10000;
|
|
|
|
var coeff = reference.coefficients[sex === 'female' ? 'female' : 'male'][bpType === 'sys' ? 'sys' : 'dia'];
|
|
var predicted = new Array(99);
|
|
for (var i = 0; i < 99; i++) {
|
|
predicted[i] = coeff[i][0] + coeff[i][5]*x + coeff[i][6]*x2s + coeff[i][7]*x3s + coeff[i][8]*x4s
|
|
+ coeff[i][1]*y + coeff[i][2]*y2s + coeff[i][3]*y3s + coeff[i][4]*y4s
|
|
+ coeff[i][9]*w + coeff[i][10]*w2s + coeff[i][11]*w3s + coeff[i][12]*w4s;
|
|
}
|
|
|
|
var minDiff = Infinity, percentile = 50;
|
|
for (var j = 0; j < 99; j++) {
|
|
var diff = Math.abs(bpValue - predicted[j]);
|
|
if (diff < minDiff) { minDiff = diff; percentile = j + 1; }
|
|
}
|
|
return { percentile: percentile, predicted: predicted };
|
|
}
|
|
|
|
export function classifyBPFromPercentiles(age, sysPctile, diaPctile, sys, dia) {
|
|
var sysClass, diaClass;
|
|
if (age >= 13) {
|
|
if (sys >= 140) sysClass = 'stage2';
|
|
else if (sys >= 130) sysClass = 'stage1';
|
|
else if (sys >= 120) sysClass = 'elevated';
|
|
else sysClass = 'normal';
|
|
|
|
if (dia >= 90) diaClass = 'stage2';
|
|
else if (dia >= 80) diaClass = 'stage1';
|
|
else diaClass = 'normal';
|
|
} else {
|
|
if (sysPctile >= 95 + 12 || sys >= 140) sysClass = 'stage2';
|
|
else if (sysPctile >= 95 || sys >= 130) sysClass = 'stage1';
|
|
else if (sysPctile >= 90 || sys >= 120) sysClass = 'elevated';
|
|
else sysClass = 'normal';
|
|
|
|
if (diaPctile >= 95 + 12 || dia >= 90) diaClass = 'stage2';
|
|
else if (diaPctile >= 95 || dia >= 80) diaClass = 'stage1';
|
|
else if (diaPctile >= 90) diaClass = 'elevated';
|
|
else diaClass = 'normal';
|
|
}
|
|
|
|
var levels = { normal: 0, elevated: 1, stage1: 2, stage2: 3 };
|
|
var overall = levels[sysClass] >= levels[diaClass] ? sysClass : diaClass;
|
|
return { sysClass: sysClass, diaClass: diaClass, classification: overall };
|
|
}
|
|
|
|
export function calculateBPAssessment(reference, age, sex, heightCm, sys, dia) {
|
|
var height = calcHeightPercentile(reference, age, sex, heightCm);
|
|
var systolic = computeBPPercentile(reference, age, sex, heightCm, 'sys', sys);
|
|
var diastolic = computeBPPercentile(reference, age, sex, heightCm, 'dia', dia);
|
|
var classification = classifyBPFromPercentiles(age, systolic.percentile, diastolic.percentile, sys, dia);
|
|
return { height: height, systolic: systolic, diastolic: diastolic, classification: classification };
|
|
}
|