linear discriminant analysis assumptions about data 1 the input variables has a gaussian distribution 2 the variance calculated for each input variables by class grouping is the same 3 the mix of classes in your training set is representative of the problem learning the model the lda model requires the estimation of statistics from the training data 1 mean of each input value for each class 2 probability of an instance belong to each class 3 covariance for the input data for each class calculate the class means meanx 1n for i 1 to i n sumxi calculate the class probabilities py 0 county 0 county 0 county 1 py 1 county 1 county 0 county 1 calculate the variance we can calculate the variance for dataset in two steps 1 calculate the squared difference for each input variable from the group mean 2 calculate the mean of the squared difference squareddifference x meank 2 variance 1 countx countclasses for i 1 to i n sumsquareddifferencexi making predictions discriminantx x mean variance mean 2 2 variance lnprobability after calculating the discriminant value for each class the class with the largest discriminant value is taken as the prediction everlookneversee make a training dataset drawn from a gaussian distribution generate gaussian distribution instances basedon given mean and standard deviation param mean mean value of class param stddev value of standard deviation entered by usr or default value of it param instancecount instance number of class return a list containing generated values basedon given mean stddev and instancecount gaussiandistribution5 0 1 0 20 doctest normalizewhitespace 6 288184753155463 6 4494456086997705 5 066335808938262 4 235456349028368 3 9078267848958586 5 031334516831717 3 977896829989127 3 56317055489747 5 199311976483754 5 133374604658605 5 546468300338232 4 086029056264687 5 005005283626573 4 935258239627312 3 494170998739258 5 537997178661033 5 320711100998849 7 3891120432406865 5 202969177309964 4 855297691835079 make corresponding y flags to detecting classes generate y values for corresponding classes param classcount number of classesdata groupings in dataset param instancecount number of instances in class return corresponding values for data groupings in dataset ygenerator1 10 0 0 0 0 0 0 0 0 0 0 ygenerator2 5 10 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 ygenerator4 10 5 15 20 doctest normalizewhitespace 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 calculate the class means calculate given class mean param instancecount number of instances in class param items items that related to specific classdata grouping return calculated actual mean of considered class items gaussiandistribution5 0 1 0 20 calculatemeanlenitems items 5 011267842911003 the sum of all items divided by number of instances calculate the class probabilities calculate the probability that a given instance will belong to which class param instancecount number of instances in class param totalcount the number of all instances return value of probability for considered class calculateprobabilities20 60 0 3333333333333333 calculateprobabilities30 100 0 3 number of instances in specific class divided by number of all instances calculate the variance calculate the variance param items a list containing all itemsgaussian distribution of all classes param means a list containing real mean values of each class param totalcount the number of all instances return calculated variance for considered dataset items gaussiandistribution5 0 1 0 20 means 5 011267842911003 totalcount 20 calculatevarianceitems means totalcount 0 9618530973487491 iterate over number of elements in items for loop iterates over number of elements in inner layer of items appending squared differences to squareddiff list one divided by the number of all instances number of classes multiplied by sum of all squared differences making predictions this function predicts new indexesgroups for our data param xitems a list containing all itemsgaussian distribution of all classes param means a list containing real mean values of each class param variance calculated value of variance by calculatevariance function param probabilities a list containing all probabilities of classes return a list containing predicted y values xitems 6 288184753155463 6 4494456086997705 5 066335808938262 4 235456349028368 3 9078267848958586 5 031334516831717 3 977896829989127 3 56317055489747 5 199311976483754 5 133374604658605 5 546468300338232 4 086029056264687 5 005005283626573 4 935258239627312 3 494170998739258 5 537997178661033 5 320711100998849 7 3891120432406865 5 202969177309964 4 855297691835079 11 288184753155463 11 44944560869977 10 066335808938263 9 235456349028368 8 907826784895859 10 031334516831716 8 977896829989128 8 56317055489747 10 199311976483754 10 133374604658606 10 546468300338232 9 086029056264687 10 005005283626572 9 935258239627313 8 494170998739259 10 537997178661033 10 320711100998848 12 389112043240686 10 202969177309964 9 85529769183508 16 288184753155463 16 449445608699772 15 066335808938263 14 235456349028368 13 907826784895859 15 031334516831716 13 977896829989128 13 56317055489747 15 199311976483754 15 133374604658606 15 546468300338232 14 086029056264687 15 005005283626572 14 935258239627313 13 494170998739259 15 537997178661033 15 320711100998848 17 389112043240686 15 202969177309964 14 85529769183508 means 5 011267842911003 10 011267842911003 15 011267842911002 variance 0 9618530973487494 probabilities 0 3333333333333333 0 3333333333333333 0 3333333333333333 predictyvaluesxitems means variance probabilities doctest normalizewhitespace 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 an empty list to store generated discriminant values of all items in dataset for each class for loop iterates over number of elements in list for loop iterates over number of inner items of each element for loop iterates over number of classes we have in our dataset appending values of discriminants for each class to temp list appending discriminant values of each item to results list calculating accuracy calculate the value of accuracy basedon predictions param actualy a list containing initial y values generated by ygenerator function param predictedy a list containing predicted y values generated by predictyvalues function return percentage of accuracy actualy 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 predictedy 0 0 0 1 1 1 0 0 1 1 0 0 0 0 1 1 1 0 1 1 1 accuracyactualy predictedy 50 0 actualy 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 predictedy 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 accuracyactualy predictedy 100 0 iterate over one element of each list at a time zip mode prediction is correct if actual y value equals to predicted y value percentage of accuracy equals to number of correct predictions divided by number of all data and multiplied by 100 ask for user value and validate that it fulfill a condition inputtype user input expected type of value inputmsg message to show user in the screen errmsg message to show in the screen in case of error condition function that represents the condition that user input is valid default default value in case the user does not type anything return user s input main function this function starts execution phase while true print linear discriminant analysis center50 print 50 n printfirst of all we should specify the number of classes that printwe want to generate as training dataset trying to get number of classes nclasses validinput inputtypeint conditionlambda x x 0 inputmsgenter the number of classes data groupings errmsgnumber of classes should be positive print 100 trying to get the value of standard deviation stddev validinput inputtypefloat conditionlambda x x 0 inputmsg enter the value of standard deviation default value is 1 0 for all classes errmsgstandard deviation should not be negative default1 0 print 100 trying to get number of instances in classes and theirs means to generate dataset counts an empty list to store instance counts of classes in dataset for i in rangenclasses usercount validinput inputtypeint conditionlambda x x 0 inputmsgfenter the number of instances for classi1 errmsgnumber of instances should be positive counts appendusercount print 100 an empty list to store values of userentered means of classes usermeans for a in rangenclasses usermean validinput inputtypefloat inputmsgfenter the value of mean for classa1 errmsgthis is an invalid value usermeans appendusermean print 100 printstandard deviation stddev print out the number of instances in classes in separated line for i count in enumeratecounts 1 printfnumber of instances in classi is count print 100 print out mean values of classes separated line for i usermean in enumerateusermeans 1 printfmean of classi is usermean print 100 generating training dataset drawn from gaussian distribution x gaussiandistributionusermeansj stddev countsj for j in rangenclasses printgenerated normal distribution n x print 100 generating ys to detecting corresponding classes y ygeneratornclasses counts printgenerated corresponding ys n y print 100 calculating the value of actual mean for each class actualmeans calculatemeancountsk xk for k in rangenclasses for loop iterates over number of elements in actualmeans list and print out them in separated line for i actualmean in enumerateactualmeans 1 printfactualreal mean of classi is actualmean print 100 calculating the value of probabilities for each class probabilities calculateprobabilitiescountsi sumcounts for i in rangenclasses for loop iterates over number of elements in probabilities list and print out them in separated line for i probability in enumerateprobabilities 1 printfprobability of classi is probability print 100 calculating the values of variance for each class variance calculatevariancex actualmeans sumcounts printvariance variance print 100 predicting y values storing predicted y values in preindexes variable preindexes predictyvaluesx actualmeans variance probabilities print 100 calculating accuracy of the model printfaccuracy accuracyy preindexes print 100 print done center100 if inputpress any key to restart or q for quit strip lower q printn goodbye center100 n break systemcls if name nt else clear noqa s605 if name main main make a training dataset drawn from a gaussian distribution generate gaussian distribution instances based on given mean and standard deviation param mean mean value of class param std_dev value of standard deviation entered by usr or default value of it param instance_count instance number of class return a list containing generated values based on given mean std_dev and instance_count gaussian_distribution 5 0 1 0 20 doctest normalize_whitespace 6 288184753155463 6 4494456086997705 5 066335808938262 4 235456349028368 3 9078267848958586 5 031334516831717 3 977896829989127 3 56317055489747 5 199311976483754 5 133374604658605 5 546468300338232 4 086029056264687 5 005005283626573 4 935258239627312 3 494170998739258 5 537997178661033 5 320711100998849 7 3891120432406865 5 202969177309964 4 855297691835079 make corresponding y flags to detecting classes generate y values for corresponding classes param class_count number of classes data groupings in dataset param instance_count number of instances in class return corresponding values for data groupings in dataset y_generator 1 10 0 0 0 0 0 0 0 0 0 0 y_generator 2 5 10 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 y_generator 4 10 5 15 20 doctest normalize_whitespace 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 calculate the class means calculate given class mean param instance_count number of instances in class param items items that related to specific class data grouping return calculated actual mean of considered class items gaussian_distribution 5 0 1 0 20 calculate_mean len items items 5 011267842911003 the sum of all items divided by number of instances calculate the class probabilities calculate the probability that a given instance will belong to which class param instance_count number of instances in class param total_count the number of all instances return value of probability for considered class calculate_probabilities 20 60 0 3333333333333333 calculate_probabilities 30 100 0 3 number of instances in specific class divided by number of all instances calculate the variance calculate the variance param items a list containing all items gaussian distribution of all classes param means a list containing real mean values of each class param total_count the number of all instances return calculated variance for considered dataset items gaussian_distribution 5 0 1 0 20 means 5 011267842911003 total_count 20 calculate_variance items means total_count 0 9618530973487491 an empty list to store all squared differences iterate over number of elements in items for loop iterates over number of elements in inner layer of items appending squared differences to squared_diff list one divided by the number of all instances number of classes multiplied by sum of all squared differences number of classes in dataset making predictions this function predicts new indexes groups for our data param x_items a list containing all items gaussian distribution of all classes param means a list containing real mean values of each class param variance calculated value of variance by calculate_variance function param probabilities a list containing all probabilities of classes return a list containing predicted y values x_items 6 288184753155463 6 4494456086997705 5 066335808938262 4 235456349028368 3 9078267848958586 5 031334516831717 3 977896829989127 3 56317055489747 5 199311976483754 5 133374604658605 5 546468300338232 4 086029056264687 5 005005283626573 4 935258239627312 3 494170998739258 5 537997178661033 5 320711100998849 7 3891120432406865 5 202969177309964 4 855297691835079 11 288184753155463 11 44944560869977 10 066335808938263 9 235456349028368 8 907826784895859 10 031334516831716 8 977896829989128 8 56317055489747 10 199311976483754 10 133374604658606 10 546468300338232 9 086029056264687 10 005005283626572 9 935258239627313 8 494170998739259 10 537997178661033 10 320711100998848 12 389112043240686 10 202969177309964 9 85529769183508 16 288184753155463 16 449445608699772 15 066335808938263 14 235456349028368 13 907826784895859 15 031334516831716 13 977896829989128 13 56317055489747 15 199311976483754 15 133374604658606 15 546468300338232 14 086029056264687 15 005005283626572 14 935258239627313 13 494170998739259 15 537997178661033 15 320711100998848 17 389112043240686 15 202969177309964 14 85529769183508 means 5 011267842911003 10 011267842911003 15 011267842911002 variance 0 9618530973487494 probabilities 0 3333333333333333 0 3333333333333333 0 3333333333333333 predict_y_values x_items means variance probabilities doctest normalize_whitespace 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 an empty list to store generated discriminant values of all items in dataset for each class for loop iterates over number of elements in list for loop iterates over number of inner items of each element to store all discriminant values of each item as a list for loop iterates over number of classes we have in our dataset appending values of discriminants for each class to temp list appending discriminant values of each item to results list calculating accuracy calculate the value of accuracy based on predictions param actual_y a list containing initial y values generated by y_generator function param predicted_y a list containing predicted y values generated by predict_y_values function return percentage of accuracy actual_y 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 predicted_y 0 0 0 1 1 1 0 0 1 1 0 0 0 0 1 1 1 0 1 1 1 accuracy actual_y predicted_y 50 0 actual_y 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 predicted_y 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 accuracy actual_y predicted_y 100 0 iterate over one element of each list at a time zip mode prediction is correct if actual y value equals to predicted y value percentage of accuracy equals to number of correct predictions divided by number of all data and multiplied by 100 usually float or int ask for user value and validate that it fulfill a condition input_type user input expected type of value input_msg message to show user in the screen err_msg message to show in the screen in case of error condition function that represents the condition that user input is valid default default value in case the user does not type anything return user s input main function this function starts execution phase trying to get number of classes trying to get the value of standard deviation trying to get number of instances in classes and theirs means to generate dataset an empty list to store instance counts of classes in dataset an empty list to store values of user entered means of classes print out the number of instances in classes in separated line print out mean values of classes separated line generating training dataset drawn from gaussian distribution generating ys to detecting corresponding classes calculating the value of actual mean for each class for loop iterates over number of elements in actual_means list and print out them in separated line calculating the value of probabilities for each class for loop iterates over number of elements in probabilities list and print out them in separated line calculating the values of variance for each class predicting y values storing predicted y values in pre_indexes variable calculating accuracy of the model noqa s605
from collections.abc import Callable
from math import log
from os import name, system
from random import gauss, seed
from typing import TypeVar



def gaussian_distribution(mean: float, std_dev: float, instance_count: int) -> list:
    
    seed(1)
    return [gauss(mean, std_dev) for _ in range(instance_count)]



def y_generator(class_count: int, instance_count: list) -> list:
    

    return [k for k in range(class_count) for _ in range(instance_count[k])]



def calculate_mean(instance_count: int, items: list) -> float:
    
    
    return sum(items) / instance_count



def calculate_probabilities(instance_count: int, total_count: int) -> float:
    
    
    return instance_count / total_count



def calculate_variance(items: list, means: list, total_count: int) -> float:
    
    squared_diff = []  
    
    for i in range(len(items)):
        
        for j in range(len(items[i])):
            
            squared_diff.append((items[i][j] - means[i]) ** 2)

    
    
    n_classes = len(means)  
    return 1 / (total_count - n_classes) * sum(squared_diff)



def predict_y_values(
    x_items: list, means: list, variance: float, probabilities: list
) -> list:
    
    
    
    results = []
    
    for i in range(len(x_items)):
        
        for j in range(len(x_items[i])):
            temp = []  
            
            for k in range(len(x_items)):
                
                temp.append(
                    x_items[i][j] * (means[k] / variance)
                    - (means[k] ** 2 / (2 * variance))
                    + log(probabilities[k])
                )
            
            results.append(temp)

    return [result.index(max(result)) for result in results]



def accuracy(actual_y: list, predicted_y: list) -> float:
    
    
    
    correct = sum(1 for i, j in zip(actual_y, predicted_y) if i == j)
    
    
    return (correct / len(actual_y)) * 100


num = TypeVar("num")


def valid_input(
    input_type: Callable[[object], num],  
    input_msg: str,
    err_msg: str,
    condition: Callable[[num], bool] = lambda x: True,
    default: str | None = None,
) -> num:
    
    while True:
        try:
            user_input = input_type(input(input_msg).strip() or default)
            if condition(user_input):
                return user_input
            else:
                print(f"{user_input}: {err_msg}")
                continue
        except ValueError:
            print(
                f"{user_input}: Incorrect input type, expected {input_type.__name__!r}"
            )



def main():
    
    while True:
        print(" Linear Discriminant Analysis ".center(50, "*"))
        print("*" * 50, "\n")
        print("First of all we should specify the number of classes that")
        print("we want to generate as training dataset")
        
        n_classes = valid_input(
            input_type=int,
            condition=lambda x: x > 0,
            input_msg="Enter the number of classes (Data Groupings): ",
            err_msg="Number of classes should be positive!",
        )

        print("-" * 100)

        
        std_dev = valid_input(
            input_type=float,
            condition=lambda x: x >= 0,
            input_msg=(
                "Enter the value of standard deviation"
                "(Default value is 1.0 for all classes): "
            ),
            err_msg="Standard deviation should not be negative!",
            default="1.0",
        )

        print("-" * 100)

        
        
        counts = []  
        for i in range(n_classes):
            user_count = valid_input(
                input_type=int,
                condition=lambda x: x > 0,
                input_msg=(f"Enter The number of instances for class_{i+1}: "),
                err_msg="Number of instances should be positive!",
            )
            counts.append(user_count)
        print("-" * 100)

        
        user_means = []
        for a in range(n_classes):
            user_mean = valid_input(
                input_type=float,
                input_msg=(f"Enter the value of mean for class_{a+1}: "),
                err_msg="This is an invalid value.",
            )
            user_means.append(user_mean)
        print("-" * 100)

        print("Standard deviation: ", std_dev)
        
        for i, count in enumerate(counts, 1):
            print(f"Number of instances in class_{i} is: {count}")
        print("-" * 100)

        
        for i, user_mean in enumerate(user_means, 1):
            print(f"Mean of class_{i} is: {user_mean}")
        print("-" * 100)

        
        x = [
            gaussian_distribution(user_means[j], std_dev, counts[j])
            for j in range(n_classes)
        ]
        print("Generated Normal Distribution: \n", x)
        print("-" * 100)

        
        y = y_generator(n_classes, counts)
        print("Generated Corresponding Ys: \n", y)
        print("-" * 100)

        
        actual_means = [calculate_mean(counts[k], x[k]) for k in range(n_classes)]
        
        
        for i, actual_mean in enumerate(actual_means, 1):
            print(f"Actual(Real) mean of class_{i} is: {actual_mean}")
        print("-" * 100)

        
        probabilities = [
            calculate_probabilities(counts[i], sum(counts)) for i in range(n_classes)
        ]

        
        
        for i, probability in enumerate(probabilities, 1):
            print(f"Probability of class_{i} is: {probability}")
        print("-" * 100)

        
        variance = calculate_variance(x, actual_means, sum(counts))
        print("Variance: ", variance)
        print("-" * 100)

        
        
        pre_indexes = predict_y_values(x, actual_means, variance, probabilities)
        print("-" * 100)

        
        print(f"Accuracy: {accuracy(y, pre_indexes)}")
        print("-" * 100)
        print(" DONE ".center(100, "+"))

        if input("Press any key to restart or 'q' for quit: ").strip().lower() == "q":
            print("\n" + "GoodBye!".center(100, "-") + "\n")
            break
        system("cls" if name == "nt" else "clear")  


if __name__ == "__main__":
    main()