0.2.0 GST

This commit is contained in:
rob tillaart 2022-06-08 14:39:43 +02:00
parent 3732af1abb
commit dc45c57956
8 changed files with 588 additions and 85 deletions

226
libraries/GST/GST.cpp Normal file
View File

@ -0,0 +1,226 @@
//
// FILE: GST.cpp
// VERSION: 0.1.1
// PURPOSE: Arduino library for Gold Standard Test metrics
// URL: https://github.com/RobTillaart/GST
// https://en.wikipedia.org/wiki/Sensitivity_and_specificity
// https://en.wikipedia.org/wiki/Confusion_matrix
//
// formula's based upon Wikipedia.
#include "GST.h"
GST::GST()
{
clearAll();
};
///////////////////////////////////////////////////////
//
// INPUT FUNCTIONS
//
void GST::setTruePositive(float v)
{
TP = v;
AP = TP + FN;
};
void GST::setTrueNegative(float v)
{
TN = v;
AN = TN + FP;
};
void GST::setFalsePositive(float v)
{
FP = v;
AN = TN + FP;
};
void GST::setFalseNegative(float v)
{
FN = v;
AP = TP + FN;
};
void GST::clearAll()
{
AP = 0;
AN = 0;
TP = 0;
TN = 0;
FP = 0;
FN = 0;
}
// These are used for updating the test matrix
float GST::addTruePositive(float v)
{
TP += v;
AP = TP + FN;
return TP;
};
float GST::addTrueNegative(float v)
{
TN += v;
AN = TN + FP;
return TN;
};
float GST::addFalsePositive(float v)
{
FP += v;
AN = TN + FP;
return FP;
};
float GST::addFalseNegative(float v)
{
FN += v;
AP = TP + FN;
return FN;
};
///////////////////////////////////////////////////////
//
// OUTPUT FUNCTIONS I
//
float GST::getTruePositive() { return TP; };
float GST::getTrueNegative() { return TN; };
float GST::getFalsePositive() { return FP; };
float GST::getFalseNegative() { return FN; };
float GST::getTotal() { return AP + AN; };
float GST::getActualPositive() { return AP; };
float GST::getActualNegative() { return AN; };
float GST::getTestedPositive() { return TP + FP; };
float GST::getTestedNegative() { return TN + FN; };
float GST::sensitivity() { return TPR(); };
float GST::specificity() { return TNR(); };
float GST::truePositiveRate() { return TPR(); };
float GST::TPR() { return TP / AP; };
float GST::trueNegativeRate() { return TNR(); };
float GST::TNR() { return TN / AN; };
float GST::falseNegativeRate() { return FNR(); };
float GST::FNR() { return FN / AP; };
float GST::falsePositiveRate() { return FPR(); };
float GST::FPR() { return FP / AN; };
///////////////////////////////////////////////////////
//
// OUTPUT FUNCTIONS II
//
float GST::positivePredictiveValue() { return PPV(); };
float GST::PPV() { return TP / (TP + FP); };
float GST::negativePredictiveValue() { return NPV(); };
float GST::NPV() { return TN / (TN + FN); };
float GST::falseDiscoveryRate() { return FDR(); };
float GST::FDR() { return FP / (TP + FP); };
float GST::falseOmissionRate() { return FOR(); };
float GST::FOR() { return FN / (TN + FN); };
float GST::positiveLikelihoodRatio() { return LRplus(); };
float GST::LRplus() { return TPR() / FPR(); };
float GST::negativeLikelihoodRatio() { return LRminus(); };
float GST::LRminus() { return FNR() / TNR(); };
float GST::prevalenceThreshold()
{
return sqrt(FPR()) / (sqrt(TPR()) + sqrt(FPR()));
};
float GST::threatScore()
{
return TP / (TP + FN + FP);
};
float GST::criticalSuccessIndex()
{
return threatScore();
};
float GST::prevalence()
{
return AP / (AP + AN);
};
float GST::accuracy()
{
return (TP + TN) / (AP + AN);
};
float GST::balancedAccuracy()
{
return (TPR() + TNR()) * 0.5;
};
float GST::F1Score()
{
return (2 * TP) / (2 * TP + FP + FN);
};
float GST::MatthewsCorrelationCoefficient() { return MCC(); };
float GST::phi() { return MCC(); };
float GST::MCC()
{
return (TP*TN - FP*FN)/sqrt((TP+FP) * (TP+FN) * (TN+FP) * (TN+FN));
};
float GST::FowlkesMallowsIndex() { return FM(); };
float GST::FM()
{
return sqrt(PPV()*TPR());
};
float GST::BookmakerInformedness() { return BM(); };
float GST::BM()
{
return TPR() + TNR() - 1;
};
float GST::markedness() { return MK(); };
float GST::deltaP() { return MK(); };
float GST::MK()
{
return PPV() + NPV() - 1;
};
float GST::diagnosticOddsRatio() { return DOR(); };
float GST::DOR()
{
return LRplus() / LRminus();
};
// -- END OF FILE --

View File

@ -1,110 +1,116 @@
#pragma once #pragma once
// //
// FILE: GST.h // FILE: GST.h
// VERSION: 0.1.0 // VERSION: 0.1.1
// PURPOSE: Arduino library for Gold Standard Test metrics // PURPOSE: Arduino library for Gold Standard Test metrics
// URL: https://github.com/RobTillaart/GST // URL: https://github.com/RobTillaart/GST
// https://en.wikipedia.org/wiki/Sensitivity_and_specificity // https://en.wikipedia.org/wiki/Sensitivity_and_specificity
// https://en.wikipedia.org/wiki/Confusion_matrix // https://en.wikipedia.org/wiki/Confusion_matrix
// //
// formula's based upon wikipedia. // formula's based upon Wikipedia.
#define GST_LIB_VERSION (F("0.1.1"))
#define GST_LIB_VERSION (F("0.1.0")) #include "Arduino.h"
class GST class GST
{ {
public: public:
GST() {}; GST();
// These 4 need to be filled in. // These four values of the matrix need to be set to get started.
void setTruePositive(float v) { TP = v; P = TP + FN; }; void setTruePositive(float v = 0);
void setTrueNegative(float v) { TN = v; N = TN + FP; }; void setTrueNegative(float v = 0);
void setFalsePositive(float v) { FP = v; N = TN + FP; }; void setFalsePositive(float v = 0);
void setFalseNegative(float v) { FN = v; P = TP + FN; }; void setFalseNegative(float v = 0);
void clearAll();
float getTruePositive() { return TP; }; // These are used for updating the test matrix
float getTrueNegative() { return TN; }; float addTruePositive(float v);
float getFalsePositive() { return FP; }; float addTrueNegative(float v);
float getFalseNegative() { return FN; }; float addFalsePositive(float v);
float addFalseNegative(float v);
float getTotal() { return P + N; };
float getActualPositive() { return P; };
float getActualNegative() { return N; };
float getTestedPositive() { return TP + FP; };
float getTestedNegative() { return TN + FN; };
float sensitivity() { return TPR(); };
float specificity() { return TNR(); };
// Output functions I
float getTruePositive();
float getTrueNegative();
float getFalsePositive();
float getFalseNegative();
// true positive rate float getTotal();
float TPR() { return TP / P; }; float getActualPositive();
// true negative rate float getActualNegative();
float TNR() { return TN / N; }; float getTestedPositive();
float getTestedNegative();
// false negative rate float sensitivity();
float FNR() { return FN / (FN + TP); }; float specificity();
// false positive rate
float FPR() { return FP / (FP + TN); };
float truePositiveRate();
// positive predictive value float TPR();
float PPV() { return TP / (TP + FP); }; float trueNegativeRate();
// negative predictive value float TNR();
float NPV() { return TN / (TN + FN); }; float falseNegativeRate();
float FNR();
// false discovery rate float falsePositiveRate();
float FDR() { return FP / (FP + TP); }; float FPR();
// false omission rate
float FOR() { return FN / (FN + TN); };
// Output functions II
// positive likelihood ratio float positivePredictiveValue();
float LRplus() { return TPR() / FPR(); }; float PPV();
// negative likelihood ratio float negativePredictiveValue();
float LRminus() { return FNR() / TNR(); }; float NPV();
float falseDiscoveryRate();
float FDR();
float falseOmissionRate();
float FOR();
float positiveLikelihoodRatio();
float prevalenceThreshold() { return sqrt(FPR()) / (sqrt(TPR()) + sqrt(FPR())); }; float LRplus();
float threatScore() { return TP / (TP + FN + FP); }; float negativeLikelihoodRatio();
float criticalSuccessIndex() { return threatScore(); }; float LRminus();
float prevalenceThreshold();
float prevalence() { return P / (P + N); }; float threatScore();
float accuracy() { return (TP + TN) / (P + N); }; float criticalSuccessIndex();
float balancedAccuracy() { return (TPR() + TNR()) / 2; };
float F1Score() { return (2 * TP)/(2 * TP + FP + FN); };
float prevalence();
float accuracy();
float balancedAccuracy();
float F1Score();
// Matthews correlation coefficient
float MCC() { return (TP*TN-FP*FN)/sqrt((TP+FP)*(TP+FN)*(TN+FP)*(TN+FN)); }; float MatthewsCorrelationCoefficient();
float phi() { return MCC(); }; float phi();
// FowlkesMallows index float MCC();
float FM() { return sqrt(PPV()*TPR()); }; float FowlkesMallowsIndex();
// Bookmaker informedness float FM();
float BM() { return TPR() + TNR() - 1; }; float BookmakerInformedness();
// markedness float BM();
float MK() { return PPV() + NPV() - 1; };
float deltaP() { return MK(); };
// diagnostic odds ratio float markedness();
float DOR() { return LRplus() / LRminus(); }; float deltaP();
float MK();
float diagnosticOddsRatio();
float DOR();
private: private:
float P = 0; float AP; // actual positive
float N = 0; float AN; // actual negative
float TP = 0; float TP; // true positive
float TN = 0; float TN; // true negative
float FP = 0; float FP; // false positive
float FN = 0; float FN; // false positive
}; };

View File

@ -13,30 +13,140 @@ Arduino library for Gold Standard Test metrics.
## Description ## Description
The GST library is **experimental**. Note: **experimental**
The GST library is an implementation of the **Gold Standard Test**.
#### Links #### Links
These sites describe the functions in more detail.
- https://en.wikipedia.org/wiki/Sensitivity_and_specificity - https://en.wikipedia.org/wiki/Sensitivity_and_specificity
- https://en.wikipedia.org/wiki/Confusion_matrix - https://en.wikipedia.org/wiki/Confusion_matrix
#### Performance
The math functions are from pretty straightforward to rather complex.
It is possible to optimize functions with intermediate values if needed.
However the right way to optimize depends on the way the library is used.
#### Related libraries
- https://github.com/RobTillaart/Statistic
## Interface ## Interface
See .h file See .h file for all functions. Many function exist in a long descriptive name and an acronym version. Here only the long names are given.
For the definitions please check - https://en.wikipedia.org/wiki/Sensitivity_and_specificity or
https://en.wikipedia.org/wiki/Confusion_matrix
### Input functions
These four numbers should all be set before output functions make sense.
The parameter **value** is typical absolute value measured or counted.
If the parameter is omitted, the default 0 will be used to reset the value.
- **void setTruePositive(float value = 0)** set the internal TP value.
- **void setTrueNegative(float value = 0)** set the internal TN value.
- **void setFalsePositive(float value = 0)** set the internal FP value.
- **void setFalseNegative(float value = 0)** set the internal FN value.
- **void clearAll()** reset all the above to 0.
In tests one often want to increase / change the numbers.
This can be done with the **addTruePositive()** etc functions.
After every addition all output functions can be called.
- **float addTruePositive(float value)** increases the internal TP value.
Use a negative value to decrease.
Returns the new value of TP.
- **float addTrueNegative(float value)** increases the internal TN value.
Use a negative value to decrease.
Returns the new value of TN.
- **float addFalsePositive(float value)** increases the internal FP value.
Use a negative value to decrease.
Returns the new value of FP.
- **float addFalseNegative(float value)** increases the internal FN value.
Use a negative value to decrease.
Returns the new value of FN.
### Output functions I
Basic output
- **float getTruePositive()** returns internal TP.
- **float getTrueNegative()** returns internal TN.
- **float getFalsePositive()** returns internal FP.
- **float getFalseNegative()** returns internal FN.
- **float getTotal()** returns total of four numbers.
- **float getActualPositive()**
- **float getActualNegative()**
- **float getTestedPositive()**
- **float getTestedNegative()**
- **float sensitivity()** equals truePositiveRate().
- **float specificity()** equals trueNegativeRate()
- **float truePositiveRate()**
- **float trueNegativeRate()**
- **float falseNegativeRate()**
- **float falsePositiveRate()**
### Output functions II
These are the more 'complex' functions.
Read the Wikipedia pages for their uses.
- **float positivePredictiveValue()**
- **float negativePredictiveValue()**
- **float falseDiscoveryRate()**
- **float falseOmissionRate()**
- **float positiveLikelihoodRatio()**
- **float negativeLikelihoodRatio()**
- **float prevalenceThreshold()**
- **float threatScore()**
- **float criticalSuccessIndex()**
- **float prevalence()**
- **float accuracy()**
- **float balancedAccuracy()**
- **float F1Score()**
- **float MatthewsCorrelationCoefficient()**
- **float FowlkesMallowsIndex()**
- **float BookmakerInformedness()**
- **float markedness()**
- **float deltaP()**
- **float diagnosticOddsRatio()**
## Future ## Future
- documentation - improve documentation
- improve - add functions
- more links? - percentage functions for TP TN FP and FN?
- test - test
- complete the CI test coverage. - complete the CI test coverage.
- examples - examples
- add real life examples. - add real life examples.
- combination with a sensor? batch testing? - combination with a sensor? batch testing?
- code
- full name functions instead of acronyms. (wrap?)
- is GST a good class name?

View File

@ -2,7 +2,18 @@
# GST Changelog # GST Changelog
## 0.1.1 2022-06-08
- add **addTruePositive()** etc functions.
- add defaults for **setTruePositive(value = 0)** etc functions
- add long descriptive names for the short functions.
- add derived class GoldenStandardTest, for descriptive name
- added some documentation
- split off GST.cpp file, prevent - https://github.com/RobTillaart/CRC/issues/21
## 0.1.0 2022-02-25 ## 0.1.0 2022-02-25
- initial version - initial version
- -

View File

@ -0,0 +1,121 @@
// FILE: GST_add_runtime.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo
// URL: https://github.com/RobTillaart/GST
#include "Arduino.h"
#include "GST.h"
GST gst;
void setup()
{
Serial.begin(115200);
while (!Serial);
Serial.println(__FILE__);
gst.setTruePositive(0);
gst.setTrueNegative(0);
gst.setFalsePositive(0);
gst.setFalseNegative(0);
}
void loop()
{
// simulate a test result
delay(500);
int score = random(4);
switch (score)
{
case 0:
gst.addTruePositive(1);
break;
case 1:
gst.addTrueNegative(1);
break;
case 2:
gst.addFalsePositive(1);
break;
case 3:
gst.addFalseNegative(1);
break;
}
confusion_matrix();
// confusion_matrix_normalized();
}
void confusion_matrix()
{
Serial.println();
Serial.println(__FUNCTION__);
Serial.println();
// PRINTED IN A MATRIX
Serial.print("\t");
Serial.print(gst.getTotal());
Serial.print("\t");
Serial.print(gst.getTestedPositive());
Serial.print("\t");
Serial.println(gst.getTestedNegative());
Serial.print("\t");
Serial.print(gst.getActualPositive());
Serial.print("\t");
Serial.print(gst.getTruePositive());
Serial.print("\t");
Serial.println(gst.getFalseNegative());
Serial.print("\t");
Serial.print(gst.getActualNegative());
Serial.print("\t");
Serial.print(gst.getFalsePositive());
Serial.print("\t");
Serial.println(gst.getTrueNegative());
Serial.println();
Serial.print("\tSensitivity:\t");
Serial.println(gst.sensitivity(), 4);
Serial.print("\tSpecificity:\t");
Serial.println(gst.specificity(), 4);
}
void confusion_matrix_normalized()
{
Serial.println();
Serial.println(__FUNCTION__);
Serial.println();
// PRINTED IN A MATRIX
Serial.print("\t");
Serial.print("100.00%");
Serial.print("\t");
Serial.print(gst.getTestedPositive());
Serial.print("\t");
Serial.println(gst.getTestedNegative());
Serial.print("\t");
Serial.print(gst.getActualPositive());
Serial.print("\t");
Serial.print(gst.TPR(), 4);
Serial.print("\t");
Serial.println(gst.FNR(), 4);
Serial.print("\t");
Serial.print(gst.getActualNegative());
Serial.print("\t");
Serial.print(gst.FPR(), 4);
Serial.print("\t");
Serial.println(gst.TNR(), 4);
}
// -- END OF FILE --

View File

@ -9,6 +9,12 @@ setTruePositive KEYWORD2
setTrueNegative KEYWORD2 setTrueNegative KEYWORD2
setFalsePositive KEYWORD2 setFalsePositive KEYWORD2
setFalseNegative KEYWORD2 setFalseNegative KEYWORD2
clearAll KEYWORD2
addTruePositive KEYWORD2
addTrueNegative KEYWORD2
addFalsePositive KEYWORD2
addFalseNegative KEYWORD2
getTruePositive KEYWORD2 getTruePositive KEYWORD2
getTrueNegative KEYWORD2 getTrueNegative KEYWORD2
@ -60,6 +66,29 @@ deltaP KEYWORD2
DOR KEYWORD2 DOR KEYWORD2
truePositiveRate KEYWORD2
trueNegativeRate KEYWORD2
falseNegativeRate KEYWORD2
falsePositiveRate KEYWORD2
positivePredictiveValue KEYWORD2
negativePredictiveValue KEYWORD2
falseDiscoveryRate KEYWORD2
falseOmissionRate KEYWORD2
positiveLikelihoodRatio KEYWORD2
negativeLikelihoodRatio KEYWORD2
MatthewsCorrelationCoefficient KEYWORD2
FowlkesMallowsIndex KEYWORD2
BookmakerInformedness KEYWORD2
markedness KEYWORD2
diagnosticOddsRatio KEYWORD2
# Constants (LITERAL1) # Constants (LITERAL1)
GST_LIB_VERSION LITERAL1 GST_LIB_VERSION LITERAL1

View File

@ -15,7 +15,7 @@
"type": "git", "type": "git",
"url": "https://github.com/RobTillaart/GST.git" "url": "https://github.com/RobTillaart/GST.git"
}, },
"version": "0.1.0", "version": "0.1.1",
"license": "MIT", "license": "MIT",
"frameworks": "arduino", "frameworks": "arduino",
"platforms": "*", "platforms": "*",

View File

@ -1,5 +1,5 @@
name=GST name=GST
version=0.1.0 version=0.1.1
author=Rob Tillaart <rob.tillaart@gmail.com> author=Rob Tillaart <rob.tillaart@gmail.com>
maintainer=Rob Tillaart <rob.tillaart@gmail.com> maintainer=Rob Tillaart <rob.tillaart@gmail.com>
sentence=Arduino library for Golden Standard Test, confusion matrix. sentence=Arduino library for Golden Standard Test, confusion matrix.