Added the Height trait to the Create Genetic Models utility.
This commit is contained in:
parent
d538afc7a2
commit
fe754cb6a2
21 changed files with 861 additions and 199 deletions
|
@ -6,6 +6,7 @@ Small and insignificant changes may not be included in this log.
|
|||
|
||||
## Unversioned Changes
|
||||
|
||||
* Added the Height trait to the Create Genetic Models utility. - *Simon Sarasova*
|
||||
* Added LocusIsPhased information to the local user profile creation process. - *Simon Sarasova*
|
||||
* Added the Height trait the traits package. Migrated locus metadata from json encoding to gob encoding. - *Simon Sarasova*
|
||||
* Upgraded Fyne to version 2.5.0. - *Simon Sarasova*
|
||||
|
|
|
@ -9,4 +9,4 @@ Many other people have written code for modules which are imported by Seekia. Th
|
|||
|
||||
Name | Date Of First Commit | Number Of Commits
|
||||
--- | --- | ---
|
||||
Simon Sarasova | June 13, 2023 | 270
|
||||
Simon Sarasova | June 13, 2023 | 271
|
|
@ -26,19 +26,19 @@ func ApplyCartoonEffect(inputImage image.Image, effectStrength int)(image.Image,
|
|||
}
|
||||
|
||||
|
||||
blurKernelSize, err := helpers.ScaleNumberProportionally(true, effectStrength, 0, 100, 1, 3)
|
||||
blurKernelSize, err := helpers.ScaleIntProportionally(true, effectStrength, 0, 100, 1, 3)
|
||||
if (err != nil) { return nil, err }
|
||||
if (blurKernelSize % 2 == 0){
|
||||
blurKernelSize += 1
|
||||
}
|
||||
|
||||
edgeThreshold, err := helpers.ScaleNumberProportionally(false, effectStrength, 0, 100, 5, 200)
|
||||
edgeThreshold, err := helpers.ScaleIntProportionally(false, effectStrength, 0, 100, 5, 200)
|
||||
if (err != nil) { return nil, err }
|
||||
|
||||
oilFilterSize, err := helpers.ScaleNumberProportionally(true, effectStrength, 0, 100, 5, 20)
|
||||
oilFilterSize, err := helpers.ScaleIntProportionally(true, effectStrength, 0, 100, 5, 20)
|
||||
if (err != nil) { return nil, err }
|
||||
|
||||
oilLevels, err := helpers.ScaleNumberProportionally(true, effectStrength, 0, 100, 1, 3)
|
||||
oilLevels, err := helpers.ScaleIntProportionally(true, effectStrength, 0, 100, 1, 3)
|
||||
if (err != nil) { return nil, err }
|
||||
|
||||
options := CTOpts{
|
||||
|
@ -78,7 +78,7 @@ func ApplyPencilEffect(inputImage image.Image, effectStrength int)(image.Image,
|
|||
goeffectsImageObject, err := convertGolangImageObjectToGoeffectsImageObject(inputImage)
|
||||
if (err != nil) { return nil, err }
|
||||
|
||||
blurAmount, err := helpers.ScaleNumberProportionally(true, effectStrength, 0, 100, 1, 20)
|
||||
blurAmount, err := helpers.ScaleIntProportionally(true, effectStrength, 0, 100, 1, 20)
|
||||
if (err != nil) { return nil, err }
|
||||
|
||||
if (blurAmount % 2 == 0) {
|
||||
|
@ -119,7 +119,7 @@ func ApplyWireframeEffect(inputImage image.Image, effectStrength int, lightMode
|
|||
grayscaleGoeffectsImage, err := grayscaleEffectObject.Apply(&goeffectsImageObject, 5)
|
||||
if (err != nil) { return nil, err }
|
||||
|
||||
threshold, err := helpers.ScaleNumberProportionally(false, effectStrength, 0, 100, 10, 100)
|
||||
threshold, err := helpers.ScaleIntProportionally(false, effectStrength, 0, 100, 10, 100)
|
||||
if (err != nil) { return nil, err }
|
||||
|
||||
sobelEffectObject := NewSobel(threshold, lightMode)
|
||||
|
@ -146,10 +146,10 @@ func ApplyOilPaintingEffect(inputImage image.Image, effectStrength int)(image.Im
|
|||
return inputImage, nil
|
||||
}
|
||||
|
||||
filterSize, err := helpers.ScaleNumberProportionally(true, effectStrength, 0, 100, 10, 30)
|
||||
filterSize, err := helpers.ScaleIntProportionally(true, effectStrength, 0, 100, 10, 30)
|
||||
if (err != nil) { return nil, err }
|
||||
|
||||
levels, err := helpers.ScaleNumberProportionally(true, effectStrength, 0, 100, 10, 70)
|
||||
levels, err := helpers.ScaleIntProportionally(true, effectStrength, 0, 100, 10, 70)
|
||||
if (err != nil) { return nil, err }
|
||||
|
||||
oilPaintingEffectObject := NewOilPainting(filterSize, levels)
|
||||
|
|
|
@ -40,7 +40,7 @@ func CreateCoupleGeneticAnalysis(person1GenomesList []prepareRawGenomes.RawGenom
|
|||
|
||||
person1PrepareRawGenomesUpdatePercentageCompleteFunction := func(newPercentage int)error{
|
||||
|
||||
newPercentageCompletion, err := helpers.ScaleNumberProportionally(true, newPercentage, 0, 100, 0, 25)
|
||||
newPercentageCompletion, err := helpers.ScaleIntProportionally(true, newPercentage, 0, 100, 0, 25)
|
||||
if (err != nil){ return err }
|
||||
|
||||
err = updatePercentageCompleteFunction(newPercentageCompletion)
|
||||
|
@ -49,8 +49,18 @@ func CreateCoupleGeneticAnalysis(person1GenomesList []prepareRawGenomes.RawGenom
|
|||
return nil
|
||||
}
|
||||
|
||||
person1GenomesWithMetadataList, allPerson1RawGenomeIdentifiersList, person1HasMultipleGenomes, person1OnlyExcludeConflictsGenomeIdentifier, person1OnlyIncludeSharedGenomeIdentifier, err := prepareRawGenomes.GetGenomesWithMetadataListFromRawGenomesList(person1GenomesList, person1PrepareRawGenomesUpdatePercentageCompleteFunction)
|
||||
anyUsefulLocationsExist, person1GenomesWithMetadataList, allPerson1RawGenomeIdentifiersList, person1HasMultipleGenomes, person1OnlyExcludeConflictsGenomeIdentifier, person1OnlyIncludeSharedGenomeIdentifier, err := prepareRawGenomes.GetGenomesWithMetadataListFromRawGenomesList(person1GenomesList, person1PrepareRawGenomesUpdatePercentageCompleteFunction)
|
||||
if (err != nil) { return false, "", err }
|
||||
if (anyUsefulLocationsExist == false){
|
||||
// We should have checked for this when genomes were first imported.
|
||||
return false, "", errors.New("CreateCoupleGeneticAnalysis called with person1GenomesList that does not contain any useful genomes")
|
||||
}
|
||||
if (len(person1GenomesList) > 1 && (len(person1GenomesList) != (len(person1GenomesWithMetadataList)-2)) ){
|
||||
// If there is more than 1 genome, 2 combined genomes are created
|
||||
// We are checking to make sure that none of the input genomes were dropped due to not having any locations
|
||||
// We should have checked to make sure each input genome has useful locations when each genome was first imported.
|
||||
return false, "", errors.New("CreateCoupleGeneticAnalysis called with person1GenomesList containing at least 1 genome without useful locations.")
|
||||
}
|
||||
|
||||
processIsStopped := checkIfProcessIsStopped()
|
||||
if (processIsStopped == true){
|
||||
|
@ -59,7 +69,7 @@ func CreateCoupleGeneticAnalysis(person1GenomesList []prepareRawGenomes.RawGenom
|
|||
|
||||
person2PrepareRawGenomesUpdatePercentageCompleteFunction := func(newPercentage int)error{
|
||||
|
||||
newPercentageCompletion, err := helpers.ScaleNumberProportionally(true, newPercentage, 0, 100, 25, 50)
|
||||
newPercentageCompletion, err := helpers.ScaleIntProportionally(true, newPercentage, 0, 100, 25, 50)
|
||||
if (err != nil){ return err }
|
||||
|
||||
err = updatePercentageCompleteFunction(newPercentageCompletion)
|
||||
|
@ -68,8 +78,18 @@ func CreateCoupleGeneticAnalysis(person1GenomesList []prepareRawGenomes.RawGenom
|
|||
return nil
|
||||
}
|
||||
|
||||
person2GenomesWithMetadataList, allPerson2RawGenomeIdentifiersList, person2HasMultipleGenomes, person2OnlyExcludeConflictsGenomeIdentifier, person2OnlyIncludeSharedGenomeIdentifier, err := prepareRawGenomes.GetGenomesWithMetadataListFromRawGenomesList(person2GenomesList, person2PrepareRawGenomesUpdatePercentageCompleteFunction)
|
||||
anyUsefulLocationsExist, person2GenomesWithMetadataList, allPerson2RawGenomeIdentifiersList, person2HasMultipleGenomes, person2OnlyExcludeConflictsGenomeIdentifier, person2OnlyIncludeSharedGenomeIdentifier, err := prepareRawGenomes.GetGenomesWithMetadataListFromRawGenomesList(person2GenomesList, person2PrepareRawGenomesUpdatePercentageCompleteFunction)
|
||||
if (err != nil) { return false, "", err }
|
||||
if (anyUsefulLocationsExist == false){
|
||||
// We should have checked for this when genomes were first imported.
|
||||
return false, "", errors.New("CreateCoupleGeneticAnalysis called with person2GenomesList that does not contain any useful genomes")
|
||||
}
|
||||
if (len(person2GenomesList) > 1 && (len(person2GenomesList) != (len(person2GenomesWithMetadataList)-2)) ){
|
||||
// If there is more than 1 genome, 2 combined genomes are created
|
||||
// We are checking to make sure that none of the input genomes were dropped due to not having any locations
|
||||
// We should have checked to make sure each input genome has useful locations when each genome was first imported.
|
||||
return false, "", errors.New("CreateCoupleGeneticAnalysis called with person2GenomesList containing at least 1 genome without useful locations.")
|
||||
}
|
||||
|
||||
processIsStopped = checkIfProcessIsStopped()
|
||||
if (processIsStopped == true){
|
||||
|
@ -936,7 +956,7 @@ func GetOffspringPolygenicDiseaseInfo_Fast(diseaseLociList []polygenicDiseases.D
|
|||
offspringMaximumPossibleRiskWeightSum += locusMaximumWeight
|
||||
}
|
||||
|
||||
offspringAverageDiseaseRiskScore, err := helpers.ScaleNumberProportionally(true, offspringSummedRiskWeights, offspringMinimumPossibleRiskWeightSum, offspringMaximumPossibleRiskWeightSum, 0, 10)
|
||||
offspringAverageDiseaseRiskScore, err := helpers.ScaleIntProportionally(true, offspringSummedRiskWeights, offspringMinimumPossibleRiskWeightSum, offspringMaximumPossibleRiskWeightSum, 0, 10)
|
||||
if (err != nil) { return false, 0, 0, err }
|
||||
|
||||
if (numberOfLociTested == 0){
|
||||
|
@ -1061,7 +1081,7 @@ func GetOffspringPolygenicDiseaseInfo(diseaseLociList []polygenicDiseases.Diseas
|
|||
offspringMaximumPossibleRiskWeightSum += locusMaximumWeight
|
||||
}
|
||||
|
||||
offspringAverageDiseaseRiskScore, err := helpers.ScaleNumberProportionally(true, offspringSummedRiskWeights, offspringMinimumPossibleRiskWeightSum, offspringMaximumPossibleRiskWeightSum, 0, 10)
|
||||
offspringAverageDiseaseRiskScore, err := helpers.ScaleIntProportionally(true, offspringSummedRiskWeights, offspringMinimumPossibleRiskWeightSum, offspringMaximumPossibleRiskWeightSum, 0, 10)
|
||||
if (err != nil) { return false, 0, 0, nil, nil, err }
|
||||
|
||||
sampleOffspringRiskScoresList = append(sampleOffspringRiskScoresList, offspringAverageDiseaseRiskScore)
|
||||
|
|
|
@ -40,7 +40,7 @@ func CreatePersonGeneticAnalysis(genomesList []prepareRawGenomes.RawGenomeWithMe
|
|||
|
||||
prepareRawGenomesUpdatePercentageCompleteFunction := func(newPercentage int)error{
|
||||
|
||||
newPercentageCompletion, err := helpers.ScaleNumberProportionally(true, newPercentage, 0, 100, 0, 50)
|
||||
newPercentageCompletion, err := helpers.ScaleIntProportionally(true, newPercentage, 0, 100, 0, 50)
|
||||
if (err != nil){ return err }
|
||||
|
||||
err = updatePercentageCompleteFunction(newPercentageCompletion)
|
||||
|
@ -49,8 +49,18 @@ func CreatePersonGeneticAnalysis(genomesList []prepareRawGenomes.RawGenomeWithMe
|
|||
return nil
|
||||
}
|
||||
|
||||
genomesWithMetadataList, allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, err := prepareRawGenomes.GetGenomesWithMetadataListFromRawGenomesList(genomesList, prepareRawGenomesUpdatePercentageCompleteFunction)
|
||||
anyUsefulLocationsExist, genomesWithMetadataList, allRawGenomeIdentifiersList, multipleGenomesExist, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, err := prepareRawGenomes.GetGenomesWithMetadataListFromRawGenomesList(genomesList, prepareRawGenomesUpdatePercentageCompleteFunction)
|
||||
if (err != nil) { return false, "", err }
|
||||
if (anyUsefulLocationsExist == false){
|
||||
// We should have checked for this when genomes were first imported.
|
||||
return false, "", errors.New("CreatePersonGeneticAnalysis called with genomeList containing no genomes with useful locations.")
|
||||
}
|
||||
if (len(genomesList) > 1 && (len(genomesList) != (len(genomesWithMetadataList)-2)) ){
|
||||
// If there is more than 1 genome, 2 combined genomes are created
|
||||
// We are checking to make sure that none of the input genomes were dropped due to not having any locations
|
||||
// We should have checked to make sure each input genome has useful locations when each genome was first imported.
|
||||
return false, "", errors.New("CreatePersonGeneticAnalysis called with genomeList containing at least 1 genome without useful locations.")
|
||||
}
|
||||
|
||||
// This map stores each genome's locus values
|
||||
// Map Structure: Genome Identifier -> Genome locus values map (rsID -> Locus Value)
|
||||
|
@ -716,7 +726,7 @@ func GetPersonGenomePolygenicDiseaseInfo(diseaseLociList []polygenicDiseases.Dis
|
|||
return false, 0, 0, nil, nil
|
||||
}
|
||||
|
||||
diseaseRiskScore, err := helpers.ScaleNumberProportionally(true, summedDiseaseRiskWeight, minimumPossibleRiskWeightSum, maximumPossibleRiskWeightSum, 0, 10)
|
||||
diseaseRiskScore, err := helpers.ScaleIntProportionally(true, summedDiseaseRiskWeight, minimumPossibleRiskWeightSum, maximumPossibleRiskWeightSum, 0, 10)
|
||||
if (err != nil) { return false, 0, 0, nil, err }
|
||||
|
||||
return true, diseaseRiskScore, numberOfLociTested, genomeLociInfoMap, nil
|
||||
|
|
|
@ -218,7 +218,7 @@ type DiscreteTraitPredictionAccuracyInfoMap map[DiscreteTraitOutcomeInfo]Discret
|
|||
|
||||
type DiscreteTraitOutcomeInfo struct{
|
||||
|
||||
// This is the outcome which was found
|
||||
// This is the outcome which was predicted
|
||||
// Example: "Blue"
|
||||
OutcomeName string
|
||||
|
||||
|
@ -283,6 +283,65 @@ func DecodeBytesToDiscreteTraitPredictionAccuracyInfoMap(inputBytes []byte)(Disc
|
|||
return newDiscreteTraitPredictionAccuracyInfoMap, nil
|
||||
}
|
||||
|
||||
type NumericTraitPredictionAccuracyInfoMap map[NumericTraitOutcomeInfo]NumericTraitPredictionAccuracyRangesMap
|
||||
|
||||
type NumericTraitOutcomeInfo struct{
|
||||
|
||||
// This is the outcome which was predicted
|
||||
// Example: 150 centimeters
|
||||
OutcomeValue float64
|
||||
|
||||
// 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 NumericTraitPredictionAccuracyRangesMap map[int]float64
|
||||
|
||||
|
||||
func EncodeNumericTraitPredictionAccuracyInfoMapToBytes(inputMap NumericTraitPredictionAccuracyInfoMap)([]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 DecodeBytesToNumericTraitPredictionAccuracyInfoMap(inputBytes []byte)(NumericTraitPredictionAccuracyInfoMap, error){
|
||||
|
||||
if (inputBytes == nil){
|
||||
return nil, errors.New("DecodeBytesToNumericTraitPredictionAccuracyInfoMap called with nil inputBytes.")
|
||||
}
|
||||
|
||||
buffer := bytes.NewBuffer(inputBytes)
|
||||
|
||||
decoder := gob.NewDecoder(buffer)
|
||||
|
||||
var newNumericTraitPredictionAccuracyInfoMap NumericTraitPredictionAccuracyInfoMap
|
||||
|
||||
err := decoder.Decode(&newNumericTraitPredictionAccuracyInfoMap)
|
||||
if (err != nil){ return nil, err }
|
||||
|
||||
return newNumericTraitPredictionAccuracyInfoMap, nil
|
||||
}
|
||||
|
||||
//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)
|
||||
|
@ -378,7 +437,7 @@ func GetNeuralNetworkDiscreteTraitPredictionFromGenomeMap(traitName string, geno
|
|||
outputLayer, err := GetNeuralNetworkRawPrediction(&neuralNetworkObject, false, neuralNetworkInput)
|
||||
if (err != nil) { return false, false, "", 0, 0, 0, err }
|
||||
|
||||
predictedOutcomeName, err := GetOutcomeNameFromOutputLayer(traitName, false, outputLayer)
|
||||
predictedOutcomeName, err := GetDiscreteOutcomeNameFromOutputLayer(traitName, false, outputLayer)
|
||||
if (err != nil) { return false, false, "", 0, 0, 0, err }
|
||||
|
||||
modelTraitAccuracyInfoFile, err := geneticPredictionModels.GetPredictionModelDiscreteTraitAccuracyInfoBytes(traitName)
|
||||
|
@ -521,7 +580,7 @@ func GetLociInfoFromNetworkInputLayer(inputLayer []float32)(int, int, int, error
|
|||
// Outputs:
|
||||
// -string: Output Name (Example: "Blue")
|
||||
// -error
|
||||
func GetOutcomeNameFromOutputLayer(traitName string, verifyOutputLayer bool, outputLayer []float32)(string, error){
|
||||
func GetDiscreteOutcomeNameFromOutputLayer(traitName string, verifyOutputLayer bool, outputLayer []float32)(string, error){
|
||||
|
||||
if (verifyOutputLayer == true){
|
||||
|
||||
|
@ -534,7 +593,7 @@ func GetOutcomeNameFromOutputLayer(traitName string, verifyOutputLayer bool, out
|
|||
}
|
||||
|
||||
// We allow a small amount of inaccuracy due to the imprecise nature of floats.
|
||||
if (summedNeurons > 1.1 || summedNeurons < .99){
|
||||
if (summedNeurons > 1.01 || summedNeurons < .99){
|
||||
summedNeuronsString := helpers.ConvertFloat32ToString(summedNeurons)
|
||||
return "", errors.New("GetOutcomeNameFromOutputLayer called with layer containing neuron values which don't sum to 1: " + summedNeuronsString)
|
||||
}
|
||||
|
@ -607,6 +666,45 @@ func GetOutcomeNameFromOutputLayer(traitName string, verifyOutputLayer bool, out
|
|||
}
|
||||
|
||||
|
||||
// This function returns which outcome is being described from a neural network's final output layer
|
||||
// This is only used for discrete traits
|
||||
// Outputs:
|
||||
// -float64: Output Value (example: 150 centimeters)
|
||||
// -error
|
||||
func GetNumericOutcomeValueFromOutputLayer(traitName string, outputLayer []float32)(float64, error){
|
||||
|
||||
if (len(outputLayer) != 1){
|
||||
return 0, errors.New("GetNumericOutcomeValueFromOutputLayer called with output layer which is not length of 1")
|
||||
}
|
||||
|
||||
outputNeuron := outputLayer[0]
|
||||
|
||||
if (outputNeuron < 0 || outputNeuron > 1){
|
||||
return 0, errors.New("GetNumericOutcomeValueFromOutputLayer called with output layer contains out-of-bounds neuron")
|
||||
}
|
||||
|
||||
getOutcomeMinAndMax := func()(float64, float64, error){
|
||||
|
||||
switch traitName{
|
||||
case "Height":{
|
||||
// Shortest person of all time: 54 cm
|
||||
// Tallest person of all time: 272 cm
|
||||
return 54, 272, nil
|
||||
}
|
||||
}
|
||||
|
||||
return 0, 0, errors.New("GetNumericOutcomeValueFromOutputLayer called with unknown traitName: " + traitName)
|
||||
}
|
||||
|
||||
outcomeMin, outcomeMax, err := getOutcomeMinAndMax()
|
||||
if (err != nil) { return 0, err }
|
||||
|
||||
outcomeValue, err := helpers.ScaleFloat64Proportionally(true, float64(outputNeuron), 0, 1, outcomeMin, outcomeMax)
|
||||
if (err != nil) { return 0, err }
|
||||
|
||||
return outcomeValue, nil
|
||||
}
|
||||
|
||||
//Outputs:
|
||||
// -int: Layer 1 neuron count (input layer)
|
||||
// -int: Layer 2 neuron count
|
||||
|
@ -631,6 +729,11 @@ func getNeuralNetworkLayerSizes(traitName string)(int, int, int, int, error){
|
|||
// There are 2 output neurons, each representing a tolerance: Tolerant, Intolerant
|
||||
return 6, 4, 3, 2, nil
|
||||
}
|
||||
case "Height":{
|
||||
// There are 3000 input neurons
|
||||
// There is 1 output neuron, representing a height value
|
||||
return 3000, 2, 2, 1, nil
|
||||
}
|
||||
}
|
||||
|
||||
return 0, 0, 0, 0, errors.New("getNeuralNetworkLayerSizes called with unknown traitName: " + traitName)
|
||||
|
@ -682,7 +785,7 @@ func CreateGeneticPredictionTrainingData_OpenSNP(
|
|||
userPhenotypeDataObject readBiobankData.PhenotypeData_OpenSNP,
|
||||
userLocusValuesMap map[int64]locusValue.LocusValue)(bool, []TrainingData, error){
|
||||
|
||||
if (traitName != "Eye Color" && traitName != "Lactose Tolerance"){
|
||||
if (traitName != "Eye Color" && traitName != "Lactose Tolerance" && traitName != "Height"){
|
||||
return false, nil, errors.New("CreateGeneticPredictionTrainingData_OpenSNP called with unknown traitName: " + traitName)
|
||||
}
|
||||
|
||||
|
@ -800,6 +903,27 @@ func CreateGeneticPredictionTrainingData_OpenSNP(
|
|||
|
||||
return true, []float32{0, 1}, nil
|
||||
}
|
||||
case "Height":{
|
||||
|
||||
userHeightIsKnown := userPhenotypeDataObject.HeightIsKnown
|
||||
if (userHeightIsKnown == false){
|
||||
return false, nil, nil
|
||||
}
|
||||
|
||||
userHeight := userPhenotypeDataObject.Height
|
||||
|
||||
// Shortest person of all time: 54 cm
|
||||
// Tallest person of all time: 272 cm
|
||||
|
||||
outputValue, err := helpers.ScaleFloat64Proportionally(true, userHeight, 54, 272, 0, 1)
|
||||
if (err != nil) { return false, nil, err }
|
||||
|
||||
outputValueFloat32 := float32(outputValue)
|
||||
|
||||
outputLayer := []float32{outputValueFloat32}
|
||||
|
||||
return true, outputLayer, nil
|
||||
}
|
||||
}
|
||||
|
||||
return false, nil, errors.New("Unknown traitName: " + traitName)
|
||||
|
@ -911,55 +1035,49 @@ func CreateGeneticPredictionTrainingData_OpenSNP(
|
|||
|
||||
anyLocusExists = true
|
||||
|
||||
getLocusAlleles := func()(string, string){
|
||||
//Outputs:
|
||||
// -float32: Final neuron value
|
||||
// -0 == Value is unknown
|
||||
// -0.5 == Value is known, phase is unknown
|
||||
// -1 == Value is known, phase is known
|
||||
// -string: Allele 1 value
|
||||
// -string: Allele 2 value
|
||||
getFirstNeuronAndLocusAlleles := func()(float32, string, string){
|
||||
|
||||
locusAllele1 := userLocusValue.Base1Value
|
||||
locusAllele2 := userLocusValue.Base2Value
|
||||
|
||||
if (randomizePhaseBool == false){
|
||||
return locusAllele1, locusAllele2
|
||||
if (locusAllele1 == locusAllele2){
|
||||
// Locus phase is unimportant
|
||||
return 1, locusAllele1, locusAllele2
|
||||
}
|
||||
|
||||
locusIsPhased := userLocusValue.LocusIsPhased
|
||||
|
||||
if (randomizePhaseBool == false && locusIsPhased == true){
|
||||
return 1, locusAllele1, locusAllele2
|
||||
}
|
||||
|
||||
// We randomize the phase of the locus
|
||||
// We always do this if the locus is not phased, because the genome data might actually be partially/fully phased,
|
||||
// even if it is not advertised as being phased
|
||||
|
||||
randomNumber := pseudorandomNumberGenerator.IntN(2)
|
||||
if (randomNumber == 1){
|
||||
// This has a 50% chance of being true.
|
||||
return locusAllele1, locusAllele2
|
||||
return 0.5, locusAllele1, locusAllele2
|
||||
}
|
||||
|
||||
return locusAllele2, locusAllele1
|
||||
return 0.5, locusAllele2, locusAllele1
|
||||
}
|
||||
|
||||
locusAllele1, locusAllele2 := getLocusAlleles()
|
||||
locusIsKnownAndPhasedNeuronValue, locusAllele1, locusAllele2 := getFirstNeuronAndLocusAlleles()
|
||||
|
||||
locusAllele1NeuronValue, err := convertAlleleToNeuron(locusAllele1)
|
||||
if (err != nil){ return false, nil, err }
|
||||
locusAllele2NeuronValue, err := convertAlleleToNeuron(locusAllele2)
|
||||
if (err != nil) { return false, nil, err }
|
||||
|
||||
getLocusIsKnownAndPhasedNeuronValue := func()float32{
|
||||
|
||||
if (locusAllele1 == locusAllele2){
|
||||
// Phase of locus must be known.
|
||||
// Swapping the loci would change nothing.
|
||||
return 1
|
||||
}
|
||||
|
||||
if (randomizePhaseBool == true){
|
||||
return 0.5
|
||||
}
|
||||
|
||||
locusIsPhased := userLocusValue.LocusIsPhased
|
||||
if (locusIsPhased == true){
|
||||
return 1
|
||||
}
|
||||
|
||||
return 0.5
|
||||
}
|
||||
|
||||
locusIsKnownAndPhasedNeuronValue := getLocusIsKnownAndPhasedNeuronValue()
|
||||
|
||||
inputLayer = append(inputLayer, locusIsKnownAndPhasedNeuronValue, locusAllele1NeuronValue, locusAllele2NeuronValue)
|
||||
}
|
||||
|
||||
|
|
|
@ -627,7 +627,7 @@ func StartCreateNewPersonGeneticAnalysis(personIdentifier string)(string, error)
|
|||
|
||||
for index, genomeMap := range personGenomesMapList{
|
||||
|
||||
newPercentageProgress, err := helpers.ScaleNumberProportionally(true, index, 0, finalIndex, 0, 10)
|
||||
newPercentageProgress, err := helpers.ScaleIntProportionally(true, index, 0, finalIndex, 0, 10)
|
||||
if (err != nil) { return err }
|
||||
|
||||
err = updatePercentageCompleteFunction(newPercentageProgress)
|
||||
|
@ -665,7 +665,7 @@ func StartCreateNewPersonGeneticAnalysis(personIdentifier string)(string, error)
|
|||
|
||||
analysisUpdatePercentageCompleteFunction := func(inputProgress int)error{
|
||||
|
||||
newPercentageProgress, err := helpers.ScaleNumberProportionally(true, inputProgress, 0, 100, 10, 10)
|
||||
newPercentageProgress, err := helpers.ScaleIntProportionally(true, inputProgress, 0, 100, 10, 10)
|
||||
if (err != nil) { return err }
|
||||
|
||||
err = updatePercentageCompleteFunction(newPercentageProgress)
|
||||
|
@ -899,7 +899,7 @@ func StartCreateNewCoupleGeneticAnalysis(inputPerson1Identifier string, inputPer
|
|||
break
|
||||
}
|
||||
|
||||
personPercentageComplete, err := helpers.ScaleNumberProportionally(true, processPercentageComplete, 0, 100, personPercentageRangeStart, personPercentageRangeEnd)
|
||||
personPercentageComplete, err := helpers.ScaleIntProportionally(true, processPercentageComplete, 0, 100, personPercentageRangeStart, personPercentageRangeEnd)
|
||||
if (err != nil) { return err }
|
||||
|
||||
err = updatePercentageCompleteFunction(personPercentageComplete)
|
||||
|
@ -978,7 +978,7 @@ func StartCreateNewCoupleGeneticAnalysis(inputPerson1Identifier string, inputPer
|
|||
|
||||
updateCoupleAnalysisPercentageCompleteFunction := func(newPercentage int)error{
|
||||
|
||||
personPercentageComplete, err := helpers.ScaleNumberProportionally(true, newPercentage, 0, 100, 74, 100)
|
||||
personPercentageComplete, err := helpers.ScaleIntProportionally(true, newPercentage, 0, 100, 74, 100)
|
||||
if (err != nil) { return err }
|
||||
|
||||
err = updatePercentageCompleteFunction(personPercentageComplete)
|
||||
|
|
|
@ -68,16 +68,17 @@ func CreateRawGenomeWithMetadataObject(genomeIdentifier [16]byte, rawGenomeStrin
|
|||
// -[]RawGenomeWithMetadata
|
||||
// -func(int)error: Update Percentage Complete Function
|
||||
//Outputs:
|
||||
// -bool: Any useful locations exist in any of the provided genomes
|
||||
// -[]GenomeWithMetadata: Genomes with metadata list
|
||||
// -[][16]byte: All raw genome identifiers list (not including combined genomes)
|
||||
// -bool: Combined genomes exist
|
||||
// -[16]byte: Only exclude conflicts genome identifier
|
||||
// -[16]byte: Only include shared genome identifier
|
||||
// -error
|
||||
func GetGenomesWithMetadataListFromRawGenomesList(inputGenomesList []RawGenomeWithMetadata, updatePercentageCompleteFunction func(int)error)([]GenomeWithMetadata, [][16]byte, bool, [16]byte, [16]byte, error){
|
||||
func GetGenomesWithMetadataListFromRawGenomesList(inputGenomesList []RawGenomeWithMetadata, updatePercentageCompleteFunction func(int)error)(bool, []GenomeWithMetadata, [][16]byte, bool, [16]byte, [16]byte, error){
|
||||
|
||||
if (len(inputGenomesList) == 0){
|
||||
return nil, nil, false, [16]byte{}, [16]byte{}, errors.New("GetGenomesWithMetadataListFromRawGenomesList called with empty inputGenomesList")
|
||||
return false, nil, nil, false, [16]byte{}, [16]byte{}, errors.New("GetGenomesWithMetadataListFromRawGenomesList called with empty inputGenomesList")
|
||||
}
|
||||
|
||||
// The reading of genomes will take up the first 20% of the percentage range
|
||||
|
@ -87,18 +88,17 @@ func GetGenomesWithMetadataListFromRawGenomesList(inputGenomesList []RawGenomeWi
|
|||
// Each map stores a genome from a company or a combined genome.
|
||||
genomesWithMetadataList := make([]GenomeWithMetadata, 0)
|
||||
|
||||
numberOfGenomesRead := 0
|
||||
totalNumberOfGenomesToRead := len(inputGenomesList)
|
||||
finalIndex := len(inputGenomesList) - 1
|
||||
|
||||
allRawGenomeIdentifiersList := make([][16]byte, 0)
|
||||
|
||||
for _, rawGenomeWithMetadataObject := range inputGenomesList{
|
||||
for index, rawGenomeWithMetadataObject := range inputGenomesList{
|
||||
|
||||
newPercentageCompletion, err := helpers.ScaleNumberProportionally(true, numberOfGenomesRead, 0, totalNumberOfGenomesToRead, 0, 20)
|
||||
if (err != nil) { return nil, nil, false, [16]byte{}, [16]byte{}, err }
|
||||
newPercentageCompletion, err := helpers.ScaleIntProportionally(true, index, 0, finalIndex, 0, 20)
|
||||
if (err != nil) { return false, nil, nil, false, [16]byte{}, [16]byte{}, err }
|
||||
|
||||
err = updatePercentageCompleteFunction(newPercentageCompletion)
|
||||
if (err != nil) { return nil, nil, false, [16]byte{}, [16]byte{}, err }
|
||||
if (err != nil) { return false, nil, nil, false, [16]byte{}, [16]byte{}, err }
|
||||
|
||||
genomeIdentifier := rawGenomeWithMetadataObject.GenomeIdentifier
|
||||
genomeIsPhased := rawGenomeWithMetadataObject.GenomeIsPhased
|
||||
|
@ -107,12 +107,11 @@ func GetGenomesWithMetadataListFromRawGenomesList(inputGenomesList []RawGenomeWi
|
|||
// Now we convert rawGenomeMap to a genomeMap
|
||||
|
||||
anyValuesExist, genomeMap, err := ConvertRawGenomeToGenomeMap(rawGenomeMap, genomeIsPhased)
|
||||
if (err != nil) { return nil, nil, false, [16]byte{}, [16]byte{}, err }
|
||||
if (err != nil) { return false, nil, nil, false, [16]byte{}, [16]byte{}, err }
|
||||
if (anyValuesExist == false){
|
||||
// We have to make sure this never happens so the user isn't confused as to why genomes
|
||||
// that were imported were not included in the analysis
|
||||
// We make sure this doesn't happen by verifying the genome at the time of importing
|
||||
return nil, nil, false, [16]byte{}, [16]byte{}, errors.New("Genome supplied to GetGenomesWithMetadataListFromRawGenomesList has no valid locations.")
|
||||
// This genome is not useful
|
||||
// No useful locations exist
|
||||
continue
|
||||
}
|
||||
|
||||
genomeWithMetadataObject := GenomeWithMetadata{
|
||||
|
@ -123,17 +122,20 @@ func GetGenomesWithMetadataListFromRawGenomesList(inputGenomesList []RawGenomeWi
|
|||
|
||||
genomesWithMetadataList = append(genomesWithMetadataList, genomeWithMetadataObject)
|
||||
allRawGenomeIdentifiersList = append(allRawGenomeIdentifiersList, genomeIdentifier)
|
||||
}
|
||||
|
||||
numberOfGenomesRead += 1
|
||||
if (len(genomesWithMetadataList) == 0){
|
||||
// None of the provided genomes contained any useful locations
|
||||
return false, nil, nil, false, [16]byte{}, [16]byte{}, nil
|
||||
}
|
||||
|
||||
containsDuplicates, _ := helpers.CheckIfListContainsDuplicates(allRawGenomeIdentifiersList)
|
||||
if (containsDuplicates == true){
|
||||
return nil, nil, false, [16]byte{}, [16]byte{}, errors.New("GetGenomesWithMetadataListFromRawGenomesList called with inputGenomesList containing duplicate genomeIdentifiers.")
|
||||
return false, nil, nil, false, [16]byte{}, [16]byte{}, errors.New("GetGenomesWithMetadataListFromRawGenomesList called with inputGenomesList containing duplicate genomeIdentifiers.")
|
||||
}
|
||||
|
||||
err := updatePercentageCompleteFunction(20)
|
||||
if (err != nil){ return nil, nil, false, [16]byte{}, [16]byte{}, err }
|
||||
if (err != nil){ return false, nil, nil, false, [16]byte{}, [16]byte{}, err }
|
||||
|
||||
if (len(genomesWithMetadataList) <= 1){
|
||||
|
||||
|
@ -141,9 +143,9 @@ func GetGenomesWithMetadataListFromRawGenomesList(inputGenomesList []RawGenomeWi
|
|||
// No genome combining is needed.
|
||||
|
||||
err = updatePercentageCompleteFunction(100)
|
||||
if (err != nil){ return nil, nil, false, [16]byte{}, [16]byte{}, err }
|
||||
if (err != nil){ return false, nil, nil, false, [16]byte{}, [16]byte{}, err }
|
||||
|
||||
return genomesWithMetadataList, allRawGenomeIdentifiersList, false, [16]byte{}, [16]byte{}, nil
|
||||
return true, genomesWithMetadataList, allRawGenomeIdentifiersList, false, [16]byte{}, [16]byte{}, nil
|
||||
}
|
||||
|
||||
// Now we create the shared genomes
|
||||
|
@ -156,15 +158,15 @@ func GetGenomesWithMetadataListFromRawGenomesList(inputGenomesList []RawGenomeWi
|
|||
// This map stores all RSIDs across all genomes
|
||||
allRSIDsMap := make(map[int64]struct{})
|
||||
|
||||
finalIndex := len(genomesWithMetadataList) - 1
|
||||
finalIndex = len(genomesWithMetadataList) - 1
|
||||
|
||||
for index, genomeWithMetadataObject := range genomesWithMetadataList{
|
||||
|
||||
newPercentageCompletion, err := helpers.ScaleNumberProportionally(true, index, 0, finalIndex, 20, 50)
|
||||
if (err != nil){ return nil, nil, false, [16]byte{}, [16]byte{}, err }
|
||||
newPercentageCompletion, err := helpers.ScaleIntProportionally(true, index, 0, finalIndex, 20, 50)
|
||||
if (err != nil){ return false, nil, nil, false, [16]byte{}, [16]byte{}, err }
|
||||
|
||||
err = updatePercentageCompleteFunction(newPercentageCompletion)
|
||||
if (err != nil){ return nil, nil, false, [16]byte{}, [16]byte{}, err }
|
||||
if (err != nil){ return false, nil, nil, false, [16]byte{}, [16]byte{}, err }
|
||||
|
||||
genomeMap := genomeWithMetadataObject.GenomeMap
|
||||
|
||||
|
@ -185,11 +187,11 @@ func GetGenomesWithMetadataListFromRawGenomesList(inputGenomesList []RawGenomeWi
|
|||
|
||||
for rsID, _ := range allRSIDsMap{
|
||||
|
||||
newPercentageCompletion, err := helpers.ScaleNumberProportionally(true, index, 0, finalIndex, 50, 100)
|
||||
if (err != nil){ return nil, nil, false, [16]byte{}, [16]byte{}, err }
|
||||
newPercentageCompletion, err := helpers.ScaleIntProportionally(true, index, 0, finalIndex, 50, 100)
|
||||
if (err != nil){ return false, nil, nil, false, [16]byte{}, [16]byte{}, err }
|
||||
|
||||
err = updatePercentageCompleteFunction(newPercentageCompletion)
|
||||
if (err != nil){ return nil, nil, false, [16]byte{}, [16]byte{}, err }
|
||||
if (err != nil){ return false, nil, nil, false, [16]byte{}, [16]byte{}, err }
|
||||
|
||||
index += 1
|
||||
|
||||
|
@ -200,7 +202,7 @@ func GetGenomesWithMetadataListFromRawGenomesList(inputGenomesList []RawGenomeWi
|
|||
}
|
||||
|
||||
anyAliasesExist, rsidAliasesList, err := locusMetadata.GetRSIDAliases(rsID)
|
||||
if (err != nil){ return nil, nil, false, [16]byte{}, [16]byte{}, err }
|
||||
if (err != nil){ return false, nil, nil, false, [16]byte{}, [16]byte{}, err }
|
||||
if (anyAliasesExist == true){
|
||||
|
||||
for _, rsidAlias := range rsidAliasesList{
|
||||
|
@ -386,7 +388,7 @@ func GetGenomesWithMetadataListFromRawGenomesList(inputGenomesList []RawGenomeWi
|
|||
}
|
||||
|
||||
locusBase1, locusBase2, phaseIsKnown_OnlyExcludeConflicts, phaseIsKnown_OnlyIncludeShared, err := getLocusBasePair()
|
||||
if (err != nil){ return nil, nil, false, [16]byte{}, [16]byte{}, err }
|
||||
if (err != nil){ return false, nil, nil, false, [16]byte{}, [16]byte{}, err }
|
||||
|
||||
// Now we add to the combined genome maps
|
||||
// The OnlyExcludeConflicts will only omit when there is a tie
|
||||
|
@ -436,7 +438,7 @@ func GetGenomesWithMetadataListFromRawGenomesList(inputGenomesList []RawGenomeWi
|
|||
}
|
||||
|
||||
onlyExcludeConflictsGenomeIdentifier, err := helpers.GetNewRandom16ByteArray()
|
||||
if (err != nil) { return nil, nil, false, [16]byte{}, [16]byte{}, err }
|
||||
if (err != nil) { return false, nil, nil, false, [16]byte{}, [16]byte{}, err }
|
||||
|
||||
onlyExcludeConflictsGenomeWithMetadataObject := GenomeWithMetadata{
|
||||
GenomeType: "OnlyExcludeConflicts",
|
||||
|
@ -445,7 +447,7 @@ func GetGenomesWithMetadataListFromRawGenomesList(inputGenomesList []RawGenomeWi
|
|||
}
|
||||
|
||||
onlyIncludeSharedGenomeIdentifier, err := helpers.GetNewRandom16ByteArray()
|
||||
if (err != nil) { return nil, nil, false, [16]byte{}, [16]byte{}, err }
|
||||
if (err != nil) { return false, nil, nil, false, [16]byte{}, [16]byte{}, err }
|
||||
|
||||
onlyIncludeSharedGenomeWithMetadataObject := GenomeWithMetadata{
|
||||
GenomeType: "OnlyIncludeShared",
|
||||
|
@ -456,9 +458,9 @@ func GetGenomesWithMetadataListFromRawGenomesList(inputGenomesList []RawGenomeWi
|
|||
genomesWithMetadataList = append(genomesWithMetadataList, onlyExcludeConflictsGenomeWithMetadataObject, onlyIncludeSharedGenomeWithMetadataObject)
|
||||
|
||||
err = updatePercentageCompleteFunction(100)
|
||||
if (err != nil){ return nil, nil, false, [16]byte{}, [16]byte{}, err }
|
||||
if (err != nil){ return false, nil, nil, false, [16]byte{}, [16]byte{}, err }
|
||||
|
||||
return genomesWithMetadataList, allRawGenomeIdentifiersList, true, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, nil
|
||||
return true, genomesWithMetadataList, allRawGenomeIdentifiersList, true, onlyExcludeConflictsGenomeIdentifier, onlyIncludeSharedGenomeIdentifier, nil
|
||||
}
|
||||
|
||||
//Outputs:
|
||||
|
|
|
@ -7,6 +7,7 @@ import "seekia/internal/helpers"
|
|||
import "encoding/csv"
|
||||
import "os"
|
||||
import "io"
|
||||
import "strings"
|
||||
|
||||
|
||||
type PhenotypeData_OpenSNP struct{
|
||||
|
@ -237,7 +238,7 @@ func ReadOpenSNPPhenotypesFile(fileObject *os.File)(bool, []PhenotypeData_OpenSN
|
|||
// -int: User height (in centimeters)
|
||||
getUserHeight := func()(bool, float64){
|
||||
|
||||
userHeightRaw := userDataLineSlice[13]
|
||||
userHeightRaw := userDataLineSlice[13]
|
||||
|
||||
switch userHeightRaw{
|
||||
case "-":{
|
||||
|
@ -273,64 +274,88 @@ func ReadOpenSNPPhenotypesFile(fileObject *os.File)(bool, []PhenotypeData_OpenSN
|
|||
case `4'9"`:{
|
||||
return true, 144.78
|
||||
}
|
||||
case `4'10"`:{
|
||||
case `4'10"`, `4'10`:{
|
||||
return true, 147.32
|
||||
}
|
||||
case `4'11"`:{
|
||||
case `4'11"`, `4' 11"`:{
|
||||
return true, 149.86
|
||||
}
|
||||
case `5'`:{
|
||||
case `5'`, `5' 0"`, `5' 0'`, `5'0"`, `5'0`:{
|
||||
return true, 152.4
|
||||
}
|
||||
case `5'1"`:{
|
||||
case `5'1"`, `5'1" or 155cm`:{
|
||||
return true, 154.94
|
||||
}
|
||||
case `5'1.5"`:{
|
||||
return true, 156.21
|
||||
}
|
||||
case `5'2"`:{
|
||||
return true, 157.48
|
||||
}
|
||||
case `5'3"`, `5'3''`, `160 cm`:{
|
||||
case `5' 2 1/2"`:{
|
||||
return true, 158.75
|
||||
}
|
||||
case `5'2.75" at max`:{
|
||||
return true, 159.385
|
||||
}
|
||||
case `5'3"`, `5'3''`:{
|
||||
return true, 160
|
||||
}
|
||||
case `5'4"`:{
|
||||
case `5' 3.5" `:{
|
||||
return true, 161.29
|
||||
}
|
||||
case `5'4"`, `5'4`, `5'4''`, "162.56", `64"`:{
|
||||
return true, 162.56
|
||||
}
|
||||
case `5'5"`:{
|
||||
case `5'4 3/4, taller than all females in my family`:{
|
||||
return true, 164.465
|
||||
}
|
||||
case `5'5"`, `5' 5"`, `5'5" `, `5’5”`:{
|
||||
return true, 165.1
|
||||
}
|
||||
case `5'6"`:{
|
||||
case "167":{
|
||||
return true, 167
|
||||
}
|
||||
case `5'6"`, `5’6`:{
|
||||
return true, 167.64
|
||||
}
|
||||
case `168 cm`:{
|
||||
return true, 168
|
||||
case `5' 6.5" `:{
|
||||
return true, 168.91
|
||||
}
|
||||
case `169.316`:{
|
||||
return true, 169.316
|
||||
}
|
||||
case `5'7"`:{
|
||||
return true, 170.18
|
||||
}
|
||||
case `Average ( 165cm < x < 180cm )`:{
|
||||
return true, 172.5
|
||||
}
|
||||
case `5'8"`:{
|
||||
return true, 172.72
|
||||
}
|
||||
case `5'9"`:{
|
||||
case `Average 173cm`:{
|
||||
return true, 173
|
||||
}
|
||||
case `5'8.5"`:{
|
||||
return true, 173.99
|
||||
}
|
||||
case `5'9"`, `5'9"/176cm`:{
|
||||
return true, 175.26
|
||||
}
|
||||
case `5" 9 1/2"`:{
|
||||
return true, 176.53
|
||||
}
|
||||
case `5'10"`, `5'10''`:{
|
||||
return true, 177.8
|
||||
}
|
||||
case `179 cm`:{
|
||||
return true, 179
|
||||
}
|
||||
case `180cm`:{
|
||||
return true, 180
|
||||
}
|
||||
case `5'11"`:{
|
||||
return true, 180.34
|
||||
}
|
||||
case `6'`:{
|
||||
case `6'`, `6 ft 0 in`, `6'0" - 183cm`:{
|
||||
return true, 182.88
|
||||
}
|
||||
case `183 cm`:{
|
||||
return true, 183
|
||||
}
|
||||
case `6'1"`:{
|
||||
case `6'1"`, `6' 1" - 185 cm`:{
|
||||
return true, 185.42
|
||||
}
|
||||
case `6'2"`:{
|
||||
|
@ -339,6 +364,9 @@ func ReadOpenSNPPhenotypesFile(fileObject *os.File)(bool, []PhenotypeData_OpenSN
|
|||
case `6'3"`:{
|
||||
return true, 190.5
|
||||
}
|
||||
case "192":{
|
||||
return true, 192
|
||||
}
|
||||
case `6'4"`:{
|
||||
return true, 193.04
|
||||
}
|
||||
|
@ -348,6 +376,9 @@ func ReadOpenSNPPhenotypesFile(fileObject *os.File)(bool, []PhenotypeData_OpenSN
|
|||
case `6'6"`:{
|
||||
return true, 198.12
|
||||
}
|
||||
case `>200cm`:{
|
||||
return true, 200
|
||||
}
|
||||
case `6'7"`:{
|
||||
return true, 200.66
|
||||
}
|
||||
|
@ -366,8 +397,28 @@ func ReadOpenSNPPhenotypesFile(fileObject *os.File)(bool, []PhenotypeData_OpenSN
|
|||
case `7'`:{
|
||||
return true, 213.36
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: Add more responses
|
||||
trimmedHeight, suffixExists := strings.CutSuffix(userHeightRaw, "cm")
|
||||
if (suffixExists == true){
|
||||
heightFloat64, err := helpers.ConvertStringToFloat64(trimmedHeight)
|
||||
if (err == nil){
|
||||
return true, heightFloat64
|
||||
}
|
||||
}
|
||||
|
||||
trimmedHeight, suffixExists = strings.CutSuffix(userHeightRaw, " cm")
|
||||
if (suffixExists == true){
|
||||
heightFloat64, err := helpers.ConvertStringToFloat64(trimmedHeight)
|
||||
if (err == nil){
|
||||
return true, heightFloat64
|
||||
}
|
||||
}
|
||||
|
||||
// This is an outcome with backticks and quotes
|
||||
result := "6`" + `2"`
|
||||
if (userHeightRaw == result){
|
||||
return true, 187.96
|
||||
}
|
||||
|
||||
return false, 0
|
||||
|
|
|
@ -1948,29 +1948,29 @@ func ConvertFloat64ToRoundedStringWithTranslatedUnits(inputFloat float64)(string
|
|||
}
|
||||
|
||||
|
||||
// This function takes a number and the min and max range of that number
|
||||
// It returns a number scaled between a new min and max
|
||||
func ScaleNumberProportionally(ascending bool, input int, inputMin int, inputMax int, newMin int, newMax int)(int, error){
|
||||
// This function takes an int and the min and max range of that int
|
||||
// It returns an int scaled between a new min and max
|
||||
func ScaleIntProportionally(ascending bool, input int, inputMin int, inputMax int, newMin int, newMax int)(int, error){
|
||||
|
||||
if (inputMin == inputMax) {
|
||||
return inputMin, nil
|
||||
}
|
||||
if (inputMin > inputMax) {
|
||||
return 0, errors.New("ScaleNumberProportionally error: InputMin is greater than inputMax")
|
||||
return 0, errors.New("ScaleIntProportionally error: InputMin is greater than inputMax")
|
||||
}
|
||||
|
||||
if (input < inputMin) {
|
||||
return 0, errors.New("ScaleNumberProportionally error: Input is less than inputMin")
|
||||
return 0, errors.New("ScaleIntProportionally error: Input is less than inputMin")
|
||||
}
|
||||
if (input > inputMax) {
|
||||
return 0, errors.New("ScaleNumberProportionally error: Input is greater than inputMax")
|
||||
return 0, errors.New("ScaleIntProportionally error: Input is greater than inputMax")
|
||||
}
|
||||
|
||||
if (newMin == newMax) {
|
||||
return newMin, nil
|
||||
}
|
||||
if (newMin > newMin){
|
||||
return 0, errors.New("ScaleNumberProportionally error: newMin is greater than newMin.")
|
||||
return 0, errors.New("ScaleIntProportionally error: newMin is greater than newMin.")
|
||||
}
|
||||
|
||||
inputRangePortionLength := input - inputMin
|
||||
|
@ -2002,6 +2002,58 @@ func ScaleNumberProportionally(ascending bool, input int, inputMin int, inputMax
|
|||
return result, nil
|
||||
}
|
||||
|
||||
// This function takes an float64 and the min and max range of that float64
|
||||
// It returns a float64 scaled between a new min and max
|
||||
func ScaleFloat64Proportionally(ascending bool, input float64, inputMin float64, inputMax float64, newMin float64, newMax float64)(float64, error){
|
||||
|
||||
if (inputMin == inputMax) {
|
||||
return inputMin, nil
|
||||
}
|
||||
if (inputMin > inputMax) {
|
||||
return 0, errors.New("ScaleFloat64Proportionally error: InputMin is greater than inputMax")
|
||||
}
|
||||
|
||||
if (input < inputMin) {
|
||||
return 0, errors.New("ScaleFloat64Proportionally error: Input is less than inputMin")
|
||||
}
|
||||
if (input > inputMax) {
|
||||
return 0, errors.New("ScaleFloat64Proportionally error: Input is greater than inputMax")
|
||||
}
|
||||
|
||||
if (newMin == newMax) {
|
||||
return newMin, nil
|
||||
}
|
||||
if (newMin > newMin){
|
||||
return 0, errors.New("ScaleFloat64Proportionally error: newMin is greater than newMin.")
|
||||
}
|
||||
|
||||
inputRangePortionLength := input - inputMin
|
||||
|
||||
inputRangeDistance := inputMax - inputMin
|
||||
|
||||
inputRangePortion := inputRangePortionLength/inputRangeDistance
|
||||
|
||||
// This represents the portion of our output range that we want to travel across
|
||||
getOutputRangePortion := func()float64{
|
||||
if (ascending == true){
|
||||
return inputRangePortion
|
||||
}
|
||||
|
||||
outputRangePortion := 1 - inputRangePortion
|
||||
return outputRangePortion
|
||||
}
|
||||
|
||||
outputRangePortion := getOutputRangePortion()
|
||||
|
||||
outputRangeDistance := newMax - newMin
|
||||
|
||||
outputRangePortionLength := outputRangeDistance * outputRangePortion
|
||||
|
||||
result := newMin + outputRangePortionLength
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func XORTwo32ByteArrays(array1 [32]byte, array2 [32]byte)[32]byte{
|
||||
|
||||
var newArray [32]byte
|
||||
|
|
|
@ -321,86 +321,86 @@ func TestSliceFunctions(t *testing.T){
|
|||
func TestNumberProportionalScaling(t *testing.T){
|
||||
|
||||
|
||||
result, err := helpers.ScaleNumberProportionally(true, 50, 0, 100, 0, 50)
|
||||
result, err := helpers.ScaleIntProportionally(true, 50, 0, 100, 0, 50)
|
||||
if (err != nil){
|
||||
t.Fatalf("ScaleNumberProportionally failed: " + err.Error())
|
||||
t.Fatalf("ScaleIntProportionally failed: " + err.Error())
|
||||
}
|
||||
if (result != 25){
|
||||
t.Fatalf("ScaleNumberProportionally failed test 1.")
|
||||
t.Fatalf("ScaleIntProportionally failed test 1.")
|
||||
}
|
||||
|
||||
result, err = helpers.ScaleNumberProportionally(true, 25, 0, 100, 0, 200)
|
||||
result, err = helpers.ScaleIntProportionally(true, 25, 0, 100, 0, 200)
|
||||
if (err != nil){
|
||||
t.Fatalf("ScaleNumberProportionally failed: " + err.Error())
|
||||
t.Fatalf("ScaleIntProportionally failed: " + err.Error())
|
||||
}
|
||||
if (result != 50){
|
||||
t.Fatalf("ScaleNumberProportionally failed test 2.")
|
||||
t.Fatalf("ScaleIntProportionally failed test 2.")
|
||||
}
|
||||
|
||||
result, err = helpers.ScaleNumberProportionally(false, 25, 0, 100, 0, 200)
|
||||
result, err = helpers.ScaleIntProportionally(false, 25, 0, 100, 0, 200)
|
||||
if (err != nil){
|
||||
t.Fatalf("ScaleNumberProportionally failed: " + err.Error())
|
||||
t.Fatalf("ScaleIntProportionally failed: " + err.Error())
|
||||
}
|
||||
if (result != 150){
|
||||
t.Fatalf("ScaleNumberProportionally failed test 3.")
|
||||
t.Fatalf("ScaleIntProportionally failed test 3.")
|
||||
}
|
||||
|
||||
result, err = helpers.ScaleNumberProportionally(true, 1, 0, 10, 0, 200)
|
||||
result, err = helpers.ScaleIntProportionally(true, 1, 0, 10, 0, 200)
|
||||
if (err != nil){
|
||||
t.Fatalf("ScaleNumberProportionally failed: " + err.Error())
|
||||
t.Fatalf("ScaleIntProportionally failed: " + err.Error())
|
||||
}
|
||||
if (result != 20){
|
||||
t.Fatalf("ScaleNumberProportionally failed test 4.")
|
||||
t.Fatalf("ScaleIntProportionally failed test 4.")
|
||||
}
|
||||
|
||||
result, err = helpers.ScaleNumberProportionally(true, -50, -100, 0, 0, 200)
|
||||
result, err = helpers.ScaleIntProportionally(true, -50, -100, 0, 0, 200)
|
||||
if (err != nil){
|
||||
t.Fatalf("ScaleNumberProportionally failed: " + err.Error())
|
||||
t.Fatalf("ScaleIntProportionally failed: " + err.Error())
|
||||
}
|
||||
if (result != 100){
|
||||
t.Fatalf("ScaleNumberProportionally failed test 5.")
|
||||
t.Fatalf("ScaleIntProportionally failed test 5.")
|
||||
}
|
||||
|
||||
|
||||
result, err = helpers.ScaleNumberProportionally(true, -25, -100, 0, 0, 200)
|
||||
result, err = helpers.ScaleIntProportionally(true, -25, -100, 0, 0, 200)
|
||||
if (err != nil){
|
||||
t.Fatalf("ScaleNumberProportionally failed: " + err.Error())
|
||||
t.Fatalf("ScaleIntProportionally failed: " + err.Error())
|
||||
}
|
||||
if (result != 150){
|
||||
t.Fatalf("ScaleNumberProportionally failed test 6.")
|
||||
t.Fatalf("ScaleIntProportionally failed test 6.")
|
||||
}
|
||||
|
||||
result, err = helpers.ScaleNumberProportionally(true, 50, 0, 100, 0, 2)
|
||||
result, err = helpers.ScaleIntProportionally(true, 50, 0, 100, 0, 2)
|
||||
if (err != nil){
|
||||
t.Fatalf("ScaleNumberProportionally failed: " + err.Error())
|
||||
t.Fatalf("ScaleIntProportionally failed: " + err.Error())
|
||||
}
|
||||
if (result != 1){
|
||||
t.Fatalf("ScaleNumberProportionally failed test 7.")
|
||||
t.Fatalf("ScaleIntProportionally failed test 7.")
|
||||
}
|
||||
|
||||
|
||||
result, err = helpers.ScaleNumberProportionally(true, 10, 0, 100, 5, 25)
|
||||
result, err = helpers.ScaleIntProportionally(true, 10, 0, 100, 5, 25)
|
||||
if (err != nil){
|
||||
t.Fatalf("ScaleNumberProportionally failed: " + err.Error())
|
||||
t.Fatalf("ScaleIntProportionally failed: " + err.Error())
|
||||
}
|
||||
if (result != 7){
|
||||
t.Fatalf("ScaleNumberProportionally failed test 8.")
|
||||
t.Fatalf("ScaleIntProportionally failed test 8.")
|
||||
}
|
||||
|
||||
result, err = helpers.ScaleNumberProportionally(true, 100, 0, 100, 2, 22)
|
||||
result, err = helpers.ScaleIntProportionally(true, 100, 0, 100, 2, 22)
|
||||
if (err != nil){
|
||||
t.Fatalf("ScaleNumberProportionally failed: " + err.Error())
|
||||
t.Fatalf("ScaleIntProportionally failed: " + err.Error())
|
||||
}
|
||||
if (result != 22){
|
||||
t.Fatalf("ScaleNumberProportionally failed test 9.")
|
||||
t.Fatalf("ScaleIntProportionally failed test 9.")
|
||||
}
|
||||
|
||||
result, err = helpers.ScaleNumberProportionally(false, 0, 0, 100, 2, 22)
|
||||
result, err = helpers.ScaleIntProportionally(false, 0, 0, 100, 2, 22)
|
||||
if (err != nil){
|
||||
t.Fatalf("ScaleNumberProportionally failed: " + err.Error())
|
||||
t.Fatalf("ScaleIntProportionally failed: " + err.Error())
|
||||
}
|
||||
if (result != 22){
|
||||
t.Fatalf("ScaleNumberProportionally failed test 10.")
|
||||
t.Fatalf("ScaleIntProportionally failed test 10.")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -121,7 +121,7 @@ func GetImageWithEmojiOverlay(inputImage image.Image, emojiImage image.Image, em
|
|||
imageLongerSideLength := max(imageWidth, imageHeight)
|
||||
|
||||
// This is the length of the longest side of the emoji we will draw
|
||||
emojiLongerSideMaximumLength, err := helpers.ScaleNumberProportionally(true, emojiScale, 0, 100, 1, imageLongerSideLength)
|
||||
emojiLongerSideMaximumLength, err := helpers.ScaleIntProportionally(true, emojiScale, 0, 100, 1, imageLongerSideLength)
|
||||
if (err != nil) { return nil, err }
|
||||
|
||||
newEmoji, err := imagery.ResizeGolangImage(emojiImage, emojiLongerSideMaximumLength)
|
||||
|
@ -130,10 +130,10 @@ func GetImageWithEmojiOverlay(inputImage image.Image, emojiImage image.Image, em
|
|||
// Now we get the X and Y Coordinate point for where we will draw the emoji
|
||||
// We first find the center coordinate of the emoji we are drawing
|
||||
|
||||
emojiCenterXCoordinate, err := helpers.ScaleNumberProportionally(true, xAxisPercentage, 0, 100, 0, imageWidth)
|
||||
emojiCenterXCoordinate, err := helpers.ScaleIntProportionally(true, xAxisPercentage, 0, 100, 0, imageWidth)
|
||||
if (err != nil) { return nil, err }
|
||||
|
||||
emojiCenterYCoordinate, err := helpers.ScaleNumberProportionally(false, yAxisPercentage, 0, 100, 0, imageHeight)
|
||||
emojiCenterYCoordinate, err := helpers.ScaleIntProportionally(false, yAxisPercentage, 0, 100, 0, imageHeight)
|
||||
if (err != nil) { return nil, err }
|
||||
|
||||
emojiWidth, emojiHeight, err := imagery.GetImageWidthAndHeightPixels(newEmoji)
|
||||
|
|
|
@ -497,7 +497,7 @@ func PixelateGolangImage(inputImage image.Image, amount0to100 int)(image.Image,
|
|||
|
||||
longerSideLength := max(widthPixels, heightPixels)
|
||||
|
||||
pixelationAmountInt, err := helpers.ScaleNumberProportionally(true, amount0to100, 0, 100, 0, longerSideLength/5)
|
||||
pixelationAmountInt, err := helpers.ScaleIntProportionally(true, amount0to100, 0, 100, 0, longerSideLength/5)
|
||||
if (err != nil) { return nil, err }
|
||||
|
||||
rectangle := image.Rect(0, 0, widthPixels, heightPixels)
|
||||
|
|
|
@ -469,7 +469,7 @@ func StartUpdatingMyConversations(identityType string, networkType byte) error{
|
|||
// Input is a value between 0-100
|
||||
// We reduce it down to a value between 0-50
|
||||
|
||||
newProgressInt, err := helpers.ScaleNumberProportionally(true, input, 0, 100, 0, 50)
|
||||
newProgressInt, err := helpers.ScaleIntProportionally(true, input, 0, 100, 0, 50)
|
||||
if (err != nil) { return err }
|
||||
|
||||
newPercentageProgressFloat := float64(newProgressInt)/100
|
||||
|
@ -715,7 +715,7 @@ func StartUpdatingMyConversations(identityType string, networkType byte) error{
|
|||
return nil
|
||||
}
|
||||
|
||||
newScaledPercentageInt, err := helpers.ScaleNumberProportionally(true, index, 0, maximumIndex, 70, 85)
|
||||
newScaledPercentageInt, err := helpers.ScaleIntProportionally(true, index, 0, maximumIndex, 70, 85)
|
||||
if (err != nil) { return err }
|
||||
|
||||
newProgressFloat := float64(newScaledPercentageInt)/100
|
||||
|
|
|
@ -268,7 +268,7 @@ func GetUpdatedMyChatMessagesMapList(myIdentityType string, networkType byte, up
|
|||
|
||||
for messageHash, messageInbox := range myRawInboxMessageHashesMap{
|
||||
|
||||
newPercentageProgress, err := helpers.ScaleNumberProportionally(true, index, 0, maximumIndex, 20, 100)
|
||||
newPercentageProgress, err := helpers.ScaleIntProportionally(true, index, 0, maximumIndex, 20, 100)
|
||||
if (err != nil){ return err }
|
||||
|
||||
err = updateProgressFunction(newPercentageProgress)
|
||||
|
|
|
@ -480,7 +480,7 @@ func StartUpdatingViewedContent(networkType byte)error{
|
|||
return nil
|
||||
}
|
||||
|
||||
newScaledPercentageInt, err := helpers.ScaleNumberProportionally(true, index, 0, maximumIndex, 50, 80)
|
||||
newScaledPercentageInt, err := helpers.ScaleIntProportionally(true, index, 0, maximumIndex, 50, 80)
|
||||
if (err != nil) { return err }
|
||||
|
||||
newProgressFloat := float64(newScaledPercentageInt)/100
|
||||
|
|
|
@ -365,7 +365,7 @@ func StartUpdatingViewedModerators(networkType byte)error{
|
|||
return nil
|
||||
}
|
||||
|
||||
progressPercentage, err := helpers.ScaleNumberProportionally(true, index, 0, numberOfModerators-1, 20, 50)
|
||||
progressPercentage, err := helpers.ScaleIntProportionally(true, index, 0, numberOfModerators-1, 20, 50)
|
||||
if (err != nil) { return err }
|
||||
|
||||
progressFloat := float64(progressPercentage)/100
|
||||
|
@ -452,7 +452,7 @@ func StartUpdatingViewedModerators(networkType byte)error{
|
|||
return nil
|
||||
}
|
||||
|
||||
newScaledPercentageInt, err := helpers.ScaleNumberProportionally(true, index, 0, maximumIndex, 50, 80)
|
||||
newScaledPercentageInt, err := helpers.ScaleIntProportionally(true, index, 0, maximumIndex, 50, 80)
|
||||
if (err != nil) { return err }
|
||||
|
||||
newProgressFloat := float64(newScaledPercentageInt)/100
|
||||
|
|
|
@ -323,7 +323,7 @@ func StartUpdatingMyMatches(networkType byte)error{
|
|||
return nil
|
||||
}
|
||||
|
||||
progressPercentage, err := helpers.ScaleNumberProportionally(true, index, 0, maximumIndex, 0, 50)
|
||||
progressPercentage, err := helpers.ScaleIntProportionally(true, index, 0, maximumIndex, 0, 50)
|
||||
if (err != nil) { return err }
|
||||
|
||||
progressFloat := float64(progressPercentage)/100
|
||||
|
@ -449,7 +449,7 @@ func StartUpdatingMyMatches(networkType byte)error{
|
|||
return nil
|
||||
}
|
||||
|
||||
newScaledPercentageInt, err := helpers.ScaleNumberProportionally(true, index, 0, maximumIndex, 50, 80)
|
||||
newScaledPercentageInt, err := helpers.ScaleIntProportionally(true, index, 0, maximumIndex, 50, 80)
|
||||
if (err != nil) { return err }
|
||||
|
||||
newProgressFloat := float64(newScaledPercentageInt)/100
|
||||
|
|
|
@ -335,7 +335,7 @@ func StartUpdatingViewedHosts(networkType byte)error{
|
|||
return nil
|
||||
}
|
||||
|
||||
progressPercentage, err := helpers.ScaleNumberProportionally(true, index, 0, numberOfEnabledHosts-1, 0, 50)
|
||||
progressPercentage, err := helpers.ScaleIntProportionally(true, index, 0, numberOfEnabledHosts-1, 0, 50)
|
||||
if (err != nil) { return err }
|
||||
|
||||
progressFloat := float64(progressPercentage)/100
|
||||
|
@ -413,13 +413,13 @@ func StartUpdatingViewedHosts(networkType byte)error{
|
|||
|
||||
hostAttributeValuesMap[hostIdentityHashString] = attributeValueFloat
|
||||
}
|
||||
|
||||
|
||||
isStopped := CheckIfBuildViewedHostsIsStopped()
|
||||
if (isStopped == true){
|
||||
return nil
|
||||
}
|
||||
|
||||
newScaledPercentageInt, err := helpers.ScaleNumberProportionally(true, index, 0, maximumIndex, 50, 80)
|
||||
newScaledPercentageInt, err := helpers.ScaleIntProportionally(true, index, 0, maximumIndex, 50, 80)
|
||||
if (err != nil) { return err }
|
||||
|
||||
newProgressFloat := float64(newScaledPercentageInt)/100
|
||||
|
@ -429,47 +429,47 @@ func StartUpdatingViewedHosts(networkType byte)error{
|
|||
appMemory.SetMemoryEntry("ViewedHostsReadyProgressStatus", newProgressString)
|
||||
}
|
||||
|
||||
compareHostsFunction := func(identityHashA string, identityHashB string)int{
|
||||
compareHostsFunction := func(identityHash1 string, identityHash2 string)int{
|
||||
|
||||
if (identityHashA == identityHashB){
|
||||
if (identityHash1 == identityHash2){
|
||||
panic("compareHostsFunction called with duplicate hosts.")
|
||||
}
|
||||
|
||||
attributeValueA, attributeValueAExists := hostAttributeValuesMap[identityHashA]
|
||||
attributeValue1, attributeValue1Exists := hostAttributeValuesMap[identityHash1]
|
||||
|
||||
attributeValueB, attributeValueBExists := hostAttributeValuesMap[identityHashB]
|
||||
attributeValue2, attributeValue2Exists := hostAttributeValuesMap[identityHash2]
|
||||
|
||||
if (attributeValueAExists == false && attributeValueBExists == false){
|
||||
if (attributeValue1Exists == false && attributeValue2Exists == false){
|
||||
|
||||
// We don't know the attribute value for either host
|
||||
// We sort hosts in unicode order
|
||||
if (identityHashA < identityHashB){
|
||||
if (identityHash1 < identityHash2){
|
||||
return -1
|
||||
}
|
||||
return 1
|
||||
|
||||
} else if (attributeValueAExists == true && attributeValueBExists == false){
|
||||
} else if (attributeValue1Exists == true && attributeValue2Exists == false){
|
||||
|
||||
// We sort unknown attribute hosts to the back of the list
|
||||
|
||||
return -1
|
||||
|
||||
} else if (attributeValueAExists == false && attributeValueBExists == true){
|
||||
} else if (attributeValue1Exists == false && attributeValue2Exists == true){
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
// Both attribute values exist
|
||||
|
||||
if (attributeValueA == attributeValueB){
|
||||
if (attributeValue1 == attributeValue2){
|
||||
// We sort identity hashes in unicode order
|
||||
if (identityHashA < identityHashB){
|
||||
if (identityHash1 < identityHash2){
|
||||
return -1
|
||||
}
|
||||
return 1
|
||||
}
|
||||
|
||||
if (attributeValueA < attributeValueB){
|
||||
if (attributeValue1 < attributeValue2){
|
||||
|
||||
if (currentSortDirection == "Ascending"){
|
||||
return -1
|
||||
|
|
|
@ -153,11 +153,6 @@ func UpdateMyExportedProfile(myProfileType string, networkType byte)error{
|
|||
_, _, _, _, myGenomesMap, err := readGeneticAnalysis.GetMetadataFromPersonGeneticAnalysis(myGeneticAnalysisObject)
|
||||
if (err != nil) { return err }
|
||||
|
||||
myGenomeLocusValuesMap, exists := myGenomesMap[genomeIdentifierToShare]
|
||||
if (exists == false){
|
||||
return errors.New("GetMyChosenMateGeneticAnalysis returning genetic analysis which has GenomesMap which is missing my genome identifier.")
|
||||
}
|
||||
|
||||
monogenicDiseaseNamesList, err := monogenicDiseases.GetMonogenicDiseaseNamesList()
|
||||
if (err != nil) { return err }
|
||||
|
||||
|
@ -253,6 +248,11 @@ func UpdateMyExportedProfile(myProfileType string, networkType byte)error{
|
|||
}
|
||||
}
|
||||
|
||||
myGenomeLocusValuesMap, exists := myGenomesMap[genomeIdentifierToShare]
|
||||
if (exists == false){
|
||||
return errors.New("GetMyChosenMateGeneticAnalysis returning genetic analysis which has GenomesMap which is missing my genome identifier.")
|
||||
}
|
||||
|
||||
for rsID, _ := range myLociToShareMap{
|
||||
|
||||
locusValueObject, exists := myGenomeLocusValuesMap[rsID]
|
||||
|
|
|
@ -38,6 +38,7 @@ import "os"
|
|||
import "strings"
|
||||
import "sync"
|
||||
import "slices"
|
||||
import "math"
|
||||
import mathRand "math/rand/v2"
|
||||
import goFilepath "path/filepath"
|
||||
import "time"
|
||||
|
@ -722,7 +723,7 @@ func setStartAndMonitorCreateTrainingDataPage(window fyne.Window, previousPage f
|
|||
if (err != nil) { return false, false, err }
|
||||
|
||||
//TODO: Add more traits
|
||||
traitNamesList := []string{"Eye Color", "Lactose Tolerance"}
|
||||
traitNamesList := []string{"Eye Color", "Lactose Tolerance", "Height"}
|
||||
|
||||
// We create the folders for each trait's training data
|
||||
|
||||
|
@ -765,7 +766,7 @@ func setStartAndMonitorCreateTrainingDataPage(window fyne.Window, previousPage f
|
|||
err = progressDetailsBinding.Set(progressDetailsStatus)
|
||||
if (err != nil) { return false, false, err }
|
||||
|
||||
trainingProgressPercentage, err := helpers.ScaleNumberProportionally(true, index, 0, maximumIndex, 0, 100)
|
||||
trainingProgressPercentage, err := helpers.ScaleIntProportionally(true, index, 0, maximumIndex, 0, 100)
|
||||
if (err != nil) { return false, false, err }
|
||||
|
||||
trainingProgressFloat64 := float64(trainingProgressPercentage)/100
|
||||
|
@ -837,18 +838,26 @@ func setStartAndMonitorCreateTrainingDataPage(window fyne.Window, previousPage f
|
|||
continue
|
||||
}
|
||||
|
||||
getUserLociValuesMap := func()(map[int64]locusValue.LocusValue, error){
|
||||
//Outputs:
|
||||
// -bool: Any useful locations exist in any of the user's genomes
|
||||
// -map[int64]locusValue.LocusValue
|
||||
// -error
|
||||
getUserLociValuesMap := func()(bool, map[int64]locusValue.LocusValue, error){
|
||||
|
||||
updatePercentageCompleteFunction := func(_ int)error{
|
||||
return nil
|
||||
}
|
||||
|
||||
genomesWithMetadataList, _, combinedGenomesExist, onlyExcludeConflictsGenomeIdentifier, _, err := prepareRawGenomes.GetGenomesWithMetadataListFromRawGenomesList(userRawGenomesWithMetadataList, updatePercentageCompleteFunction)
|
||||
if (err != nil) { return nil, err }
|
||||
anyUsefulLocationsExist, genomesWithMetadataList, _, combinedGenomesExist, onlyExcludeConflictsGenomeIdentifier, _, err := prepareRawGenomes.GetGenomesWithMetadataListFromRawGenomesList(userRawGenomesWithMetadataList, updatePercentageCompleteFunction)
|
||||
if (err != nil) { return false, nil, err }
|
||||
if (anyUsefulLocationsExist == false){
|
||||
// None of the user's genomes have any useful locations
|
||||
return false, nil, nil
|
||||
}
|
||||
if (combinedGenomesExist == false){
|
||||
|
||||
if (len(genomesWithMetadataList) != 1){
|
||||
return nil, errors.New("GetGenomesWithMetadataListFromRawGenomesList returning non-1 length genomesWithMetadataList when combinedGenomesExist == false")
|
||||
return false, nil, errors.New("GetGenomesWithMetadataListFromRawGenomesList returning non-1 length genomesWithMetadataList when combinedGenomesExist == false")
|
||||
}
|
||||
|
||||
// Only 1 genome exists
|
||||
|
@ -857,7 +866,7 @@ func setStartAndMonitorCreateTrainingDataPage(window fyne.Window, previousPage f
|
|||
|
||||
genomeMap := genomeWithMetadataObject.GenomeMap
|
||||
|
||||
return genomeMap, nil
|
||||
return true, genomeMap, nil
|
||||
}
|
||||
|
||||
for _, genomeWithMetadataObject := range genomesWithMetadataList{
|
||||
|
@ -868,15 +877,19 @@ func setStartAndMonitorCreateTrainingDataPage(window fyne.Window, previousPage f
|
|||
|
||||
genomeMap := genomeWithMetadataObject.GenomeMap
|
||||
|
||||
return genomeMap, nil
|
||||
return true, genomeMap, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, errors.New("OnlyExcludeConflicts genome not found from GetGenomesWithMetadataListFromRawGenomesList's returned list.")
|
||||
return false, nil, errors.New("OnlyExcludeConflicts genome not found from GetGenomesWithMetadataListFromRawGenomesList's returned list.")
|
||||
}
|
||||
|
||||
userLociValuesMap, err := getUserLociValuesMap()
|
||||
anyUsefulLocationsExist, userLociValuesMap, err := getUserLociValuesMap()
|
||||
if (err != nil) { return false, false, err }
|
||||
if (anyUsefulLocationsExist == false){
|
||||
// None of the user's genome files contain any useful locations
|
||||
continue
|
||||
}
|
||||
|
||||
for _, traitName := range traitNamesList{
|
||||
|
||||
|
@ -980,7 +993,7 @@ func setTrainModelsPage(window fyne.Window, previousPage func()){
|
|||
description3 := getLabelCentered("This will take a while.")
|
||||
description4 := getLabelCentered("You must select a trait model to train.")
|
||||
|
||||
traitNamesList := []string{"Eye Color", "Lactose Tolerance"}
|
||||
traitNamesList := []string{"Eye Color", "Lactose Tolerance", "Height"}
|
||||
|
||||
traitNameSelector := widget.NewSelect(traitNamesList, nil)
|
||||
|
||||
|
@ -1305,7 +1318,7 @@ func setTestModelsPage(window fyne.Window, previousPage func()){
|
|||
description5 := getLabelCentered("The results will also be saved in the ModelAccuracies folder.")
|
||||
description6 := getLabelCentered("You must select a trait model to test.")
|
||||
|
||||
traitNamesList := []string{"Eye Color", "Lactose Tolerance"}
|
||||
traitNamesList := []string{"Eye Color", "Lactose Tolerance", "Height"}
|
||||
|
||||
traitNameSelector := widget.NewSelect(traitNamesList, nil)
|
||||
|
||||
|
@ -1383,7 +1396,7 @@ func setStartAndMonitorTestModelPage(window fyne.Window, traitName string, previ
|
|||
|
||||
//Outputs:
|
||||
// -bool: Process completed (true == was not stopped mid-way)
|
||||
// -geneticPrediction.TraitPredictionAccuracyInfoMap
|
||||
// -geneticPrediction.DiscreteTraitPredictionAccuracyInfoMap
|
||||
// -error
|
||||
testModel := func()(bool, geneticPrediction.DiscreteTraitPredictionAccuracyInfoMap, error){
|
||||
|
||||
|
@ -1464,10 +1477,10 @@ func setStartAndMonitorTestModelPage(window fyne.Window, traitName string, previ
|
|||
return false, nil, errors.New("Neural network prediction output length does not match expected output length.")
|
||||
}
|
||||
|
||||
correctOutcomeName, err := geneticPrediction.GetOutcomeNameFromOutputLayer(traitName, true, trainingDataExpectedOutputLayer)
|
||||
correctOutcomeName, err := geneticPrediction.GetDiscreteOutcomeNameFromOutputLayer(traitName, true, trainingDataExpectedOutputLayer)
|
||||
if (err != nil) { return false, nil, err }
|
||||
|
||||
predictedOutcomeName, err := geneticPrediction.GetOutcomeNameFromOutputLayer(traitName, true, predictionLayer)
|
||||
predictedOutcomeName, err := geneticPrediction.GetDiscreteOutcomeNameFromOutputLayer(traitName, true, predictionLayer)
|
||||
if (err != nil) { return false, nil, err }
|
||||
|
||||
getPredictionIsCorrectBool := func()bool{
|
||||
|
@ -1565,7 +1578,7 @@ func setStartAndMonitorTestModelPage(window fyne.Window, traitName string, previ
|
|||
// This map stores the accuracy for each outcome
|
||||
traitPredictionAccuracyInfoMap := make(map[geneticPrediction.DiscreteTraitOutcomeInfo]geneticPrediction.DiscreteTraitPredictionAccuracyInfo)
|
||||
|
||||
for traitAccuracyData, value := range traitPredictionInfoMap{
|
||||
for traitPredictionInfo, value := range traitPredictionInfoMap{
|
||||
|
||||
quantityOfExamples := value.QuantityOfExamples
|
||||
quantityOfPredictions := value.QuantityOfPredictions
|
||||
|
@ -1601,7 +1614,7 @@ func setStartAndMonitorTestModelPage(window fyne.Window, traitName string, previ
|
|||
newTraitPredictionAccuracyInfo.ProbabilityOfCorrectOutcomePrediction = percentageOfCorrectOutcomePredictions
|
||||
}
|
||||
|
||||
traitPredictionAccuracyInfoMap[traitAccuracyData] = newTraitPredictionAccuracyInfo
|
||||
traitPredictionAccuracyInfoMap[traitPredictionInfo] = newTraitPredictionAccuracyInfo
|
||||
}
|
||||
|
||||
// Testing is complete.
|
||||
|
@ -1635,6 +1648,224 @@ func setStartAndMonitorTestModelPage(window fyne.Window, traitName string, previ
|
|||
}
|
||||
|
||||
setViewModelTestingDiscreteTraitResultsPage(window, traitName, traitPredictionAccuracyInfoMap, previousPage)
|
||||
|
||||
} else {
|
||||
|
||||
// traitIsDiscreteOrNumeric == "Numeric"
|
||||
|
||||
//Outputs:
|
||||
// -bool: Process completed (true == was not stopped mid-way)
|
||||
// -geneticPrediction.NumericTraitPredictionAccuracyInfoMap
|
||||
// -error
|
||||
testModel := func()(bool, geneticPrediction.NumericTraitPredictionAccuracyInfoMap, error){
|
||||
|
||||
// We use this map to count up the information about predictions
|
||||
// We use information from this map to construct the final accuracy information map
|
||||
// Map Structure: NumericTraitOutcomeInfo -> List of true outcomes
|
||||
traitPredictionInfoMap := make(map[geneticPrediction.NumericTraitOutcomeInfo][]float64)
|
||||
|
||||
|
||||
_, testingSetFilepathsList, err := getTrainingAndTestingDataFilepathLists(traitName)
|
||||
if (err != nil) { return false, nil, err }
|
||||
|
||||
traitNameWithoutWhitespaces := strings.ReplaceAll(traitName, " ", "")
|
||||
|
||||
// We read the trained model for this trait
|
||||
modelFilename := traitNameWithoutWhitespaces + "Model.gob"
|
||||
|
||||
trainedModelFilepath := goFilepath.Join("./TrainedModels/", modelFilename)
|
||||
|
||||
fileExists, fileContents, err := localFilesystem.GetFileContents(trainedModelFilepath)
|
||||
if (err != nil) { return false, nil, err }
|
||||
if (fileExists == false){
|
||||
return false, nil, errors.New("TrainedModel not found: " + trainedModelFilepath)
|
||||
}
|
||||
|
||||
neuralNetworkObject, err := geneticPrediction.DecodeBytesToNeuralNetworkObject(fileContents)
|
||||
if (err != nil) { return false, nil, err }
|
||||
|
||||
numberOfTrainingDatas := len(testingSetFilepathsList)
|
||||
numberOfTrainingDatasString := helpers.ConvertIntToString(numberOfTrainingDatas)
|
||||
|
||||
finalIndex := numberOfTrainingDatas - 1
|
||||
|
||||
for index, filePath := range testingSetFilepathsList{
|
||||
|
||||
testModelIsStoppedBoolMutex.RLock()
|
||||
testModelIsStopped := testModelIsStoppedBool
|
||||
testModelIsStoppedBoolMutex.RUnlock()
|
||||
|
||||
if (testModelIsStopped == true){
|
||||
// User exited the process
|
||||
return false, nil, nil
|
||||
}
|
||||
|
||||
fileExists, fileContents, err := localFilesystem.GetFileContents(filePath)
|
||||
if (err != nil) { return false, nil, err }
|
||||
if (fileExists == false){
|
||||
return false, nil, errors.New("TrainingData file not found: " + filePath)
|
||||
}
|
||||
|
||||
trainingDataObject, err := geneticPrediction.DecodeBytesToTrainingDataObject(fileContents)
|
||||
if (err != nil) { return false, nil, err }
|
||||
|
||||
trainingDataInputLayer := trainingDataObject.InputLayer
|
||||
trainingDataExpectedOutputLayer := trainingDataObject.OutputLayer
|
||||
|
||||
predictionLayer, err := geneticPrediction.GetNeuralNetworkRawPrediction(&neuralNetworkObject, false, trainingDataInputLayer)
|
||||
if (err != nil) { return false, nil, err }
|
||||
|
||||
if (len(predictionLayer) != 1){
|
||||
return false, nil, errors.New("Neural network numeric prediction output layer length is not 1.")
|
||||
}
|
||||
|
||||
if (len(trainingDataExpectedOutputLayer) != 1){
|
||||
return false, nil, errors.New("Neural network training data prediction output layer length is not 1.")
|
||||
}
|
||||
|
||||
correctOutcomeValue, err := geneticPrediction.GetNumericOutcomeValueFromOutputLayer(traitName, trainingDataExpectedOutputLayer)
|
||||
if (err != nil) { return false, nil, err }
|
||||
|
||||
predictedOutcomeValue, err := geneticPrediction.GetNumericOutcomeValueFromOutputLayer(traitName, predictionLayer)
|
||||
if (err != nil) { return false, nil, err }
|
||||
|
||||
numberOfKnownLoci, numberOfKnownAndPhasedLoci, numberOfLoci, err := geneticPrediction.GetLociInfoFromNetworkInputLayer(trainingDataInputLayer)
|
||||
if (err != nil) { return false, nil, err }
|
||||
|
||||
proportionOfLociTested := float64(numberOfKnownLoci)/float64(numberOfLoci)
|
||||
percentageOfLociTested := int(100*proportionOfLociTested)
|
||||
|
||||
proportionOfPhasedLoci := float64(numberOfKnownAndPhasedLoci)/float64(numberOfKnownLoci)
|
||||
percentageOfPhasedLoci := int(100*proportionOfPhasedLoci)
|
||||
|
||||
newNumericTraitOutcomeInfo := geneticPrediction.NumericTraitOutcomeInfo{
|
||||
OutcomeValue: predictedOutcomeValue,
|
||||
PercentageOfLociTested: percentageOfLociTested,
|
||||
PercentageOfPhasedLoci: percentageOfPhasedLoci,
|
||||
}
|
||||
|
||||
existingList, exists := traitPredictionInfoMap[newNumericTraitOutcomeInfo]
|
||||
if (exists == false){
|
||||
traitPredictionInfoMap[newNumericTraitOutcomeInfo] = []float64{correctOutcomeValue}
|
||||
} else {
|
||||
existingList = append(existingList, correctOutcomeValue)
|
||||
traitPredictionInfoMap[newNumericTraitOutcomeInfo] = existingList
|
||||
}
|
||||
|
||||
exampleIndexString := helpers.ConvertIntToString(index+1)
|
||||
numberOfExamplesProgress := "Tested " + exampleIndexString + "/" + numberOfTrainingDatasString + " Examples"
|
||||
|
||||
progressDetailsBinding.Set(numberOfExamplesProgress)
|
||||
|
||||
newProgressFloat64 := float64(index)/float64(finalIndex)
|
||||
|
||||
progressPercentageBinding.Set(newProgressFloat64)
|
||||
}
|
||||
|
||||
// Now we construct the TraitAccuracyInfoMap
|
||||
|
||||
// This map stores the accuracy for each outcome
|
||||
traitPredictionAccuracyInfoMap := make(map[geneticPrediction.NumericTraitOutcomeInfo]geneticPrediction.NumericTraitPredictionAccuracyRangesMap)
|
||||
|
||||
for traitPredictionInfo, realOutcomesList := range traitPredictionInfoMap{
|
||||
|
||||
if (len(realOutcomesList) == 0){
|
||||
return false, nil, errors.New("traitPredictionInfoMap contains empty realOutcomesList.")
|
||||
}
|
||||
|
||||
// This is the predicted height value for this set of real outcomes
|
||||
predictionValue := traitPredictionInfo.OutcomeValue
|
||||
|
||||
// Map Structure: Accuracy Percentage (AP) -> Amount needed to deviate from prediction
|
||||
// for the value to be accurate (AP)% of the time
|
||||
newNumericTraitPredictionAccuracyRangesMap := make(map[int]float64)
|
||||
|
||||
rangeDistance := float64(0)
|
||||
|
||||
for {
|
||||
|
||||
rangeMin := predictionValue - rangeDistance
|
||||
rangeMax := predictionValue + rangeDistance
|
||||
|
||||
valuesInRangeList := make([]float64, 0)
|
||||
valuesOutOfRangeList := make([]float64, 0)
|
||||
|
||||
for _, outcomeValue := range realOutcomesList{
|
||||
|
||||
if (outcomeValue <= rangeMax && outcomeValue >= rangeMin){
|
||||
valuesInRangeList = append(valuesInRangeList, outcomeValue)
|
||||
} else {
|
||||
valuesOutOfRangeList = append(valuesOutOfRangeList, outcomeValue)
|
||||
}
|
||||
}
|
||||
|
||||
quantityOfValuesInRange := len(valuesInRangeList)
|
||||
totalQuantityOfValues := len(realOutcomesList)
|
||||
|
||||
proportionOfValuesInRange := float64(quantityOfValuesInRange)/float64(totalQuantityOfValues)
|
||||
percentageOfValuesInRange := proportionOfValuesInRange * 100
|
||||
|
||||
if (percentageOfValuesInRange >= 1){
|
||||
percentageOfValuesInRangeInt := int(percentageOfValuesInRange)
|
||||
|
||||
newNumericTraitPredictionAccuracyRangesMap[percentageOfValuesInRangeInt] = rangeDistance
|
||||
}
|
||||
if (quantityOfValuesInRange == totalQuantityOfValues){
|
||||
newNumericTraitPredictionAccuracyRangesMap[100] = rangeDistance
|
||||
break
|
||||
}
|
||||
|
||||
// Now we increase rangeDistance
|
||||
// We find the distance to the next closest item in the list that isn't already in our range
|
||||
|
||||
nearestValueDistance := float64(0)
|
||||
|
||||
for index, outcomeValue := range valuesOutOfRangeList{
|
||||
|
||||
distance := math.Abs(predictionValue - outcomeValue)
|
||||
|
||||
if (index == 0 || distance < nearestValueDistance){
|
||||
nearestValueDistance = distance
|
||||
}
|
||||
}
|
||||
|
||||
rangeDistance += nearestValueDistance
|
||||
}
|
||||
|
||||
traitPredictionAccuracyInfoMap[traitPredictionInfo] = newNumericTraitPredictionAccuracyRangesMap
|
||||
}
|
||||
|
||||
// Testing is complete.
|
||||
|
||||
// We save the info map as a file in the ModelAccuracies folder
|
||||
|
||||
fileBytes, err := geneticPrediction.EncodeNumericTraitPredictionAccuracyInfoMapToBytes(traitPredictionAccuracyInfoMap)
|
||||
if (err != nil) { return false, nil, err }
|
||||
|
||||
_, err = localFilesystem.CreateFolder("./ModelAccuracies")
|
||||
if (err != nil) { return false, nil, err }
|
||||
|
||||
modelAccuracyFilename := traitNameWithoutWhitespaces + "ModelAccuracy.gob"
|
||||
|
||||
err = localFilesystem.CreateOrOverwriteFile(fileBytes, "./ModelAccuracies/", modelAccuracyFilename)
|
||||
if (err != nil) { return false, nil, err }
|
||||
|
||||
progressPercentageBinding.Set(1)
|
||||
|
||||
return true, traitPredictionAccuracyInfoMap, nil
|
||||
}
|
||||
|
||||
processIsComplete, traitPredictionAccuracyInfoMap, err := testModel()
|
||||
if (err != nil){
|
||||
setErrorEncounteredPage(window, err, previousPage)
|
||||
return
|
||||
}
|
||||
if (processIsComplete == false){
|
||||
// User exited the page
|
||||
return
|
||||
}
|
||||
|
||||
setViewModelTestingNumericTraitResultsPage(window, traitName, traitPredictionAccuracyInfoMap, previousPage)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1680,6 +1911,11 @@ func setViewModelTestingDiscreteTraitResultsPage(window fyne.Window, traitName s
|
|||
traitObject, err := traits.GetTraitObject(traitName)
|
||||
if (err != nil) { return nil, err }
|
||||
|
||||
traitIsDiscreteOrNumeric := traitObject.DiscreteOrNumeric
|
||||
if (traitIsDiscreteOrNumeric != "Discrete"){
|
||||
return nil, errors.New("setViewModelTestingDiscreteTraitResultsPage called with non-discrete trait: " + traitName)
|
||||
}
|
||||
|
||||
outcomeNamesList := traitObject.OutcomesList
|
||||
|
||||
for _, outcomeName := range outcomeNamesList{
|
||||
|
@ -1801,6 +2037,175 @@ func setViewModelTestingDiscreteTraitResultsPage(window fyne.Window, traitName s
|
|||
}
|
||||
|
||||
|
||||
// This is a page to view the details of testing for a specific trait's model
|
||||
func setViewModelTestingNumericTraitResultsPage(window fyne.Window, traitName string, traitAccuracyInfoMap geneticPrediction.NumericTraitPredictionAccuracyInfoMap, exitPage func()){
|
||||
|
||||
title := getBoldLabelCentered("Numeric Trait Prediction Accuracy Details")
|
||||
|
||||
exitButton := getWidgetCentered(widget.NewButtonWithIcon("Exit", theme.CancelIcon(), exitPage))
|
||||
|
||||
description1 := getLabelCentered("The results of the prediction accuracy for this trait are below.")
|
||||
|
||||
traitNameTitle := widget.NewLabel("Trait Name:")
|
||||
traitNameLabel := getBoldLabel(traitName)
|
||||
traitNameRow := container.NewHBox(layout.NewSpacer(), traitNameTitle, traitNameLabel, layout.NewSpacer())
|
||||
|
||||
description2 := getLabelCentered("Each value is a range that the prediction must be widened by to be accurate X% of the time.")
|
||||
description3 := getLabelCentered("For example, for a height prediction to be accurate 90% of the time, allow a +/-10 cm range.")
|
||||
|
||||
getResultsGrid := func()(*fyne.Container, error){
|
||||
|
||||
probabilityOfTitle := getItalicLabelCentered("Probability Of")
|
||||
correctPredictionTitle := getItalicLabelCentered("Correct Prediction")
|
||||
|
||||
accuracyRangeTitle1 := getItalicLabelCentered("Accuracy Range")
|
||||
knownLociLabel_0to33 := getItalicLabelCentered("0-33% Known Loci")
|
||||
|
||||
accuracyRangeTitle2 := getItalicLabelCentered("Accuracy Range")
|
||||
knownLociLabel_34to66 := getItalicLabelCentered("34-66% Known Loci")
|
||||
|
||||
accuracyRangeTitle3 := getItalicLabelCentered("Accuracy Range")
|
||||
knownLociLabel_67to100 := getItalicLabelCentered("67-100% Known Loci")
|
||||
|
||||
probabilityOfCorrectPredictionColumn := container.NewVBox(probabilityOfTitle, correctPredictionTitle, widget.NewSeparator())
|
||||
accuracyRangeColumn_0to33 := container.NewVBox(accuracyRangeTitle1, knownLociLabel_0to33, widget.NewSeparator())
|
||||
accuracyRangeColumn_34to66 := container.NewVBox(accuracyRangeTitle2, knownLociLabel_34to66, widget.NewSeparator())
|
||||
accuracyRangeColumn_67to100 := container.NewVBox(accuracyRangeTitle3, knownLociLabel_67to100, widget.NewSeparator())
|
||||
|
||||
traitObject, err := traits.GetTraitObject(traitName)
|
||||
if (err != nil) { return nil, err }
|
||||
|
||||
traitIsDiscreteOrNumeric := traitObject.DiscreteOrNumeric
|
||||
if (traitIsDiscreteOrNumeric != "Numeric"){
|
||||
return nil, errors.New("setViewModelTestingNumericTraitResultsPage called with non-discrete trait: " + traitName)
|
||||
}
|
||||
|
||||
probabilityMinimumRange := 1
|
||||
|
||||
for {
|
||||
|
||||
if (probabilityMinimumRange == 100){
|
||||
break
|
||||
}
|
||||
|
||||
getProbabilityMaximumRange := func()int{
|
||||
|
||||
if (probabilityMinimumRange == 90){
|
||||
return 100
|
||||
}
|
||||
|
||||
probabilityMaximumRange := probabilityMinimumRange + 9
|
||||
|
||||
return probabilityMaximumRange
|
||||
}
|
||||
|
||||
probabilityMaximumRange := getProbabilityMaximumRange()
|
||||
|
||||
probabilityMinimumRangeString := helpers.ConvertIntToString(probabilityMinimumRange)
|
||||
probabilityMaximumRangeString := helpers.ConvertIntToString(probabilityMaximumRange)
|
||||
|
||||
probabilityOfCorrectPredictionRangeFormatted := probabilityMinimumRangeString + "% - " + probabilityMaximumRangeString + "%"
|
||||
|
||||
probabilityOfCorrectPredictionRangeLabel := getBoldLabelCentered(probabilityOfCorrectPredictionRangeFormatted)
|
||||
|
||||
// We use the below variables to sum up the accuracy distances so we can average them
|
||||
|
||||
predictionAccuracyDistancesSum_0to33 := float64(0)
|
||||
distancesCount_0to33 := 0
|
||||
|
||||
predictionAccuracyDistancesSum_34to66 := float64(0)
|
||||
distancesCount_34to66 := 0
|
||||
|
||||
predictionAccuracyDistancesSum_67to100 := float64(0)
|
||||
distancesCount_67to100 := 0
|
||||
|
||||
for traitOutcomeInfo, traitPredictionAccuracyRangesMap := range traitAccuracyInfoMap{
|
||||
|
||||
percentageOfLociTested := traitOutcomeInfo.PercentageOfLociTested
|
||||
|
||||
for percentageCorrect, distance := range traitPredictionAccuracyRangesMap{
|
||||
|
||||
if (percentageCorrect < probabilityMinimumRange || percentageCorrect > probabilityMaximumRange){
|
||||
continue
|
||||
}
|
||||
|
||||
if (percentageOfLociTested <= 33){
|
||||
|
||||
predictionAccuracyDistancesSum_0to33 += distance
|
||||
distancesCount_0to33 += 1
|
||||
|
||||
} else if (percentageOfLociTested > 33 && percentageOfLociTested <= 66){
|
||||
|
||||
predictionAccuracyDistancesSum_34to66 += distance
|
||||
distancesCount_34to66 += 1
|
||||
|
||||
} else {
|
||||
|
||||
predictionAccuracyDistancesSum_67to100 += distance
|
||||
distancesCount_67to100 += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getAverageAccuracyText := func(distancesSum float64, distancesCount int)string{
|
||||
if (distancesCount == 0){
|
||||
return "Unknown"
|
||||
}
|
||||
|
||||
averageDistance := distancesSum/float64(distancesCount)
|
||||
|
||||
averageDistanceString := helpers.ConvertFloat64ToStringRounded(averageDistance, 1)
|
||||
|
||||
//TODO: Retrieve units from traits package?
|
||||
result := "+/- " + averageDistanceString + " centimeters"
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
averageDistanceText_0to33 := getAverageAccuracyText(predictionAccuracyDistancesSum_0to33, distancesCount_0to33)
|
||||
averageDistanceText_34to66 := getAverageAccuracyText(predictionAccuracyDistancesSum_34to66, distancesCount_34to66)
|
||||
averageDistanceText_67to100 := getAverageAccuracyText(predictionAccuracyDistancesSum_67to100, distancesCount_67to100)
|
||||
|
||||
averageDistanceLabel_0to33 := getBoldLabelCentered(averageDistanceText_0to33)
|
||||
averageDistanceLabel_34to66 := getBoldLabelCentered(averageDistanceText_34to66)
|
||||
averageDistanceLabel_67to100 := getBoldLabelCentered(averageDistanceText_67to100)
|
||||
|
||||
probabilityOfCorrectPredictionColumn.Add(probabilityOfCorrectPredictionRangeLabel)
|
||||
accuracyRangeColumn_0to33.Add(averageDistanceLabel_0to33)
|
||||
accuracyRangeColumn_34to66.Add(averageDistanceLabel_34to66)
|
||||
accuracyRangeColumn_67to100.Add(averageDistanceLabel_67to100)
|
||||
|
||||
probabilityOfCorrectPredictionColumn.Add(widget.NewSeparator())
|
||||
accuracyRangeColumn_0to33.Add(widget.NewSeparator())
|
||||
accuracyRangeColumn_34to66.Add(widget.NewSeparator())
|
||||
accuracyRangeColumn_67to100.Add(widget.NewSeparator())
|
||||
|
||||
if (probabilityMinimumRange == 1){
|
||||
probabilityMinimumRange = 10
|
||||
} else {
|
||||
probabilityMinimumRange += 10
|
||||
}
|
||||
}
|
||||
|
||||
resultsGrid := container.NewHBox(layout.NewSpacer(), probabilityOfCorrectPredictionColumn, accuracyRangeColumn_0to33, accuracyRangeColumn_34to66, accuracyRangeColumn_67to100, layout.NewSpacer())
|
||||
|
||||
return resultsGrid, nil
|
||||
}
|
||||
|
||||
resultsGrid, err := getResultsGrid()
|
||||
if (err != nil){
|
||||
setErrorEncounteredPage(window, err, func(){setHomePage(window)})
|
||||
return
|
||||
}
|
||||
|
||||
page := container.NewVBox(title, exitButton, widget.NewSeparator(), description1, widget.NewSeparator(), traitNameRow, widget.NewSeparator(), description2, description3, widget.NewSeparator(), resultsGrid)
|
||||
|
||||
pageScrollable := container.NewVScroll(page)
|
||||
|
||||
window.SetContent(pageScrollable)
|
||||
}
|
||||
|
||||
|
||||
// This function returns a list of training data and testing data filepaths for a trait.
|
||||
//Outputs:
|
||||
// -[]string: Sorted list of training data filepaths
|
||||
|
@ -1808,7 +2213,7 @@ func setViewModelTestingDiscreteTraitResultsPage(window fyne.Window, traitName s
|
|||
// -error
|
||||
func getTrainingAndTestingDataFilepathLists(traitName string)([]string, []string, error){
|
||||
|
||||
if (traitName != "Eye Color" && traitName != "Lactose Tolerance"){
|
||||
if (traitName != "Eye Color" && traitName != "Lactose Tolerance" && traitName != "Height"){
|
||||
return nil, nil, errors.New("getTrainingAndTestingDataFilepathLists called with invalid traitName: " + traitName)
|
||||
}
|
||||
|
||||
|
@ -1845,11 +2250,14 @@ func getTrainingAndTestingDataFilepathLists(traitName string)([]string, []string
|
|||
|
||||
if (traitName == "Eye Color"){
|
||||
|
||||
return 112953, nil
|
||||
return 113648, nil
|
||||
|
||||
} else if (traitName == "Lactose Tolerance"){
|
||||
|
||||
return 24808, nil
|
||||
return 24872, nil
|
||||
} else if (traitName == "Height"){
|
||||
|
||||
return 92281, nil
|
||||
}
|
||||
|
||||
return 0, errors.New("Unknown traitName: " + traitName)
|
||||
|
@ -1862,7 +2270,7 @@ func getTrainingAndTestingDataFilepathLists(traitName string)([]string, []string
|
|||
|
||||
numberOfTrainingDataFilesString := helpers.ConvertIntToString(numberOfTrainingDataFiles)
|
||||
|
||||
return nil, nil, errors.New(traitName + " number of training datas is unexpected: " + numberOfTrainingDataFilesString)
|
||||
return nil, nil, errors.New(traitName + " quantity of training datas is unexpected: " + numberOfTrainingDataFilesString)
|
||||
}
|
||||
|
||||
// We sort the training data to be in a deterministically random order
|
||||
|
|
Loading…
Reference in a new issue