805 lines
27 KiB
Go
805 lines
27 KiB
Go
// trainedPredictionModels contains trained prediction neural network models for predicting genetic traits
|
|
// These models are stored as .gob encoded files of []float32 weights
|
|
// This package also contains prediction accuracy information for each model
|
|
// Prediction accuracy models describe information about how accurate the predictions made by the models are
|
|
// All of the files in this package are created by the Create Genetic Models utility.
|
|
// This utility is located in /utilities/createGeneticModels/createGeneticModels.go
|
|
|
|
package trainedPredictionModels
|
|
|
|
import "seekia/internal/genetics/geneticPrediction"
|
|
import "seekia/internal/genetics/geneticPredictionModels"
|
|
import "seekia/internal/genetics/locusValue"
|
|
|
|
import _ "embed"
|
|
|
|
import "math"
|
|
import "bytes"
|
|
import "encoding/gob"
|
|
import "slices"
|
|
import "sync"
|
|
import "errors"
|
|
|
|
|
|
// These are the trained prediction model files:
|
|
|
|
//go:embed predictionModels/EyeColorModel.gob
|
|
var predictionModelFile_EyeColor []byte
|
|
|
|
//go:embed predictionModels/LactoseToleranceModel.gob
|
|
var predictionModelFile_LactoseTolerance []byte
|
|
|
|
//go:embed predictionModels/HeightModel.gob
|
|
var predictionModelFile_Height []byte
|
|
|
|
//go:embed predictionModels/AutismModel.gob
|
|
var predictionModelFile_Autism []byte
|
|
|
|
//go:embed predictionModels/HomosexualnessModel.gob
|
|
var predictionModelFile_Homosexualness []byte
|
|
|
|
//go:embed predictionModels/ObesityModel.gob
|
|
var predictionModelFile_Obesity []byte
|
|
|
|
// These are the trained prediction models
|
|
// Each model has a mutex so it will only be used to make 1 prediction at a time
|
|
|
|
var predictionModel_EyeColor *geneticPredictionModels.NeuralNetwork
|
|
var predictionModelMutex_EyeColor sync.Mutex
|
|
|
|
var predictionModel_LactoseTolerance *geneticPredictionModels.NeuralNetwork
|
|
var predictionModelMutex_LactoseTolerance sync.Mutex
|
|
|
|
var predictionModel_Height *geneticPredictionModels.NeuralNetwork
|
|
var predictionModelMutex_Height sync.Mutex
|
|
|
|
var predictionModel_Autism *geneticPredictionModels.NeuralNetwork
|
|
var predictionModelMutex_Autism sync.Mutex
|
|
|
|
var predictionModel_Homosexualness *geneticPredictionModels.NeuralNetwork
|
|
var predictionModelMutex_Homosexualness sync.Mutex
|
|
|
|
var predictionModel_Obesity *geneticPredictionModels.NeuralNetwork
|
|
var predictionModelMutex_Obesity sync.Mutex
|
|
|
|
|
|
// These are the discrete trait prediction model accuracy files:
|
|
|
|
//go:embed predictionModelAccuracies/EyeColorModelAccuracy.gob
|
|
var predictionAccuracyFile_EyeColor []byte
|
|
|
|
//go:embed predictionModelAccuracies/LactoseToleranceModelAccuracy.gob
|
|
var predictionAccuracyFile_LactoseTolerance []byte
|
|
|
|
|
|
// These are the discrete trait prediction model accuracy maps
|
|
|
|
var predictionAccuracyMap_EyeColor DiscreteTraitPredictionAccuracyInfoMap
|
|
var predictionAccuracyMap_LactoseTolerance DiscreteTraitPredictionAccuracyInfoMap
|
|
|
|
|
|
// These are the numeric attribute prediction model accuracy files:
|
|
|
|
//go:embed predictionModelAccuracies/HeightModelAccuracy.gob
|
|
var predictionAccuracyFile_Height []byte
|
|
|
|
//go:embed predictionModelAccuracies/AutismModelAccuracy.gob
|
|
var predictionAccuracyFile_Autism []byte
|
|
|
|
//go:embed predictionModelAccuracies/HomosexualnessModelAccuracy.gob
|
|
var predictionAccuracyFile_Homosexualness []byte
|
|
|
|
//go:embed predictionModelAccuracies/ObesityModelAccuracy.gob
|
|
var predictionAccuracyFile_Obesity []byte
|
|
|
|
// These are the numeric attribute prediction model accuracy maps
|
|
|
|
var predictionAccuracyMap_Height NumericAttributePredictionAccuracyInfoMap
|
|
var predictionAccuracyMap_Autism NumericAttributePredictionAccuracyInfoMap
|
|
var predictionAccuracyMap_Homosexualness NumericAttributePredictionAccuracyInfoMap
|
|
var predictionAccuracyMap_Obesity NumericAttributePredictionAccuracyInfoMap
|
|
|
|
|
|
// This function has to be called once upon application startup
|
|
// We must also call it before certain tests
|
|
func InitializeTrainedPredictionModels()error{
|
|
|
|
// We first initialize the neural networks
|
|
|
|
attributeNamesList := []string{"Eye Color", "Lactose Tolerance", "Height", "Autism", "Obesity", "Homosexualness"}
|
|
|
|
for _, attributeName := range attributeNamesList{
|
|
|
|
getPredictionModelFileBytes := func()([]byte, error){
|
|
|
|
switch attributeName{
|
|
|
|
case "Eye Color":{
|
|
return predictionModelFile_EyeColor, nil
|
|
}
|
|
case "Lactose Tolerance":{
|
|
return predictionModelFile_LactoseTolerance, nil
|
|
}
|
|
case "Height":{
|
|
return predictionModelFile_Height, nil
|
|
}
|
|
case "Autism":{
|
|
return predictionModelFile_Autism, nil
|
|
}
|
|
case "Obesity":{
|
|
return predictionModelFile_Obesity, nil
|
|
}
|
|
case "Homosexualness":{
|
|
return predictionModelFile_Homosexualness, nil
|
|
}
|
|
}
|
|
|
|
return nil, errors.New("Trying to initialize genetic prediction model with unknown attributeName: " + attributeName)
|
|
}
|
|
|
|
predictionModelFileBytes, err := getPredictionModelFileBytes()
|
|
if (err != nil) { return err }
|
|
|
|
neuralNetworkObject, err := geneticPredictionModels.DecodeBytesToNeuralNetworkObject(predictionModelFileBytes)
|
|
if (err != nil) { return err }
|
|
|
|
switch attributeName{
|
|
|
|
case "Eye Color":{
|
|
predictionModel_EyeColor = &neuralNetworkObject
|
|
continue
|
|
}
|
|
case "Lactose Tolerance":{
|
|
predictionModel_LactoseTolerance = &neuralNetworkObject
|
|
continue
|
|
}
|
|
case "Height":{
|
|
predictionModel_Height = &neuralNetworkObject
|
|
continue
|
|
}
|
|
case "Autism":{
|
|
predictionModel_Autism = &neuralNetworkObject
|
|
continue
|
|
}
|
|
case "Obesity":{
|
|
predictionModel_Obesity = &neuralNetworkObject
|
|
continue
|
|
}
|
|
case "Homosexualness":{
|
|
predictionModel_Homosexualness = &neuralNetworkObject
|
|
continue
|
|
}
|
|
}
|
|
|
|
return errors.New("Trying to initialize genetic prediction model with unknown attributeName: " + attributeName)
|
|
}
|
|
|
|
// Now we initialize the prediction accuracy information
|
|
// We start with discrete traits
|
|
|
|
discreteTraitNamesList := []string{"Eye Color", "Lactose Tolerance"}
|
|
|
|
for _, traitName := range discreteTraitNamesList{
|
|
|
|
getPredictionAccuracyFileBytes := func()([]byte, error){
|
|
|
|
switch traitName{
|
|
case "Eye Color":{
|
|
return predictionAccuracyFile_EyeColor, nil
|
|
}
|
|
case "Lactose Tolerance":{
|
|
return predictionAccuracyFile_LactoseTolerance, nil
|
|
}
|
|
}
|
|
|
|
return nil, errors.New("Prediction accuracy file not found for discrete trait: " + traitName)
|
|
}
|
|
|
|
predictionAccuracyFileBytes, err := getPredictionAccuracyFileBytes()
|
|
if (err != nil) { return err }
|
|
|
|
// We convert the gob encoded file to a map
|
|
|
|
discreteTraitPredictionAccuracyInfoMap, err := decodeBytesToDiscreteTraitPredictionAccuracyInfoMap(predictionAccuracyFileBytes)
|
|
if (err != nil) { return err }
|
|
|
|
// We initialize the global variables
|
|
|
|
switch traitName{
|
|
case "Eye Color":{
|
|
predictionAccuracyMap_EyeColor = discreteTraitPredictionAccuracyInfoMap
|
|
continue
|
|
}
|
|
case "Lactose Tolerance":{
|
|
predictionAccuracyMap_LactoseTolerance = discreteTraitPredictionAccuracyInfoMap
|
|
continue
|
|
}
|
|
}
|
|
|
|
return errors.New("Unknown discrete trait name: " + traitName)
|
|
}
|
|
|
|
// Now we process numeric attributes
|
|
|
|
numericAttributeNamesList := []string{"Height", "Autism", "Homosexualness", "Obesity"}
|
|
|
|
for _, traitName := range numericAttributeNamesList{
|
|
|
|
getPredictionAccuracyFileBytes := func()([]byte, error){
|
|
|
|
switch traitName{
|
|
case "Height":{
|
|
return predictionAccuracyFile_Height, nil
|
|
}
|
|
case "Autism":{
|
|
return predictionAccuracyFile_Autism, nil
|
|
}
|
|
case "Homosexualness":{
|
|
return predictionAccuracyFile_Homosexualness, nil
|
|
}
|
|
case "Obesity":{
|
|
return predictionAccuracyFile_Obesity, nil
|
|
}
|
|
}
|
|
|
|
return nil, errors.New("Prediction accuracy file not found for numeric trait: " + traitName)
|
|
}
|
|
|
|
predictionAccuracyFileBytes, err := getPredictionAccuracyFileBytes()
|
|
if (err != nil) { return err }
|
|
|
|
// We convert the gob encoded file to a map
|
|
|
|
numericTraitPredictionAccuracyInfoMap, err := decodeBytesToNumericAttributePredictionAccuracyInfoMap(predictionAccuracyFileBytes)
|
|
if (err != nil) { return err }
|
|
|
|
// We initialize the global variables
|
|
|
|
switch traitName{
|
|
case "Height":{
|
|
predictionAccuracyMap_Height = numericTraitPredictionAccuracyInfoMap
|
|
continue
|
|
}
|
|
case "Autism":{
|
|
predictionAccuracyMap_Autism = numericTraitPredictionAccuracyInfoMap
|
|
continue
|
|
}
|
|
case "Homosexualness":{
|
|
predictionAccuracyMap_Homosexualness = numericTraitPredictionAccuracyInfoMap
|
|
continue
|
|
}
|
|
case "Obesity":{
|
|
predictionAccuracyMap_Obesity = numericTraitPredictionAccuracyInfoMap
|
|
continue
|
|
}
|
|
}
|
|
|
|
return errors.New("Unknown numeric trait name: " + traitName)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// We use this to check if a neural network exists for an attribute
|
|
func CheckIfAttributeNeuralNetworkExists(attributeName string)bool{
|
|
|
|
switch attributeName{
|
|
|
|
case "Eye Color",
|
|
"Lactose Tolerance",
|
|
"Height",
|
|
"Autism",
|
|
"Obesity",
|
|
"Homosexualness":{
|
|
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
|
|
//Outputs:
|
|
// -bool: Neural network model exists for this trait (trait prediction is possible for this trait)
|
|
// -bool: Trait prediction is possible for this user (User has at least 1 known trait locus value)
|
|
// -string: Predicted trait outcome (Example: "Blue")
|
|
// -int: Confidence: Probability (0-100) that the prediction is accurate
|
|
// -int: Quantity of loci known
|
|
// -int: Quantity of phased loci
|
|
// -error
|
|
func GetNeuralNetworkDiscreteTraitPredictionFromGenomeMap(traitName string, traitRSIDsList []int64, genomeMap map[int64]locusValue.LocusValue)(bool, bool, string, int, int, int, error){
|
|
|
|
getPredictionModelObject := func()(bool, *geneticPredictionModels.NeuralNetwork){
|
|
|
|
switch traitName{
|
|
|
|
case "Eye Color":{
|
|
return true, predictionModel_EyeColor
|
|
}
|
|
case "Lactose Tolerance":{
|
|
return true, predictionModel_LactoseTolerance
|
|
}
|
|
}
|
|
|
|
return false, nil
|
|
}
|
|
|
|
predictionModelExists, predictionModelObject := getPredictionModelObject()
|
|
if (predictionModelExists == false){
|
|
// Neural network trait prediction is not possible for this trait
|
|
return false, false, "", 0, 0, 0, nil
|
|
}
|
|
|
|
if (predictionModelObject == nil){
|
|
return false, false, "", 0, 0, 0, errors.New("GetNeuralNetworkDiscreteTraitPredictionFromGenomeMap called when trained prediction models are not initialized.")
|
|
}
|
|
|
|
if (len(traitRSIDsList) == 0){
|
|
return false, false, "", 0, 0, 0, errors.New("GetNeuralNetworkDiscreteTraitPredictionFromGenomeMap called with empty traitRSIDsList for trait with a neural network.")
|
|
}
|
|
|
|
traitRSIDsListCopy := slices.Clone(traitRSIDsList)
|
|
slices.Sort(traitRSIDsListCopy)
|
|
|
|
neuralNetworkInput, quantityOfLociKnown, quantityOfPhasedLoci, err := geneticPrediction.CreateInputNeuralNetworkLayerFromGenomeMap(traitRSIDsListCopy, genomeMap)
|
|
if (err != nil) { return false, false, "", 0, 0, 0, err }
|
|
|
|
if (quantityOfLociKnown == 0){
|
|
// We can't predict anything about this trait for this genome
|
|
return true, false, "", 0, 0, 0, nil
|
|
}
|
|
|
|
getPredictionOutcome := func()(string, error){
|
|
|
|
// We lock the mutex for the prediction model
|
|
|
|
switch traitName{
|
|
|
|
case "Eye Color":{
|
|
predictionModelMutex_EyeColor.Lock()
|
|
defer predictionModelMutex_EyeColor.Unlock()
|
|
}
|
|
case "Lactose Tolerance":{
|
|
predictionModelMutex_LactoseTolerance.Lock()
|
|
defer predictionModelMutex_LactoseTolerance.Unlock()
|
|
}
|
|
default:{
|
|
return "", errors.New("traitName not found: " + traitName)
|
|
}
|
|
}
|
|
|
|
outputLayer, err := geneticPrediction.GetNeuralNetworkRawPrediction(predictionModelObject, false, neuralNetworkInput)
|
|
if (err != nil) { return "", err }
|
|
|
|
predictedOutcomeName, err := geneticPrediction.GetDiscreteOutcomeNameFromOutputLayer(traitName, false, outputLayer)
|
|
if (err != nil) { return "", err }
|
|
|
|
return predictedOutcomeName, nil
|
|
}
|
|
|
|
predictedOutcome, err := getPredictionOutcome()
|
|
if (err != nil) { return false, false, "", 0, 0, 0, err }
|
|
|
|
modelTraitAccuracyInfoMap, err := GetPredictionModelDiscreteTraitAccuracyInfoMap(traitName)
|
|
if (err != nil) { return false, false, "", 0, 0, 0, err }
|
|
|
|
// We find the model trait accuracy info object that is the most similar to our predicted outcome
|
|
|
|
getPredictionAccuracy := func()int{
|
|
|
|
totalNumberOfTraitLoci := len(traitRSIDsList)
|
|
|
|
proportionOfLociTested := float64(quantityOfLociKnown)/float64(totalNumberOfTraitLoci)
|
|
percentageOfLociTested := int(proportionOfLociTested * 100)
|
|
|
|
proportionOfPhasedLoci := float64(quantityOfPhasedLoci)/float64(totalNumberOfTraitLoci)
|
|
percentageOfPhasedLoci := int(proportionOfPhasedLoci * 100)
|
|
|
|
// This is a value between 0 and 100 that represents the most likely accuracy probability for this prediction
|
|
closestPredictionAccuracy := 0
|
|
|
|
// This is a value that represents the distance our closest prediction accuracy has from the current prediction
|
|
// Consider each prediction accuracy value on an (X,Y) coordinate plane
|
|
// X = Number of loci tested
|
|
// Y = Number of phased loci
|
|
closestPredictionAccuracyDistance := float64(0)
|
|
|
|
anyOutcomeAccuracyFound := false
|
|
|
|
for traitOutcomeInfo, traitPredictionAccuracyInfo := range modelTraitAccuracyInfoMap{
|
|
|
|
outcomeName := traitOutcomeInfo.OutcomeName
|
|
if (outcomeName != predictedOutcome){
|
|
continue
|
|
}
|
|
|
|
probabilityOfCorrectOutcomePrediction := traitPredictionAccuracyInfo.ProbabilityOfCorrectOutcomePrediction
|
|
|
|
currentPercentageOfLociTested := traitOutcomeInfo.PercentageOfLociTested
|
|
currentPercentageOfPhasedLoci := traitOutcomeInfo.PercentageOfPhasedLoci
|
|
|
|
// Distance Formula for 2 coordinates (x1, y1) and (x2, y2):
|
|
// distance = √((x2 - x1)^2 + (y2 - y1)^2)
|
|
|
|
differenceInX := float64(currentPercentageOfLociTested - percentageOfLociTested)
|
|
differenceInY := float64(currentPercentageOfPhasedLoci - percentageOfPhasedLoci)
|
|
|
|
distance := math.Sqrt(math.Pow(differenceInX, 2) + math.Pow(differenceInY, 2))
|
|
|
|
if (distance == 0){
|
|
// We found the exact prediction accuracy
|
|
return probabilityOfCorrectOutcomePrediction
|
|
}
|
|
|
|
if (anyOutcomeAccuracyFound == false){
|
|
closestPredictionAccuracyDistance = distance
|
|
closestPredictionAccuracy = probabilityOfCorrectOutcomePrediction
|
|
anyOutcomeAccuracyFound = true
|
|
continue
|
|
} else {
|
|
if (distance < closestPredictionAccuracyDistance){
|
|
closestPredictionAccuracyDistance = distance
|
|
closestPredictionAccuracy = probabilityOfCorrectOutcomePrediction
|
|
}
|
|
}
|
|
}
|
|
|
|
if (anyOutcomeAccuracyFound == false){
|
|
// This means that our model has never actually predicted this outcome
|
|
// This shouldn't happen unless our model is really bad, or our training set has very few people with this outcome.
|
|
// We return a 0% accuracy rating
|
|
return 0
|
|
}
|
|
|
|
return closestPredictionAccuracy
|
|
}
|
|
|
|
predictionAccuracy := getPredictionAccuracy()
|
|
|
|
return true, true, predictedOutcome, predictionAccuracy, quantityOfLociKnown, quantityOfPhasedLoci, nil
|
|
}
|
|
|
|
|
|
// This function is used to predict numeric traits and polygenic disease risk scores
|
|
//Outputs:
|
|
// -bool: Neural network model exists for this attribute (neural network prediction is possible for this attribute)
|
|
// -bool: Attribute prediction is possible for this user (User has at least 1 known attribute locus value)
|
|
// -float64: Predicted attribute outcome (Example: Height in centimeters)
|
|
// -map[int]float64: Accuracy ranges map
|
|
// -Map Structure: Probability prediction is accurate (X) -> Distance from prediction that must be travelled in both directions to
|
|
// create a range in which the true value will fall into, X% of the time
|
|
// -int: Quantity of loci known
|
|
// -int: Quantity of phased loci
|
|
// -error
|
|
func GetNeuralNetworkNumericAttributePredictionFromGenomeMap(attributeName string, attributeLociList []int64, genomeMap map[int64]locusValue.LocusValue)(bool, bool, float64, map[int]float64, int, int, error){
|
|
|
|
getPredictionModelObject := func()(bool, *geneticPredictionModels.NeuralNetwork){
|
|
|
|
switch attributeName{
|
|
|
|
case "Height":{
|
|
return true, predictionModel_Height
|
|
}
|
|
case "Autism":{
|
|
return true, predictionModel_Autism
|
|
}
|
|
case "Obesity":{
|
|
return true, predictionModel_Obesity
|
|
}
|
|
case "Homosexualness":{
|
|
return true, predictionModel_Homosexualness
|
|
}
|
|
}
|
|
|
|
return false, nil
|
|
}
|
|
|
|
predictionModelExists, predictionModelObject := getPredictionModelObject()
|
|
if (predictionModelExists == false){
|
|
// Neural network trait prediction is not possible for this trait
|
|
return false, false, 0, nil, 0, 0, nil
|
|
}
|
|
|
|
if (predictionModelObject == nil){
|
|
return false, false, 0, nil, 0, 0, errors.New("GetNeuralNetworkNumericAttributePredictionFromGenomeMap called when trained prediction models are not initialized.")
|
|
}
|
|
|
|
if (len(attributeLociList) == 0){
|
|
return false, false, 0, nil, 0, 0, errors.New("GetNeuralNetworkNumericAttributePredictionFromGenomeMap called with empty attributeLociList for an attribute with a neural network.")
|
|
}
|
|
|
|
attributeLociListCopy := slices.Clone(attributeLociList)
|
|
slices.Sort(attributeLociListCopy)
|
|
|
|
neuralNetworkInput, quantityOfLociKnown, quantityOfPhasedLoci, err := geneticPrediction.CreateInputNeuralNetworkLayerFromGenomeMap(attributeLociListCopy, genomeMap)
|
|
if (err != nil) { return false, false, 0, nil, 0, 0, err }
|
|
|
|
if (quantityOfLociKnown == 0){
|
|
// We can't predict anything about this attribute for this genome
|
|
return true, false, 0, nil, 0, 0, nil
|
|
}
|
|
|
|
getPredictionOutcome := func()(float64, error){
|
|
|
|
// We lock the mutex for the prediction model
|
|
|
|
switch attributeName{
|
|
|
|
case "Height":{
|
|
predictionModelMutex_Height.Lock()
|
|
defer predictionModelMutex_Height.Unlock()
|
|
}
|
|
case "Autism":{
|
|
predictionModelMutex_Autism.Lock()
|
|
defer predictionModelMutex_Autism.Unlock()
|
|
}
|
|
case "Obesity":{
|
|
predictionModelMutex_Obesity.Lock()
|
|
defer predictionModelMutex_Obesity.Unlock()
|
|
}
|
|
case "Homosexualness":{
|
|
predictionModelMutex_Homosexualness.Lock()
|
|
defer predictionModelMutex_Homosexualness.Unlock()
|
|
}
|
|
default:{
|
|
return 0, errors.New("attributeName not found: " + attributeName)
|
|
}
|
|
}
|
|
|
|
outputLayer, err := geneticPrediction.GetNeuralNetworkRawPrediction(predictionModelObject, true, neuralNetworkInput)
|
|
if (err != nil) { return 0, err }
|
|
|
|
predictedOutcomeValue, err := geneticPrediction.GetNumericOutcomeValueFromOutputLayer(attributeName, outputLayer)
|
|
if (err != nil) { return 0, err }
|
|
|
|
return predictedOutcomeValue, nil
|
|
}
|
|
|
|
predictedOutcome, err := getPredictionOutcome()
|
|
if (err != nil) { return false, false, 0, nil, 0, 0, err }
|
|
|
|
modelAccuracyInfoMap, err := GetPredictionModelNumericAttributeAccuracyInfoMap(attributeName)
|
|
if (err != nil) { return false, false, 0, nil, 0, 0, err }
|
|
|
|
// We create a prediction confidence ranges map for our prediction
|
|
|
|
getPredictionConfidenceRangesMap := func()map[int]float64{
|
|
|
|
totalNumberOfAttributeLoci := len(attributeLociListCopy)
|
|
|
|
proportionOfLociTested := float64(quantityOfLociKnown)/float64(totalNumberOfAttributeLoci)
|
|
percentageOfLociTested := int(proportionOfLociTested * 100)
|
|
|
|
proportionOfPhasedLoci := float64(quantityOfPhasedLoci)/float64(totalNumberOfAttributeLoci)
|
|
percentageOfPhasedLoci := int(proportionOfPhasedLoci * 100)
|
|
|
|
// This is a value between 0 and 100 that represents the most similar confidence ranges map for this prediction
|
|
var closestPredictionConfidenceRangesMap map[int]float64
|
|
|
|
// This is a value that represents the distance our closest prediction confidence ranges map has from the current prediction
|
|
// Consider each prediction accuracy value on an (X,Y) coordinate plane
|
|
// X = Number of loci tested
|
|
// Y = Number of phased loci
|
|
closestPredictionConfidenceRangesMapDistance := float64(0)
|
|
|
|
for attributeOutcomeInfo, attributePredictionConfidenceRangesMap := range modelAccuracyInfoMap{
|
|
|
|
currentPercentageOfLociTested := attributeOutcomeInfo.PercentageOfLociTested
|
|
currentPercentageOfPhasedLoci := attributeOutcomeInfo.PercentageOfPhasedLoci
|
|
|
|
// Distance Formula for 2 coordinates (x1, y1) and (x2, y2):
|
|
// distance = √((x2 - x1)^2 + (y2 - y1)^2)
|
|
|
|
differenceInX := float64(currentPercentageOfLociTested - percentageOfLociTested)
|
|
differenceInY := float64(currentPercentageOfPhasedLoci - percentageOfPhasedLoci)
|
|
|
|
distance := math.Sqrt(math.Pow(differenceInX, 2) + math.Pow(differenceInY, 2))
|
|
|
|
if (distance == 0){
|
|
// We found the exact prediction confidence ranges map
|
|
return attributePredictionConfidenceRangesMap
|
|
}
|
|
|
|
if (closestPredictionConfidenceRangesMap == nil || distance < closestPredictionConfidenceRangesMapDistance){
|
|
closestPredictionConfidenceRangesMapDistance = distance
|
|
closestPredictionConfidenceRangesMap = attributePredictionConfidenceRangesMap
|
|
}
|
|
}
|
|
|
|
return closestPredictionConfidenceRangesMap
|
|
}
|
|
|
|
predictionConfidenceRangesMap := getPredictionConfidenceRangesMap()
|
|
|
|
return true, true, predictedOutcome, predictionConfidenceRangesMap, quantityOfLociKnown, quantityOfPhasedLoci, nil
|
|
}
|
|
|
|
|
|
// This map is used to store information about how accurate genetic prediction models are for discrete traits
|
|
// Map Structure: Discrete Trait Outcome Info -> Discrete Trait Prediction Accuracy Info
|
|
type DiscreteTraitPredictionAccuracyInfoMap map[DiscreteTraitOutcomeInfo]DiscreteTraitPredictionAccuracyInfo
|
|
|
|
type DiscreteTraitOutcomeInfo struct{
|
|
|
|
// This is the outcome which was predicted
|
|
// Example: "Blue"
|
|
OutcomeName string
|
|
|
|
// This is a value between 0-100 which describes the percentage of the loci which were tested for the input for the prediction
|
|
PercentageOfLociTested int
|
|
|
|
// This is a value between 0-100 which describes the percentage of the tested loci which were phased for the input for the prediction
|
|
PercentageOfPhasedLoci int
|
|
}
|
|
|
|
type DiscreteTraitPredictionAccuracyInfo struct{
|
|
|
|
// This contains the quantity of examples for the outcome with the specified percentageOfLociTested and percentageOfPhasedLoci
|
|
QuantityOfExamples int
|
|
|
|
// This contains the quantity of predictions for the outcome with the specified percentageOfLociTested and percentageOfPhasedLoci
|
|
// Prediction = our model predicted this outcome
|
|
QuantityOfPredictions int
|
|
|
|
// This stores the probability (0-100) that our model will accurately predict this outcome for a genome which has
|
|
// the specified percentageOfLociTested and percentageOfPhasedLoci
|
|
// In other words: What is the probability that if you give Seekia a blue-eyed genome, it will give you a correct Blue prediction?
|
|
// This value is only accurate is QuantityOfExamples > 0
|
|
ProbabilityOfCorrectGenomePrediction int
|
|
|
|
// This stores the probability (0-100) that our model is correct if our model predicts that a genome
|
|
// with the specified percentageOfLociTested and percentageOfPhasedLoci has this outcome
|
|
// In other words: What is the probability that if Seekia says a genome will have blue eyes, it is correct?
|
|
// This value is only accurate is QuantityOfPredictions > 0
|
|
ProbabilityOfCorrectOutcomePrediction int
|
|
}
|
|
|
|
func EncodeDiscreteTraitPredictionAccuracyInfoMapToBytes(inputMap DiscreteTraitPredictionAccuracyInfoMap)([]byte, error){
|
|
|
|
buffer := new(bytes.Buffer)
|
|
|
|
encoder := gob.NewEncoder(buffer)
|
|
|
|
err := encoder.Encode(inputMap)
|
|
if (err != nil) { return nil, err }
|
|
|
|
inputMapBytes := buffer.Bytes()
|
|
|
|
return inputMapBytes, nil
|
|
}
|
|
|
|
func decodeBytesToDiscreteTraitPredictionAccuracyInfoMap(inputBytes []byte)(DiscreteTraitPredictionAccuracyInfoMap, error){
|
|
|
|
if (inputBytes == nil){
|
|
return nil, errors.New("DecodeBytesToDiscreteTraitPredictionAccuracyInfoMap called with nil inputBytes.")
|
|
}
|
|
|
|
buffer := bytes.NewBuffer(inputBytes)
|
|
|
|
decoder := gob.NewDecoder(buffer)
|
|
|
|
var newDiscreteTraitPredictionAccuracyInfoMap DiscreteTraitPredictionAccuracyInfoMap
|
|
|
|
err := decoder.Decode(&newDiscreteTraitPredictionAccuracyInfoMap)
|
|
if (err != nil){ return nil, err }
|
|
|
|
return newDiscreteTraitPredictionAccuracyInfoMap, nil
|
|
}
|
|
|
|
type NumericAttributePredictionAccuracyInfoMap map[NumericAttributePredictionInfo]NumericAttributePredictionAccuracyRangesMap
|
|
|
|
type NumericAttributePredictionInfo struct{
|
|
|
|
// This is a value between 0-100 which describes the percentage of the loci which were tested for the input for the prediction
|
|
PercentageOfLociTested int
|
|
|
|
// This is a value between 0-100 which describes the percentage of the tested loci which were phased for the input for the prediction
|
|
PercentageOfPhasedLoci int
|
|
}
|
|
|
|
// Map Structure: Accuracy Percentage (AP) -> Amount needed to deviate from prediction for the value to be accurate (AP)% of the time
|
|
// For example, if the model predicted that someone was 150 centimeters tall, how many centimeters would we have to deviate in both directions
|
|
// in order for the true outcome to fall into the range 10% of the time, 20% of the time, 30% of the time, etc...
|
|
// Example:
|
|
// -90%+: 50 centimeters
|
|
// If you travel 50 centimeters in both directions from the prediction,
|
|
// the true height value will fall into this range 90% of the time.
|
|
// -50%+: 20 centimeters
|
|
// -10%+: 10 centimeters
|
|
type NumericAttributePredictionAccuracyRangesMap map[int]float64
|
|
|
|
|
|
func EncodeNumericAttributePredictionAccuracyInfoMapToBytes(inputMap NumericAttributePredictionAccuracyInfoMap)([]byte, error){
|
|
|
|
buffer := new(bytes.Buffer)
|
|
|
|
encoder := gob.NewEncoder(buffer)
|
|
|
|
err := encoder.Encode(inputMap)
|
|
if (err != nil) { return nil, err }
|
|
|
|
inputMapBytes := buffer.Bytes()
|
|
|
|
return inputMapBytes, nil
|
|
}
|
|
|
|
func decodeBytesToNumericAttributePredictionAccuracyInfoMap(inputBytes []byte)(NumericAttributePredictionAccuracyInfoMap, error){
|
|
|
|
if (inputBytes == nil){
|
|
return nil, errors.New("DecodeBytesToNumericAttributePredictionAccuracyInfoMap called with nil inputBytes.")
|
|
}
|
|
|
|
buffer := bytes.NewBuffer(inputBytes)
|
|
|
|
decoder := gob.NewDecoder(buffer)
|
|
|
|
var newNumericAttributePredictionAccuracyInfoMap NumericAttributePredictionAccuracyInfoMap
|
|
|
|
err := decoder.Decode(&newNumericAttributePredictionAccuracyInfoMap)
|
|
if (err != nil){ return nil, err }
|
|
|
|
return newNumericAttributePredictionAccuracyInfoMap, nil
|
|
}
|
|
|
|
func GetPredictionModelDiscreteTraitAccuracyInfoMap(traitName string)(DiscreteTraitPredictionAccuracyInfoMap, error){
|
|
|
|
getAccuracyInfoMap := func()(DiscreteTraitPredictionAccuracyInfoMap, error){
|
|
|
|
switch traitName{
|
|
case "Eye Color":{
|
|
return predictionAccuracyMap_EyeColor, nil
|
|
}
|
|
case "Lactose Tolerance":{
|
|
return predictionAccuracyMap_LactoseTolerance, nil
|
|
}
|
|
}
|
|
|
|
return nil, errors.New("GetPredictionModelDiscreteTraitAccuracyInfoMap called with unknown traitName: " + traitName)
|
|
}
|
|
|
|
accuracyInfoMap, err := getAccuracyInfoMap()
|
|
if (err != nil) { return nil, err }
|
|
|
|
if (accuracyInfoMap == nil){
|
|
return nil, errors.New("GetPredictionModelDiscreteTraitAccuracyInfoMap called when map is not initialized.")
|
|
}
|
|
|
|
return accuracyInfoMap, nil
|
|
}
|
|
|
|
|
|
// The files returned by this function are .gob encoded geneticPrediction.NumericAttributePredictionAccuracyInfoMap objects
|
|
func GetPredictionModelNumericAttributeAccuracyInfoMap(attributeName string)(NumericAttributePredictionAccuracyInfoMap, error){
|
|
|
|
getAccuracyInfoMap := func()(NumericAttributePredictionAccuracyInfoMap, error){
|
|
|
|
switch attributeName{
|
|
case "Height":{
|
|
return predictionAccuracyMap_Height, nil
|
|
}
|
|
case "Autism":{
|
|
return predictionAccuracyMap_Autism, nil
|
|
}
|
|
case "Homosexualness":{
|
|
return predictionAccuracyMap_Homosexualness, nil
|
|
}
|
|
case "Obesity":{
|
|
return predictionAccuracyMap_Obesity, nil
|
|
}
|
|
}
|
|
|
|
return nil, errors.New("GetPredictionModelNumericAttributeAccuracyInfoMap called with unknown attributeName: " + attributeName)
|
|
}
|
|
|
|
accuracyInfoMap, err := getAccuracyInfoMap()
|
|
if (err != nil) { return nil, err }
|
|
|
|
if (accuracyInfoMap == nil){
|
|
return nil, errors.New("GetPredictionModelNumericAttributeAccuracyInfoMap called when map is not initialized.")
|
|
}
|
|
|
|
return accuracyInfoMap, nil
|
|
}
|
|
|
|
|