@decidables/detectable-math
Version:
detectable-math: Equations for calculating Signal Detection Theory
255 lines (204 loc) • 5.98 kB
JavaScript
import jStat from 'jstat';
/*
SDTMath Static Class - Not intended for instantiation!
Model parameters:
d = sensitivity (d' for equal variance, d_a for unequal variance)
c = response bias (c for equal variance, c_a for unequal variance)
s = standard deviation of signal distribution, with standard deviation of noise distribution = 1
Variables:
H = hits
M = misses
FA = false alarms
CR = correct rejections
HR = hit rate
zHR = Z-transformed hit rate
FAR = false alarm rate
zFAR = Z-transformed false alarm rate
ACC = accuracy
PPV = positive predictive value
FOMR = false omission rate (used FOMR to avoid keyword FOR!)
d = sensitivity (d' for equal variance, d_a for unequal variance)
c = response bias (c for equal variance, c_a for unequal variance)
s = standard deviation of signal distribution, with standard deviation of noise distribution = 1
muN = mean of noise distribution
muS = mean of signal distribution
l = lambda, threshold location, with l = 0 indicating no response bias
h = height of signal distribution
Functions:
Z = z-score
Z^-1 = inverse z-score
Equations (* = unequal variance):
HR = H / (H + M)
FAR = FA / (FA + CR)
ACC = (H + CR) / (H + M + FA + CR)
ACC = (HR + (1 - FAR)) / 2
PPV = H / (H + FA)
FOMR = M / (M + CR)
d' = Z^-1(HR) - Z^-1(FAR)
*d' = (2 / (s^2 + 1))^(1/2) * (s * Z^-1(HR) - Z^-1(FAR))
c = -(Z^-1(HR) + Z^-1(FAR))/2
*c = (2 / (s^2 + 1))^(1/2) * (s / s + 1) * -(Z^-1(HR) + Z^-1(FAR))
HR = Z(d'/2 - c)
*HR = Z(((s^2 + 1) / 2)^(1/2) * (d' / (s + 1) - c / s))
FAR = Z(-d'/2 - c)
*FAR = Z(((s^2 + 1) / 2)^(1/2) * -(d' / (s + 1) + c))
HR = Z(d' + Z^-1(FAR))
*HR = Z(((s^2 + 1) / 2)^(1/2) * d' + Z^-1(FAR) / s)
HR = Z(-2c - Z^-1(FAR))
*HR = Z(-((s^2 + 1) / 2)^(1/2) * ((s + 1) / s) * c - Z^-1(FAR))
muN = -d'/2
*muN = -((s^2 + 1) / 2)^(1/2) * (1 / (s + 1)) * d'
d' = -2 * muN
*d' = -(2 / (s^2 + 1))^(1/2) * (s + 1) * muN
muS = d'/2
*muS = ((s^2 + 1) / 2)^(1/2) * (s / (s + 1)) * d'
d' = 2 * muS
*d' = (2 / (s^2 + 1))^(1/2) * ((s + 1) / s) * muS
l = c
*l = ((s^2 + 1) / 2)^(1/2) * c
c = l
*c = (2 / (s^2 + 1))^(1/2) * l
h = 1 / (s * (2 * pi)^(1/2))
s = 1 / (h * (2 * pi)^(1/2))
zHR = Z(HR)
zFAR = Z(FAR)
HR = Z^-1(zHR)
FAR = Z^-1(zFAR)
*/
export default class SDTMath {
static d = {
DEFAULT: 1.0,
MIN: -6.0,
MAX: 6.0,
STEP: 0.01,
JUMP: 0.1,
};
static c = {
DEFAULT: 0,
MIN: -3.0,
MAX: 3.0,
STEP: 0.01,
JUMP: 0.1,
};
static s = {
DEFAULT: 1.0,
MIN: 0.8,
MAX: 40.0,
STEP: 0.01,
JUMP: 0.05,
};
static hM2Hr(h, m) {
if ((h === 0) && (m === 0)) {
return 0;
}
return h / (h + m);
}
static faCr2Far(fa, cr) {
if ((fa === 0) && (cr === 0)) {
return 0;
}
return fa / (fa + cr);
}
static hMFaCr2Acc(h, m, fa, cr) {
if ((h === 0) && (m === 0) && (fa === 0) && (cr === 0)) {
return 0;
}
return (h + cr) / (h + m + fa + cr);
}
static hrFar2Acc(hr, far) {
return (hr + (1 - far)) / 2;
}
static hFa2Ppv(h, fa) {
if ((h === 0) && (fa === 0)) {
return 0;
}
return h / (h + fa);
}
static mCr2Fomr(m, cr) {
if ((m === 0) && (cr === 0)) {
return 0;
}
return m / (m + cr);
}
static hrFar2D(hr, far, s = 1) {
if (s === 1) return (jStat.normal.inv(hr, 0, 1) - jStat.normal.inv(far, 0, 1));
return (
Math.sqrt(2 / (s * s + 1))
* (s * jStat.normal.inv(hr, 0, 1) - jStat.normal.inv(far, 0, 1))
);
}
static hrFar2C(hr, far, s = 1) {
if (s === 1) return (-(jStat.normal.inv(hr, 0, 1) + jStat.normal.inv(far, 0, 1)) / 2);
return (
Math.sqrt(2 / (s * s + 1))
* (s / (s + 1))
* -(jStat.normal.inv(hr, 0, 1) + jStat.normal.inv(far, 0, 1))
);
}
static dC2Hr(d, c, s = 1) {
if (s === 1) return (jStat.normal.cdf(d / 2 - c, 0, 1));
return (jStat.normal.cdf(Math.sqrt((s * s + 1) / 2) * (d / (1 + s) - c / s), 0, 1));
}
static dC2Far(d, c, s = 1) {
if (s === 1) return (jStat.normal.cdf(-(d / 2 + c), 0, 1));
return (jStat.normal.cdf(Math.sqrt((s * s + 1) / 2) * -(d / (1 + s) + c), 0, 1));
}
static dFar2Hr(d, far, s = 1) {
if (s === 1) return (jStat.normal.cdf(d + jStat.normal.inv(far, 0, 1), 0, 1));
return (jStat.normal.cdf(
(Math.sqrt((s * s + 1) / 2) * d + jStat.normal.inv(far, 0, 1)) / s,
0,
1,
));
}
static cFar2Hr(c, far, s = 1) {
if (s === 1) return (jStat.normal.cdf(-(2 * c) - jStat.normal.inv(far, 0, 1), 0, 1));
return (jStat.normal.cdf(
-Math.sqrt((s * s + 1) / 2) * ((s + 1) / s) * c - jStat.normal.inv(far, 0, 1),
0,
1,
));
}
static d2MuN(d, s = 1) {
if (s === 1) return -d / 2;
return -Math.sqrt((s * s + 1) / 2) * (1 / (s + 1)) * d;
}
static muN2D(muN, s = 1) {
if (s === 1) return -2 * muN;
return -Math.sqrt(2 / (s * s + 1)) * (s + 1) * muN;
}
static d2MuS(d, s = 1) {
if (s === 1) return d / 2;
return Math.sqrt((s * s + 1) / 2) * (s / (s + 1)) * d;
}
static muS2D(muS, s = 1) {
if (s === 1) return 2 * muS;
return Math.sqrt(2 / (s * s + 1)) * ((s + 1) / s) * muS;
}
static c2L(c, s = 1) {
if (s === 1) return c;
return Math.sqrt((s * s + 1) / 2) * c;
}
static l2C(l, s = 1) {
if (s === 1) return l;
return Math.sqrt(2 / (s * s + 1)) * l;
}
static s2H(s = 1) {
return 1 / (s * Math.sqrt(2 * Math.PI));
}
static h2S(h) {
return 1 / (h * Math.sqrt(2 * Math.PI));
}
static hr2Zhr(hr) {
return jStat.normal.inv(hr, 0, 1);
}
static far2Zfar(far) {
return jStat.normal.inv(far, 0, 1);
}
static zhr2Hr(zhr) {
return jStat.normal.cdf(zhr, 0, 1);
}
static zfar2Far(zfar) {
return jStat.normal.cdf(zfar, 0, 1);
}
}