text:
import sys
import uuid

import numpy as np
import sympy as sp
from scipy.optimize import least_squares

from collections import OrderedDict
import re
import logging
import copy

from app.model import objects
from app.util import report

INDEPENDENT_VARIABLE = "t"

DELTA = "\u0394"
LAMBDA = "\u03BB"
BETA = "\u03B2"
SIGMA = "\u03C3"
ALPHA = "\u03B1"
PHI = "\u03A6"
PI = "\u03C0"
NAUGHT = "\u2080"
NU = "\u03BD"
SUB_T = "\u209C"
SUB_L = "\u2097"
CHI = "\u03C7"
SQUARE = "\u00B2"

SIMPLE_EXPONENTIAL = f"a*exp(-{LAMBDA}*t)"
STRETCHED_EXPONENTIAL = f"a*exp(-({LAMBDA}*t)^{BETA})"
SIMPLE_GAUSSIAN = f"a*exp(-1/2*({SIGMA}*t)^2)"
GAUSSIAN_KT = f"a*(1/3 + 2/3*(1 - ({SIGMA}*t)^2)*exp(-1/2*({SIGMA}*t)^2))"
LORENTZIAN_KT = f"a*(1/3 + 2/3*(1 - {LAMBDA}*t)*exp(-{LAMBDA}*t))"
COMBINED_KT = f"a*(1/3 + 2/3*(1-{SIGMA}^2*t^2-{LAMBDA}*t)*exp(-{SIGMA}^2*t^2/2-{LAMBDA}*t))"
STRETCHED_KT = f"a*(1/3 + 2/3*(1-({SIGMA}*t)^{BETA})*exp(-({SIGMA}*t)^{BETA}/{BETA}))"
DAMPED_COSINE = f"a*cos(2*{PI}*{NU}*t + {PI}*{PHI}/180)*exp(-{BETA}*t)"
INTERNAL_COSINE = f"a*(f*cos(2*{PI}*{NU}*t + {PI}*{PHI}/180)*exp(-{LAMBDA}{SUB_T}*t) + (1 - f)*exp(-{LAMBDA}{SUB_L}*t))"
BESSEL = f"a*j{NAUGHT}(2*{PI}*{NU}*t + {PI}*{PHI}/180)"
INTERNAL_BESSEL = f"a*(f*j{NAUGHT}(2*{PI}*{NU}*t + {PI}*{PHI}/180)*exp(-{LAMBDA}{SUB_T}*t) + (1-f)*exp(-{LAMBDA}{SUB_L}*t))"

EQUATION_DICTIONARY = {
    "Simple Exponential": SIMPLE_EXPONENTIAL,
    "Stretched Exponential": STRETCHED_EXPONENTIAL,
    "Simple Gaussian": SIMPLE_GAUSSIAN,
    "Gaussian KT": GAUSSIAN_KT,
    "Lorentzian KT": LORENTZIAN_KT,
    "Combined KT": COMBINED_KT,
    "Stretched KT": STRETCHED_KT,
    "Damped Cosine": DAMPED_COSINE,
    "Internal Cosine": INTERNAL_COSINE,
    "Bessel": BESSEL,
    "Internal Bessel": INTERNAL_BESSEL
}

DEFAULT_VALUES = {
    "Simple Exponential": {'a': 0.25, LAMBDA: 0.1},
    "Stretched Exponential": {'a': 0.25, LAMBDA: 0.1, BETA: 1},
    "Simple Gaussian": {'a': 0.25, SIGMA: 0.1},
    "Gaussian KT": {'a': 0.25, SIGMA: 1.0},
    "Lorentzian KT": {'a': 0.25, LAMBDA: 1},
    "Combined KT": {'a': 0.25, SIGMA: 1, LAMBDA: 1},
    "Stretched KT": {'a': 0.25, SIGMA: 1, BETA: 1},
    "Damped Cosine": {'a': 0.25, PHI: 0, BETA: 1, NU: 1},
    "Internal Cosine": {'a': 0.25, 'f': 0.667, NU: 1, PHI: 0, f'{LAMBDA}{SUB_T}': 1, f'{LAMBDA}{SUB_L}': 0.1},
    "Bessel": {'a': 0.25, NU: 1, PHI: 0},
    "Internal Bessel": {'a': 0.25, 'f': 0.667, NU: 1, PHI: 0, f'{LAMBDA}{SUB_T}': 1, f'{LAMBDA}{SUB_L}': 0.1}
}

USER_EQUATION_DICTIONARY = {}

class FitParameter:
    def __init__(self, symbol, value, lower, upper, is_global, is_fixed, is_run_specific=False, output=None, uncertainty=None):
        self.uncertainty = uncertainty
        self.output = output
        self.upper = upper
        self.lower = lower
        self.value = value
        self.symbol = symbol
        self.is_fixed = is_fixed
        self.is_run_specific = is_run_specific
        self.is_global = is_global

    def __copy__(self):
        return FitParameter(self.symbol, self.value, self.lower, self.upper, self.is_global, self.is_fixed, self.is_run_specific, self.output, self.uncertainty)

    def __eq__(self, other):
        return other.symbol == self.symbol \
               and other.value == self.value \
               and other.lower == self.lower \
               and other.upper == self.upper \
               and other.is_global == self.is_global \
               and other.is_fixed == self.is_fixed \
               and other.is_run_specific == self.is_run_specific \
               and other.output == self.output \
               and other.uncertainty == self.uncertainty

    def __str__(self):
        return '{}={}'.format(self.symbol, self.value)

    def __repr__(self):
        return 'FitParameter({}, {}, {}, {}, {}, {}, {})'.format(self.symbol, self.value, self.lower, self.upper,
                                                                 self.is_global, self.is_fixed, self.is_run_specific)

    def get_value(self):
        return float(self.value)

class FitExpression:
    def __init__(self, expression_string, variables=None):
        self.__expression_string = expression_string

        if variables is None:
            variables = parse(self.__expression_string)

        self.__variables = variables
        self.__expression = lambdify(self.__expression_string, variables)
        self.safe = True

    def __getstate__(self):
        return self.__expression_string, self.__variables, self.safe

    def __setstate__(self, state):
        self.__expression_string = state[0]
        self.__variables = state[1]
        self.__expression = lambdify(self.__expression_string, self.__variables)
        self.safe = state[2]

    def __eq__(self, other):
        return str(other) == self.__expression_string and other.__variables == self.__variables

    def __str__(self):
        return self.__expression_string

    def __repr__(self):
        return f'FitExpression({self.__expression_string}, {self.__expression}, {self.safe})'

    def __call__(self, *args, **kwargs):
        # The length of this function is due to the fact I have trust issues.
        if not self.safe:
            return self.__expression(*args, **{_replace_unsupported_unicode_characters(k): v for k, v in kwargs.items()})

        # We need to cast it to a complex type in order to avoid errors where we are raising a negative
        # number to a fractional power (which would result in an array of Nan's usually). We do the calculation
        # and then take the real component below.
        try:
            if INDEPENDENT_VARIABLE in kwargs:
                time_array = np.array(kwargs[INDEPENDENT_VARIABLE], dtype=complex)
            else:
                time_array = np.array(args[0], dtype=complex)
        except ValueError:
            raise InvalidFitArgumentsError("First argument should be an array of numbers or a number.")

        pars = {}
        unnamed_pars = []
        if len(args) > 1:
            for arg in args[1:]:
                if isinstance(arg, FitParameter):
                    pars[arg.symbol] = arg.value
                else:
                    try:
                        unnamed_pars.append(float(arg))
                    except ValueError:
                        raise InvalidFitArgumentsError("Every parameter after the array should be of type FitParameter or a number.")
        for k, v in kwargs.items():
            if k != INDEPENDENT_VARIABLE:
                pars[_replace_unsupported_unicode_characters(k)] = v

        try:
            return self.__expression(time_array, *unnamed_pars, **pars).real
        except TypeError as e:
            if ALPHA in pars.keys():
                pars.pop(ALPHA)
                return self.__expression(time_array, *unnamed_pars, **pars).real
            raise InvalidFitArgumentsError("Bad arguments passed to fit expression.") from e

class FitConfig:
    """

    IMPORTANT NOTE : All of the get function use a for loop on self.parameters.items(). Always do this to keep the order
    of the items we get out consistent. The least squares method we use doesn't let us specify the value or lower bound
    etc of a symbol except by passing in arrays ordered the same as the array of symbols.

    """

    def __init__(self):
        self.expression = ''
        self.parameters = {}
        self.titles = {}
        self.data = OrderedDict()  # Important so we can specify order of runs to be fitted for batch fit.
        self.batch = True
        self.flags = 0

    def __repr__(self):
        return f"FitConfig({self.expression}, {self.parameters}, {self.titles}, {self.batch}, {self.flags}, {self.data})"

    def set_flags(self, *flags):
        self.flags = 0
        for flag in flags:
            self.flags = self.flags | flag

    def is_global(self):
        is_global = False
        for par_dict in self.parameters.values():
            for par in par_dict.values():
                is_global = is_global or par.is_global
        return is_global

    def is_batch(self):
        return self.batch

    def is_plus(self):
        return self.is_batch() and self.is_global()

    def get_symbols_for_run(self, run_id, is_fixed=None, is_global=None):
        symbols = []
        for symbol, parameter in self.parameters[run_id].items():
            if (is_fixed is None or (is_fixed == parameter.is_fixed)) and \
                    (is_global is None or (is_global == parameter.is_global)):
                symbols.append(symbol)

        return symbols

    def get_values_for_run(self, run_id, is_fixed=None, is_global=None):
        values = []

        for _, parameter in self.parameters[run_id].items():
            if (is_fixed is None or (is_fixed == parameter.is_fixed)) and \
                    (is_global is None or (is_global == parameter.is_global)):
                values.append(parameter.get_value())

        return np.array(values, dtype=float)

    def get_lower_values_for_run(self, run_id, is_fixed=None, is_global=None):
        values = []

        for _, parameter in self.parameters[run_id].items():
            if (is_fixed is None or (is_fixed == parameter.is_fixed)) and \
                    (is_global is None or (is_global == parameter.is_global)):
                values.append(parameter.lower)

        return np.array(values, dtype=float)

    def get_upper_values_for_run(self, run_id, is_fixed=None, is_global=None):
        values = []

        for _, parameter in self.parameters[run_id].items():
            if (is_fixed is None or (is_fixed == parameter.is_fixed)) and \
                    (is_global is None or (is_global == parameter.is_global)):
                values.append(parameter.upper)

        return np.array(values, dtype=float)

    def get_adjusted_global_symbols(self):
        symbols = []
        for i, run_id in enumerate(self.parameters.keys()):
            for symbol, parameter in self.parameters[run_id].items():
                if parameter.is_fixed:
                    continue
                if parameter.is_global and i == 0:
                    symbols.append(symbol)
                elif not parameter.is_global:
                    symbols.append(symbol + _shortened_run_id(run_id))

        return symbols

    def get_adjusted_global_values(self):
        values = []
        for i, run_id in enumerate(self.parameters.keys()):
            for _, parameter in self.parameters[run_id].items():
                if parameter.is_fixed:
                    continue
                if (parameter.is_global and i == 0) or (not parameter.is_global):
                    values.append(float(parameter.get_value()))

        return np.array(values, dtype=float)

    def get_adjusted_global_lowers(self):
        lowers = []
        for i, run_id in enumerate(self.parameters.keys()):
            for _, parameter in self.parameters[run_id].items():
                if parameter.is_fixed:
                    continue
                if (parameter.is_global and i == 0) or (not parameter.is_global):
                    lowers.append(float(parameter.lower))

        return np.array(lowers, dtype=float)

    def get_adjusted_global_uppers(self):
        uppers = []
        for i, run_id in enumerate(self.parameters.keys()):
            for _, parameter in self.parameters[run_id].items():
                if parameter.is_fixed:
                    continue
                if (parameter.is_global and i == 0) or (not parameter.is_global):
                    uppers.append(float(parameter.upper))

        return np.array(uppers, dtype=float)

    def set_outputs(self, run_id, symbol, output, uncertainty):
        self.parameters[run_id][symbol].value = output
        self.parameters[run_id][symbol].output = output
        self.parameters[run_id][symbol].fixed_value = output
        self.parameters[run_id][symbol].uncertainty = uncertainty

class FitEngine:
    def __init__(self):
        self.__logger = logging.getLogger(__name__)

    def fit(self, config: FitConfig) -> objects.FitDataset:
        report.log_debug("Config passed to FitEngine: {}".format(str(config)))

        if len(set([len(asymmetry) for asymmetry in config.data.values()])) != 1:
            raise ValueError("Must have one or more datasets all of equal length to fit")

        if config.expression == '':
            raise ValueError("Empty function attribute")

        if config.is_plus():
            return self._fit_global_plus(config)
        elif config.is_global():
            return self._fit_global(config)
        elif config.is_batch():
            return self._fit_batch(config)
        else:
            return self._fit_non_global(config)

    def _fit_global_plus(self, config: FitConfig):
        dataset = self._fit_batch(config)
        # FIXME how will truly run dependent fixed values affect this line below?
        mean_values = {symbol: np.mean([f.parameters[symbol].output for f in dataset.fits.values()]) for symbol in
                       list(dataset.fits.values())[0].parameters.keys()}
        for _, parameters in config.parameters.items():
            for symbol, par in parameters.items():
                if par.is_global:
                    par.value = mean_values[par.symbol]

        dataset = self._fit_global(config)
        dataset.flags |= objects.FitDataset.Flags.GLOBAL_PLUS

        return dataset

    def _fit_batch(self, config: FitConfig):
        dataset = objects.FitDataset()
        dataset.flags |= objects.FitDataset.Flags.BATCH

        for run_id, (time, asymmetry, uncertainty, meta) in config.data.items():
            # We create a separate lambda expression for each run in case they set separate run dependant fixed values.
            function = alpha_correction(config.expression)

            # Replace the symbols with fixed values with their actual values. This way they don't throw off uncertainty.
            fixed_symbols = config.get_symbols_for_run(run_id, is_fixed=True)
            fixed_values = config.get_values_for_run(run_id, is_fixed=True)
            function = FitEngine._replace_fixed(function, fixed_symbols, fixed_values)

            # Create a residual expression for the least squares fit
            lambda_expression = FitExpression(function, variables=config.get_symbols_for_run(run_id, is_fixed=False))
            residual = FitEngine._residual(lambda_expression)

            guesses = config.get_values_for_run(run_id, is_fixed=False)
            lowers = config.get_lower_values_for_run(run_id, is_fixed=False)
            uppers = config.get_upper_values_for_run(run_id, is_fixed=False)

            # 6) Perform a least squares fit
            try:
                opt = least_squares(residual, guesses, bounds=[lowers, uppers],
                                    args=(time, asymmetry, uncertainty))
            except Exception as e:
                raise FittingError("An error occurred running the least squares fit.") from e

            try:
                unc, chi_sq = get_std_unc(opt, asymmetry)
            except (np.linalg.LinAlgError, NameError):  # Fit did not converge
                unc = [-1 for _ in opt.x]
                chi_sq = 0.0

            # 7) Replace initial values with fitted values for unfixed variables
            for i, symbol in enumerate(config.get_symbols_for_run(run_id, is_fixed=False)):
                for o_run_id in config.data.keys():
                    config.set_outputs(o_run_id, symbol, opt.x[i], unc[i])

            for symbol, value in zip(fixed_symbols, fixed_values):
                for o_run_id in config.data.keys():
                    config.set_outputs(o_run_id, symbol, value, 0)

            # 9) Fill in all values for our new fit object
            new_fit = objects.Fit(copy.deepcopy(config.parameters[run_id]), config.expression, config.titles[run_id], run_id, meta, asymmetry, goodness=chi_sq)

            # 10) Add fit to our dataset
            dataset.fits[run_id] = new_fit

        # 11) Attach fit spec options and function to dataset (mostly for debugging purposes)
        dataset.expression = config.expression
        return dataset

    def _fit_global(self, config: FitConfig):
        # The methods from config will get the adjusted symbols, values, etc. in the same order every time for a global
        #   fit (which is necessary because we can't specify which value belongs to which symbol in the scipy least
        #   squares function.
        guesses = config.get_adjusted_global_values()
        lowers = config.get_adjusted_global_lowers()
        uppers = config.get_adjusted_global_uppers()

        # We are essentially doing one big fit where you will have global and local (local to a specific section of the
        #   concatenated asymmetry) parameters being refined.
        concatenated_asymmetry = np.array([])
        concatenated_uncertainty = np.array([])
        concatenated_time = np.array([])
        for _, (time, asymmetry, uncertainty, _) in config.data.items():
            concatenated_asymmetry = np.concatenate((concatenated_asymmetry, asymmetry))
            concatenated_uncertainty = np.concatenate((concatenated_uncertainty, uncertainty))
            concatenated_time = np.concatenate((concatenated_time, time))

        # Create a single global lambda function for all the datasets. Essentially, it acts like a
        #    stepwise function, once you pass into the asymmetry of another dataset, a separate function
        #    in the lambda will start being used. See its definition for more on why.
        global_lambda_expression = self._lambdify_global(config)
        residual = FitEngine._residual(global_lambda_expression)

        # Run a lease squares fit with the global lambda and concatenated datasets
        try:
            opt = least_squares(residual, guesses, bounds=[lowers, uppers],
                                args=(concatenated_time, concatenated_asymmetry, concatenated_uncertainty))
        except Exception as e:
            raise FittingError("An error occurred running the least squares fit.") from e

        try:
            unc, chi_sq = get_std_unc(opt, concatenated_asymmetry)
        except (np.linalg.LinAlgError, NameError):  # Fit did not converge
            unc = [-1 for _ in opt.x]
            chi_sq = 0.0

        # Assemble the Fit object, first by updating the parameters in the config with the outputs from
        #   the fit, as well as adding a FitExpression object that can be called.
        dataset = objects.FitDataset()
        dataset.flags |= objects.FitDataset.Flags.GLOBAL

        values = {}
        uncertainties = {}
        for i, symbol in enumerate(config.get_adjusted_global_symbols()):
            values[symbol] = opt.x[i]
            uncertainties[symbol] = unc[i]

        for run_id, (_, asymmetry, _, meta) in config.data.items():
            for symbol, parameter in config.parameters[run_id].items():
                if parameter.is_fixed:
                    continue

                if not parameter.is_global:
                    config.set_outputs(run_id, symbol,
                                       values[symbol + _shortened_run_id(run_id)],
                                       uncertainties[symbol + _shortened_run_id(run_id)])
                else:
                    config.set_outputs(run_id, symbol, values[symbol], uncertainties[symbol])

            fixed_symbols = config.get_symbols_for_run(run_id, is_fixed=True)
            fixed_values = config.get_values_for_run(run_id, is_fixed=True)
            for symbol, value in zip(fixed_symbols, fixed_values):
                config.set_outputs(run_id, symbol, value, 0)

            new_fit = objects.Fit(config.parameters[run_id], config.expression, config.titles[run_id], run_id, meta, asymmetry, goodness=chi_sq)

            dataset.fits[run_id] = new_fit

        dataset.expression = config.expression

        return dataset

    def _fit_non_global(self, config) -> objects.FitDataset:
        dataset = objects.FitDataset()

        for run_id, (time, asymmetry,  uncertainty, meta) in config.data.items():
            # We create a separate lambda expression for each run in case they set separate run dependant fixed values.
            function = alpha_correction(config.expression)

            # Replace the symbols with fixed values with their actual values. This way they don't throw off uncertainty.
            fixed_symbols = config.get_symbols_for_run(run_id, is_fixed=True)
            fixed_values = config.get_values_for_run(run_id, is_fixed=True)
            function = FitEngine._replace_fixed(function, fixed_symbols, fixed_values)

            # Create a residual expression for the least squares fit
            lambda_expression = FitExpression(function, variables=config.get_symbols_for_run(run_id, is_fixed=False))
            residual = FitEngine._residual(lambda_expression)

            guesses = config.get_values_for_run(run_id, is_fixed=False)
            lowers = config.get_lower_values_for_run(run_id, is_fixed=False)
            uppers = config.get_upper_values_for_run(run_id, is_fixed=False)

            # 6) Perform a least squares fit
            try:
                opt = least_squares(residual, guesses, bounds=[lowers, uppers],
                                    args=(time, asymmetry, uncertainty))
            except Exception as e:
                raise FittingError("An error occurred running the least squares fit.") from e

            try:
                unc, chi_sq = get_std_unc(opt, asymmetry)
            except (np.linalg.LinAlgError, NameError):  # Fit did not converge
                unc = [-1 for _ in opt.x]
                chi_sq = 0.0

            # 7) Replace initial values with fitted values for unfixed variables
            for i, symbol in enumerate(config.get_symbols_for_run(run_id, is_fixed=False)):
                config.set_outputs(run_id, symbol, opt.x[i], unc[i])

            for symbol, value in zip(fixed_symbols, fixed_values):
                config.set_outputs(run_id, symbol, value, 0)

            # 9) Fill in all values for our new fit object
            new_fit = objects.Fit(config.parameters[run_id], config.expression, config.titles[run_id], run_id, meta, asymmetry, goodness=chi_sq)

            # 10) Add fit to our dataset
            dataset.fits[run_id] = new_fit

        # 11) Attach fit spec options and function to dataset (mostly for debugging purposes)
        dataset.expression = config.expression
        return dataset

    @staticmethod
    def _lambdify_global(config: FitConfig):
        """ Creates a single lambda expression for a global fit.

            In a global fit we will have some parameters that are fit locally, for a single run, and some that are fit
            across all runs. To get this to work with a single residual function for scipy least_squares we essentially
            create a piecewise function where we apply different lambdas to different sections of the concatenated
            arrays.

            Example:
                Fitting three asymmetries.

                a * t + b / c
                a, b are local parameters
                c is a global parameter

                arr = a1 | a2 | a3

                The lambda that is created would essentially look like this.

                f(arr, pars) = {
                    a1 => a1 * t + b1 / c
                    a2 => a2 * t + b2 / c
                    a3 => a3 * t + b3 / c

                So you can see we append some unique value (a piece of the run id) to end of the local parameters so
                they are only fit to that specific section while we use the global parameter throughout.
        """

        function = alpha_correction(config.expression)

        lambdas = []
        lengths = []
        ends = []
        starts = []
        number_runs = len(config.data.items())

        for i, (run_id, asymmetry) in enumerate(config.data.items()):
            fixed_symbols = config.get_symbols_for_run(run_id, is_fixed=True)
            fixed_values = config.get_values_for_run(run_id, is_fixed=True)
            new_function = FitEngine._replace_fixed(function, fixed_symbols, fixed_values)

            for symbol in config.get_symbols_for_run(run_id, is_fixed=False, is_global=False):
                new_function = FitEngine._replace_var_with(new_function, symbol, symbol + _shortened_run_id(run_id))

            new_lambda_expression = FitExpression(new_function, variables=config.get_adjusted_global_symbols())
            new_lambda_expression.safe = False  # No type checks, speeds up the fit dramatically.
            lambdas.append(new_lambda_expression)

            starts.append(sum(lengths))
            lengths.append(int(len(asymmetry[0])))
            ends.append(starts[-1] + lengths[-1])

        def _lambda_expression(arr, *pars, **kwargs):
            values = [lambdas[j](arr[starts[j]: ends[j]], *pars, **kwargs) for j in range(number_runs)]
            return np.hstack(values)

        return _lambda_expression

    @staticmethod
    def _residual(lambda_expression):
        def residual(pars, x, y_data, dy_data):
            y_calc = lambda_expression(np.array(x, dtype=complex), *pars).real
            return np.divide(np.subtract(y_data, y_calc), dy_data)
        return residual

    @staticmethod
    def _replace_fixed(function, symbols, values):
        for symbol, value in zip(symbols, values):
            function = re.sub(r'(?<=\b){}(?=\b)'.format(symbol), str(value), function)
        return function

    @staticmethod
    def _replace_var_with(function, old_symbol, new_symbol):
        return re.sub(r'(?<=\b){}(?=\b)'.format(old_symbol), str(new_symbol), function)

def get_std_unc(result, data, error=None, num_constraints=0):
    """Return the standard uncertainty of refined parameters.
    This method is based on the scipy.optimize.least_squares routine.

    Args:
        result: Output from scipy.optimize.least_squares routine
        data (numpy array): The data against which the fit is performed
        error (numpy array): Experimental uncertainties on the data points
            (set to unity if not provided)
        num_constraints (int): Number of constraints used in the model
    Returns:
        p_unc (numpy array): standard uncertainties of the refined parameters.
        chisq (float): Value of chi^2 for the fit.
    """

    if error is None:
        error = np.ones_like(data)

    weights = 1.0 / error ** 2
    rw = np.sqrt((result.fun ** 2).sum() / (data ** 2 * weights).sum())
    num_params = len(result.x)
    r_exp = np.sqrt((data.shape[0] - num_params + num_constraints) / (data ** 2 * weights).sum())
    j = result.jac
    jac = np.dot(j.transpose(), j)
    cov = np.linalg.inv(jac) * rw ** 2 / r_exp ** 2
    p_unc = np.sqrt(cov.diagonal())
    chi_sq = rw ** 2 / r_exp ** 2

    return p_unc, chi_sq

def parse(s: str) -> list:
    f""" Takes in an expression as a string and returns a set of the free variables.

    Note that this means it will not return {INDEPENDENT_VARIABLE} in the set. This method also expects
    that the expression will NOT be in the form 'a(t) = x*t' but simply 'x*t'.

    Parameters
    ----------
        s : str
            The string form of the expression we are to parse.

    Returns
    -------
        symbols : set[str]
            A set of strings; one for each free variable in the expression.
    """
    try:
        symbols = sorted([str(v) for v in sp.sympify(s).atoms(sp.Symbol)])

        if INDEPENDENT_VARIABLE in symbols:
            symbols.remove(INDEPENDENT_VARIABLE)
        if PI in symbols:
            symbols.remove(PI)

        return symbols
    except (SyntaxError, ValueError):
        raise ImproperlyFormattedExpressionError(f"The expression '{s}' is not properly formatted.")
    except Exception as e:
        raise InvalidExpressionError(f'Expression invalid due to "{str(e)}".')

def is_accepted_expression(expression: str) -> bool:
    """ Checks if the provided expression is properly formatted and valid.

    PARAMETERS
    ----------
        expression : str
            The string form of the expression we are to check.

    RETURNS
    -------
        accepted : bool
            A boolean indicating if the expression is properly formatted and valid
    """
    try:
        parse(expression)
        return True
    except (ImproperlyFormattedExpressionError, InvalidExpressionError):
        return False

_UNSUPPORTED_UNICODE_CHARACTER_DICTIONARY = {
    PI: "pi",  # this is the only value that matters, pi will be recognized by sympy. The rest are just filler chars.
    NAUGHT: '0',
    SUB_T: 'T',
    SUB_L: 'L'
}

# fixme, move all this functionality into FitExpression, plus lambdify. Keep all that logic in one cute place.
def _replace_unsupported_unicode_characters(expression: str) -> str:
    f"""Replaces unsupported unicode characters with valid alternates. Meant for internal use only.

    There are some characters we include in beams for purely visual purposes (the unicode for pi or naught for
    example) and sometimes these will throw errors in sympy. So we need to replace them with alternate symbols.
    Note that {PI} would become 'pi' and {PI}2 would become 'pi2'. Currently this method is only applied immediately
    before and after lambdifying and in the call method of the fit expression so the user never sees an altered
    expression. That's also why this is a protected method, it shouldn't be called outside this module.

    PARAMETERS
    ----------
        expression : str

    RETURNS
    -------
        expression : str
            The expression with unsupported characters replaced.
    """
    for uc, c in _UNSUPPORTED_UNICODE_CHARACTER_DICTIONARY.items():
        expression = expression.replace(uc, c)
    return expression

_ALIASED_FUNCTIONS = [
    (r'j0.*?\(', 'spherical_jn(0,')
]

def _replace_aliased_functions(expression: str) -> str:
    """ Replace aliased functions with the actual expressions recognized by scipy or numpy.

    Replacements are determined by _ALIASED_FUNCTIONS where the first item of the tuple is the regex of alias to find
    and the second is what we are replacing it with. This is limited as you may be able to see. It would not be able
    to support the following alias for example:

        j0(x) => spherical_jn(x, 0)

    but easily supports:

        j0(x) => spherical_jn(0, x)

    Further functionality could be added. This is just meant for internal use only so no need to complicate it until we
    need to.

    PARAMETERS
    ----------
        expression : str

    RETURNS
    -------
        expression : str
            string representation of expression with aliases replaced
    """
    if not is_accepted_expression(expression):
        raise InvalidExpressionError("Cannot replace aliases in invalid expression.")

    expression_string = expression
    for alias_regex, actual in _ALIASED_FUNCTIONS:
        expression_string = re.sub(alias_regex, actual, expression_string)
    return expression_string

def alpha_correction(expression: str) -> str:
    """ Returns the given expression wrapped in the expression for the alpha correction. """
    return f'((1-{ALPHA})+((1+{ALPHA})*({expression})))/((1+{ALPHA})+((1-{ALPHA})*({expression})))'

def lambdify(expression: str, variables=None):
    """ Takes a string representation of an expression and returns a callable lambda.

    PARAMETERS
    ----------
        expression: str
            The string representation of the expression
        variables: iterable[str]
            Collection of free variables, if not provided then this will call parse on the expression

    RETURNS
    -------
        lambda_expression: lambda
            A callable lambda representation of the expression
    """
    expression_string = _replace_unsupported_unicode_characters(expression)
    expression_string = _replace_aliased_functions(expression_string)

    variables = variables if variables is not None else parse(expression)
    var_names = [INDEPENDENT_VARIABLE] if INDEPENDENT_VARIABLE in expression_string else []
    var_names.extend([_replace_unsupported_unicode_characters(var) for var in variables])

    lambda_expression = sp.lambdify(var_names, sp.sympify(expression_string), ["numpy", "scipy"])
    return lambda_expression

def _shortened_run_id(run_id: str) -> str:
    """Returns the first section of uuid."""
    return "_" + run_id.split('-')[0]

class ImproperlyFormattedExpressionError(Exception):
    """
    The expression is not properly formatted (e.g. 'x=3*y' or 'x*')
    """
    def __init__(self, *args):
        super(ImproperlyFormattedExpressionError, self).__init__(*args)

class InvalidExpressionError(Exception):
    """
    The expression is properly formatted but logically incorrect (e.g. a function that takes
    two args was only given one).
    """
    def __init__(self, *args):
        super(InvalidExpressionError, self).__init__(*args)

class InvalidFitArgumentsError(Exception):
    """Bad arguments passed to fit expression. """
    def __init__(self, *args):
        super(InvalidFitArgumentsError, self).__init__(*args)

class FittingError(Exception):
    def __init__(self, *args):
        super(FittingError, self).__init__(*args)

reason: import numpy,from scipy,import sympy


text:
import pylab
from xml.dom.minidom import parse, Node
import sys
import numpy as np
import shutil
import os

DPI = 75

def getText(nodelist):
    rc = ""
    for node in nodelist:
        if node.nodeType == node.TEXT_NODE:
            rc = rc + node.data
    return rc

def main(argv):
    input_fname = sys.argv[1]
    dom = parse(input_fname)
    outdir = sys.argv[2]
    res = dom.getElementsByTagName("results")[0]
    tools = [e for e in res.childNodes if e.nodeType == Node.ELEMENT_NODE]

    # get the pybindgen revno
    pbg_env = dom.getElementsByTagName("environment")[0].getElementsByTagName("pybindgen")[0]
    pbg_txt = getText(pbg_env.childNodes).split("\n")
    for l in pbg_txt:
        k, s, v = l.partition(':')
        k = k.strip()
        if k == 'revno':
            v = v.strip()
            revno = v
            break

    num_tests = len(tools[0].getElementsByTagName("test"))
    shutil.rmtree(outdir, True)
    os.mkdir(outdir)
    shutil.copy2(input_fname, outdir)

    figures = []

    sizes = [float(t.getAttribute('module-file-size')) for t in tools]
    labels= [t.tagName for t in tools]
    ind = range(len(sizes))
    pylab.bar(ind, sizes)
    pylab.xticks([0.5+x for x in ind], labels)
    pylab.title("Extension module file size (B)")
    fname = "sizes.png"
    pylab.savefig(os.path.join(outdir, fname), dpi=DPI)
    figures.append(fname)

    for t in range(num_tests):
        pylab.figure()
        labels = []
        values = []
        for x, tool in enumerate(tools):
            labels.append(tool.tagName)
            values.append(float(tool.getElementsByTagName("test")[t].getAttribute('time')))
        ind = range(len(values))
        pylab.bar(ind, values)
        pylab.xticks([0.5+x for x in ind], labels)

        desc = tools[0].getElementsByTagName("test")[t].getAttribute("description")
        pylab.title(desc)

        fname = "%s.png" % (desc)
        pylab.savefig(os.path.join(outdir, fname), dpi=DPI)
        figures.append(fname)

    index_html = file("%s/index.html" % outdir, "wt")
    print >> index_html, """

<title> PyBindGen Benchmarks </title>

Details in the Raw XML file.
  Source files for the benchmarks.

""" % (os.path.basename(input_fname), revno)

    for fig in figures:
        print >> index_html, """

""" % (fig,)

    print >> index_html, """

"""

    index_html.close()

if __name__ == '__main__':
    main(sys.argv)

ext: py

reason: import numpy


text:
from inspect import signature

from .exceptions import exceptions
from .sliders import sliders
from .utils import utils

import matplotlib.colors as mcolors
import matplotlib.pyplot as plt

import numpy as np

from .nullclines import Nullcline2D

class PhasePortrait2D:
    """
    PhasePortrait2D
    ----------------
    Makes a phase portrait of a 2D system.

    Methods
    -------
    draw_plot :
        Draws the streamplot. Is internaly used by method `plot`.

    add_function :
        Adds a function to the `dF` plot.

    add_slider :
        Adds a `Slider` for the `dF` function.

    plot :
        Prepares the plots and computes the values.
        Returns the axis and the figure.
    """
    _name_ = 'PhasePortrait2D'
    def __init__(self, dF, Range, *, MeshDim=30, dF_args={}, Density = 1, Polar = False, Title = 'Phase Portrait', xlabel = 'X', ylabel = r"$\dot{X}$", color='rainbow'):
        """
        PhasePortrait2D
        ---------------

        Parameters
        ----------
        dF : callable
            A dF type function.
        Range : [x_range, y_range]
            Ranges of the axis in the main plot.

        Key Arguments
        -------------
        MeshDim : int, default=30
            Number of elements in the arrows grid.
        dF_args : dict
            If necesary, must contain the kargs for the `dF` function.
        Density : float, default=1
            Number of elements in the arrows grid plot.
        Polar : bool, default=False
            Whether to use polar coordinates or not.
        Title : str, default='Phase Portrait'
        xlabel : str, default='X'
            x label of the plot.
        ylabel : str, default='$\dot{X}$'
            y label of the plot.
        color : str, default='rainbow'
            Matplotlib `Cmap`.
        """
        self.dF_args = dF_args.copy()                    # dF function's args
        self.dF = dF                                     # Function containing system's equations
        self.Range = Range                               # Range of graphical representation

        self.L = MeshDim                                                 # Number of points in the meshgrid
        self.Density = Density                                           # Controls concentration of nearby trajectories
        self.Polar = Polar                                               # If dF expression given in polar coord. mark as True
        self.Title = Title                                               # Title of the plot
        self.xlabel = xlabel                                             # Title on X axis
        self.ylabel = ylabel                                             # Title on Y axis

        # Variables for plotting
        self.fig, self.ax = plt.subplots()
        self.color = color
        self.sliders = {}
        self.nullclines = []

        # Meshgrid
        self._X, self._Y = np.meshgrid(np.linspace(*self.Range[0,:], self.L), np.linspace(*self.Range[1,:], self.L))

        if self.Polar:
            self._R, self._Theta = (self._X**2 + self._Y**2)**0.5, np.arctan2(self._Y, self._X) # Cartesian representation to polar representation transform

    def plot(self, *, color=None):
        """
        Prepares the plots and computes the values.

        Returns
        -------
        tuple(matplotlib Figure, matplotlib Axis)

        Key Arguments
        -------------
        color : str
            Matplotlib `Cmap`.
        """
        if color is not None:
            self.color = color

        self.draw_plot(color=self.color)
        self.fig.canvas.draw_idle()

        return self.fig, self.ax

    def draw_plot(self, *, color=None):
        """
        Draws the streamplot. Is internaly used by method `plot`.

        Returns
        -------
        matplotlib.Streamplot

        Key Arguments
        -------------
        color : str
            Matplotlib `Cmap`.
        """
        self.dF_args.update({name: slider.value for name, slider in self.sliders.items() if slider.value!= None})

        try:
            for nullcline in self.nullclines:
                nullcline.plot()
        except AttributeError:
            pass

        if self.Polar:
            self._PolarTransformation()
        else:
            self._dX, self._dY = self.dF(self._X, self._Y, **self.dF_args)

        if utils.is_number(self._dX):
            self._dX = self._X.copy() * 0 + self._dX
        if utils.is_number(self._dY):
            self._dY = self._Y.copy() * 0 + self._dY

        colors = (self._dX**2+self._dY**2)**(0.5)
        colors_norm = mcolors.Normalize(vmin=colors.min(), vmax=colors.max())
        stream = self.ax.streamplot(self._X, self._Y, self._dX, self._dY, color=colors, cmap=color, norm=colors_norm, linewidth=1, density= self.Density)
        self.ax.set_xlim(self.Range[0,:])
        self.ax.set_ylim(self.Range[1,:])
        x0,x1 = self.ax.get_xlim()
        y0,y1 = self.ax.get_ylim()
        self.ax.set_aspect(abs(x1-x0)/abs(y1-y0))
        self.ax.set_title(f'{self.Title}')
        self.ax.set_xlabel(f'{self.xlabel}')
        self.ax.set_ylabel(f'{self.ylabel}')
        self.ax.grid()

        return stream

    def add_nullclines(self, *, precision=0.01, offset=0, density=50, xRange=None, yRange=None, dF_args=None, xcolor='r', ycolor='g', bgcolor='w', alpha=0):
        self.nullclines.append(Nullcline2D(self, self.dF,
                                          precision=precision, offset=offset, density=density,
                                          xRange=xRange, yRange=yRange, dF_args=dF_args,
                                          xcolor=xcolor, ycolor=ycolor, bgcolor=bgcolor, alpha=alpha, polar=self.Polar))

    def add_slider(self, param_name, *, valinit=None, valstep=0.1, valinterval=10):
        """
        Adds a slider which can change the value of a parameter in execution time.

        Parameters
        ----------
        param_name : str
            It takes the name of the parameter on which the slider will be defined. Must be the same as the one appearing as karg in the `dF` function.

        valinit : numeric, optional
            Initial value of *param_name* variable. Default value is 0.5.

        valstep : numeric, optional
            Slider step value. Default value is 0.1.

        valinterval : numeric or list, optional
            Slider range. Default value is [-10, 10].
        """

        self.sliders.update({param_name: sliders.Slider(self, param_name, valinit=valinit, valstep=valstep, valinterval=valinterval)})

        self.fig.subplots_adjust(bottom=0.25)

        self.sliders[param_name].slider.on_changed(self.sliders[param_name])

    def _PolarTransformation(self):
        """
        Computes the expression of the velocity field if coordinates are given in polar representation.
        """
        self._dR, self._dTheta = self.dF(self._R, self._Theta, **self.dF_args)
        self._dX, self._dY = self._dR*np.cos(self._Theta) - self._R*np.sin(self._Theta)*self._dTheta, self._dR*np.sin(self._Theta)+self._R*np.cos(self._Theta)*self._dTheta

    @property
    def dF(self):
        return self._dF

    @dF.setter
    def dF(self, func):
        if not callable(func):
            raise exceptions.dFNotCallable(func)
        sig = signature(func)
        if len(sig.parameters)<2 + len(self.dF_args):
            raise exceptions.dFInvalid(sig, self.dF_args)
        self._dF = func

    @property
    def Range(self):
        return self._Range

    @Range.setter
    def Range(self, value):
        self._Range = np.array(utils.construct_interval(value, dim=2))

    @property
    def dF_args(self):
        return self._dF_args

    @dF_args.setter
    def dF_args(self, value):
        if value:
            if not isinstance(value, dict):
                raise exceptions.dF_argsInvalid(value)
        self._dF_args = value

max_stars_repo_name: Loracio/phase-portrait

max_issues_repo_name: Loracio/phase-portrait

max_forks_repo_name: Loracio/phase-portrait


text:
import numpy as np

class Butterfly:
    def __init__(self, min_x: [], max_x: [], fitness_func, dimensions, c):
        self.min_position = min_x
        self.max_position = max_x
        self.number_of_dimensions = dimensions
        self.c = c
        self.fragrance = 0

        self.X_positions = np.zeros(self.number_of_dimensions)
        self.P_best = np.zeros(self.number_of_dimensions)

        self.__init_position()

        self.fitness = fitness_func
        self.best_fitness = fitness_func(self.X_positions)
        self.l = self.best_fitness

    def __init_position(self):
        self.X_positions = np.random.uniform(self.min_position, self.max_position, self.number_of_dimensions)

    def update_fitness(self):
        if self.fitness(self.X_positions) < self.best_fitness:
            self.best_fitness = self.fitness(self.X_positions)
            self.l = self.best_fitness

    def get_best_fitness(self):
        return self.best_fitness

    def update_fragrance(self, a):
        self.fragrance = self.c * self.l ** a

    def update_to_best(self, g_best):
        self.X_positions = np.clip(self.X_positions + (np.random.uniform(0, 1, self.number_of_dimensions) * g_best
                                                       - self.X_positions), self.min_position, self.max_position) * self.fragrance

    def update_to_random(self, butterfly1, butterfly2):
        self.X_positions = np.clip(self.X_positions + (np.random.uniform(0, 1, self.number_of_dimensions) * butterfly1
                                                       - butterfly2), self.min_position, self.max_position) * self.fragrance

    def update_a(self):
        pass

max_stars_repo_name: zdoryk/TSI

max_issues_repo_name: zdoryk/TSI

max_forks_repo_name: zdoryk/TSI


text:
"""Created on Tue Apr 14 15:08:48 2020
@author: Dhruv  Khurana
"""
checkimport = 0
from tkinter import *
from tkinter import filedialog
from tkinter.ttk import *
from PIL import ImageTk,Image
import numpy as np
import pandas as pd
from sklearn.tree import DecisionTreeClassifier
from sklearn import preprocessing
from sklearn import metrics
from sklearn.model_selection import train_test_split
import csv
def is_integer(s):
    try:
        int(s)
        return True
    except ValueError:
        return False
def is_float(s):
    try:
        float(s)
        return True
    except ValueError:
        return False
def dfimport() :
   global df,checkimport,b_next,import_file_path
   import_file_path = filedialog.askopenfilename()
   if import_file_path.endswith('.csv'):
      canvas2.create_text(240,260,fill="darkblue",font="Arial 12 bold",text="File read successfully!",tag = "read")
      checkimport = 1
      b_next = Button(f3, text='Next',style = 'W.TButton',command=lambda :f4.tkraise())
      b_next.place(relx = 0.8, rely = 0.8, anchor = 'se')
      df = pd.read_csv(import_file_path)
   else:
      messagebox.showerror("MedPred Warning","Error: Sorry, file format is not supported!")
def read_back():
    f2.tkraise()
    b_next.destroy()
    canvas2.delete("read")
def butpress(v):
    global checkimport
    if v.get() == 1:
     f4.tkraise()
     checkimport = 0
    elif v.get() == 2:
     f3.tkraise()
    else:
     messagebox.showwarning("MedPred","AppWarning: You must select an option to proceed further.")
def train():
   global drugTree
   global bn,acc
   if checkimport == 0:
     my_data = pd.read_csv("drug200.csv", delimiter=",")
     X = my_data[['Age', 'Sex', 'BP', 'Cholesterol', 'Na_to_K']].values
     Label_s = preprocessing.LabelEncoder()
     Label_s.fit(['M','F'])
     X[:,1] = Label_s.transform(X[:,1])
     Label_BP = preprocessing.LabelEncoder()
     Label_BP.fit([ 'LOW', 'NORMAL', 'HIGH'])
     X[:,2] = Label_BP.transform(X[:,2])
     Label_C = preprocessing.LabelEncoder()
     Label_C.fit([ 'NORMAL', 'HIGH'])
     X[:,3] = Label_C.transform(X[:,3])
     y = my_data["Drug"]
     X_trainset, X_testset, y_trainset, y_testset = train_test_split(X, y, test_size=0.3, random_state=3)
     drugTree = DecisionTreeClassifier(criterion="entropy", max_depth = 4)
     drugTree.fit(X_trainset,y_trainset)
     predTree = drugTree.predict(X_testset)
     acc = metrics.accuracy_score(y_testset, predTree)
     canvas3.create_text(240,280,fill="black",font="Arial 12 bold",text="Model is trained.",tag = "Trained")
     messagebox.showinfo("MedPred"," Training completed! ")
     bn = Button(f4, text='Next',style = 'W.TButton',command=lambda :f5.tkraise())
     bn.place(relx = 0.85, rely = 0.85, anchor = 'se')
   else:
       if len(df)==0:
           messagebox.showerror("MedPred Warning","Error: Training failed! File is empty/corrupted")
       elif len(df)<10:
           messagebox.showwarning("MedPred Error","Error: Training failed! File is too short. We recommend min. size of 10 samples for a good model training")
       else:
          X = df[['Age', 'Sex', 'BP', 'Cholesterol', 'Na_to_K']].values
          Label_s = preprocessing.LabelEncoder()
          Label_s.fit(['M','F'])
          X[:,1] = Label_s.transform(X[:,1])
          Label_BP = preprocessing.LabelEncoder()
          Label_BP.fit([ 'LOW', 'NORMAL', 'HIGH'])
          X[:,2] = Label_BP.transform(X[:,2])
          Label_C = preprocessing.LabelEncoder()
          Label_C.fit([ 'NORMAL', 'HIGH'])
          X[:,3] = Label_C.transform(X[:,3])
          y = df["Drug"]
          X_trainset, X_testset, y_trainset, y_testset = train_test_split(X, y, test_size=0.3, random_state=3)
          drugTree = DecisionTreeClassifier(criterion="entropy", max_depth = 4)
          drugTree.fit(X_trainset,y_trainset)
          predTree = drugTree.predict(X_testset)
          acc = metrics.accuracy_score(y_testset, predTree)
          canvas3.create_text(240,260,fill="darkblue",font="Arial 12 bold",text="Model is trained.",tag = "Trained")
          messagebox.showinfo("MedPred"," Training completed! ")
          bn = Button(f4, text='Next',style = 'W.TButton',command=lambda :f5.tkraise())
          bn.place(relx = 0.85, rely = 0.85, anchor = 'se')
def train_back():
    f2.tkraise()
    if checkimport==1:
        read_back()
    bn.destroy()
    canvas3.delete("Trained")
def predict(age,sex,bp,c,nak):
    if len(age)==0:
     messagebox.showwarning("MedPred","Please enter age!")
    elif len(nak)==0 :
     messagebox.showwarning("MedPred","Please enter Na-K level!")
    elif is_integer(age)==False:
     messagebox.showwarning("MedPred","Please enter a valid numeric age!")
    elif is_float(nak)==False:
     messagebox.showwarning("MedPred","Please enter valid Na-K level")
    else:
     age = int(age)
     nak = float(nak)
     canvas4.delete("predict")
     z = []
     z.append(age)
     z.append(sex.get())
     z.append(bp.get())
     z.append(c.get())
     z.append(nak)
     z = np.array(z).reshape(1,-1)
     pred = drugTree.predict(z)
     canvas4.create_text(180,260,fill="black",font="Calibri 14 bold",text="The drug suitable for this patient is: %s"%pred[0],tag="predict")
def load():
    if checkimport == 0:
     window = Toplevel(root)
     window.title("Patient Dataset")
     width = 590
     height = 600
     screen_width = window.winfo_screenwidth()
     screen_height = window.winfo_screenheight()
     x = (screen_width / 2) - (width / 2)
     y = (screen_height / 2) - (height / 2)
     window.geometry("%dx%d+%d+%d" % (width, height, x, y))
     TableMargin = Frame(window, width=700)
     TableMargin.pack(side=TOP)
     scrollbarx = Scrollbar(TableMargin, orient=HORIZONTAL)
     scrollbary = Scrollbar(TableMargin, orient=VERTICAL)
     tree = Treeview(TableMargin, columns=("Age", "Sex", "BP","Cholesterol","Na_to_K","Drug"), height=400, selectmode="extended",
                    yscrollcommand=scrollbary.set, xscrollcommand=scrollbarx.set)
     scrollbary.config(command=tree.yview)
     scrollbary.pack(side=RIGHT, fill=Y)
     scrollbarx.config(command=tree.xview)
     scrollbarx.pack(side=BOTTOM, fill=X)
     tree.heading('Age', text="Age", anchor=W)
     tree.heading('Sex', text="Sex", anchor=W)
     tree.heading('BP', text="BP", anchor=W)
     tree.heading('Cholesterol', text="Cholesterol", anchor=W)
     tree.heading('Na_to_K', text="Na_to_K", anchor=W)
     tree.heading('Drug', text="Drug", anchor=W)
     tree.column('#0', stretch=NO, minwidth=0, width=0)
     tree.column('#1', stretch=NO, minwidth=0, width=90)
     tree.column('#2', stretch=NO, minwidth=0, width=90)
     tree.column('#3', stretch=NO, minwidth=0, width=120)
     tree.column('#4', stretch=NO, minwidth=0, width=120)
     tree.column('#5', stretch=NO, minwidth=0, width=90)
     tree.column('#6', stretch=NO, minwidth=0, width=90)
     tree.pack()
     with open('drug200.csv') as f:
      reader = csv.DictReader(f, delimiter=',')
      for row in reader:
       age = row['Age']
       sex = row['Sex']
       bp = row['BP']
       chol = row['Cholesterol']
       nak = row['Na_to_K']
       drug = row['Drug']
       tree.insert("",'end', values=(age,sex,bp,chol,nak,drug))

    else:
       window = Toplevel(root)
       window.title("Patient Dataset")
       width = 590
       height = 600
       screen_width = window.winfo_screenwidth()
       screen_height = window.winfo_screenheight()
       x = (screen_width / 2) - (width / 2)
       y = (screen_height / 2) - (height / 2)
       window.geometry("%dx%d+%d+%d" % (width, height, x, y))
       TableMargin = Frame(window, width=700)
       TableMargin.pack(side=TOP)
       scrollbarx = Scrollbar(TableMargin, orient=HORIZONTAL)
       scrollbary = Scrollbar(TableMargin, orient=VERTICAL)
       tree = Treeview(TableMargin, columns=("Age", "Sex", "BP","Cholesterol","Na_to_K","Drug"), height=400, selectmode="extended",
                    yscrollcommand=scrollbary.set, xscrollcommand=scrollbarx.set)
       scrollbary.config(command=tree.yview)
       scrollbary.pack(side=RIGHT, fill=Y)
       scrollbarx.config(command=tree.xview)
       scrollbarx.pack(side=BOTTOM, fill=X)
       tree.heading('Age', text="Age", anchor=W)
       tree.heading('Sex', text="Sex", anchor=W)
       tree.heading('BP', text="BP", anchor=W)
       tree.heading('Cholesterol', text="Cholesterol", anchor=W)
       tree.heading('Na_to_K', text="Na_to_K", anchor=W)
       tree.heading('Drug', text="Drug", anchor=W)
       tree.column('#0', stretch=NO, minwidth=0, width=0)
       tree.column('#1', stretch=NO, minwidth=0, width=90)
       tree.column('#2', stretch=NO, minwidth=0, width=90)
       tree.column('#3', stretch=NO, minwidth=0, width=120)
       tree.column('#4', stretch=NO, minwidth=0, width=120)
       tree.column('#5', stretch=NO, minwidth=0, width=90)
       tree.column('#6', stretch=NO, minwidth=0, width=90)
       tree.pack()
       with open(import_file_path) as f:
        reader = csv.DictReader(f, delimiter=',')
        for row in reader:
         age = row['Age']
         sex = row['Sex']
         bp = row['BP']
         chol = row['Cholesterol']
         nak = row['Na_to_K']
         drug = row['Drug']
         tree.insert("",'end', values=(age,sex,bp,chol,nak,drug))
def reset():
    canvas4.delete("predict")
    e_age.delete(0,'end')
    e_nak.delete(0,'end')
def home_back():
    f1.tkraise()
    if checkimport==1:
        b_next.destroy()
        canvas2.delete("read")
    bn.destroy()
    canvas3.delete("Trained")
    reset()
def accuracy():
    g = acc*100
    messagebox.showinfo("MedPred",'The percentage accuracy of this trained model is %f' % g)
def about():
    top = Toplevel(root)
    top.title("About MedPred")
    top.geometry("300x200")
    canvas=Canvas(top,width=300,height=300)
    canvas.place(relx = 0.5, rely = 0.5, anchor = 'center')
    canvas.create_text(150,100,fill="red",font="Times 13 bold",text="MedPred")
    canvas.create_text(150,120,fill="black",font="Times 11 bold",text="Personalized Drug Prediction App")
    canvas.create_text(150,140,fill="blue",font="Arial 10",text="© 2020 Dhruv Khurana")
    canvas.create_text(150,160,fill="black",font="Calibri 10",text="This app was developed by Dhruv Khurana")
    canvas.create_text(150,180,fill="black",font="Calibri 10",text="as a part of second-year NTCC Project at ASET, Noida.")
    canvas.create_text(150,230,fill="purple",font="Times 11",text="Datasets have been extracted from the Internet.")
root = Tk()
img = PhotoImage(file='doctor.ico')
root.tk.call('wm', 'iconphoto', root._w, img)
root.title('MedPred')
root.geometry("400x400")
root.resizable(0, 0)
style = Style()
style.configure('W.TButton', font = ('Calibri', 11),foreground = 'black',highlightbackground = 'blue',background = 'blue')
f1 = Frame(root, width=400, height=400)
f2 = Frame(root, width=400, height=400)
f3 = Frame(root, width=400, height=400)
f4 = Frame(root, width=400, height=400)
f5 = Frame(root, width=400, height=400)
f1.grid(row=0, column=0, sticky = 'news')
f2.grid(row=0, column=0, sticky = 'news')
f3.grid(row=0, column=0, sticky = 'news')
f4.grid(row=0, column=0, sticky = 'news')
f5.grid(row=0, column=0, sticky = 'news')
canvas2 = Canvas(f3,width=400,height=400)
image2 = ImageTk.PhotoImage(Image.open("medicine.png"))
canvas2.create_image(0,0,anchor=NW,image=image2)
canvas2.place(relx = 0.5, rely = 0.5, anchor = 'center')
canvas1 = Canvas(f2,width=400,height=400)
image1 = ImageTk.PhotoImage(Image.open("medical.png"))
canvas1.create_image(0,0,anchor=NW,image=image1)
canvas1.place(relx = 0.5, rely = 0.5, anchor = 'center')
canvas3 = Canvas(f4,width=400,height=400)
image3 = ImageTk.PhotoImage(Image.open("train.png"))
canvas3.create_image(0,0,anchor=NW,image=image3)
canvas3.place(relx = 0.5, rely = 0.5, anchor = 'center')
canvas3.create_text(200,170,fill="black",font="Arial 11",text="Train the app model for prediction")
canvas4 = Canvas(f5,width=400,height=400)
image4 = ImageTk.PhotoImage(Image.open("pred.png"))
canvas4.create_image(0,0,anchor=NW,image=image4)
canvas4.place(relx = 0.5, rely = 0.5, anchor = 'center')
canvas4.create_text(200,20,fill="black",font="Arial 11 bold",text="DRUG PREDICTION")
canvas4.create_text(87,40,fill="black",font="Calibri 11 bold",text="Enter details of the patient")
canvas4.create_text(20,64,fill="black",font="Times 11 bold",text="Age")
e_age = Entry(f5)
e_age.place(x = 45,y = 52,width=60,height=22)
canvas4.create_text(20,90,fill="black",font="Times 11 bold",text="Sex")
sex = IntVar()
Radiobutton(f5, text='Male',variable=sex, value=1).place(relx = 0.18, rely = 0.22, anchor = 'center')
Radiobutton(f5, text='Female',variable=sex, value=0).place(relx = 0.33, rely = 0.22, anchor = 'center')
canvas4.create_text(55,118,fill="black",font="Times 11 bold",text="Blood Pressure")
bp = IntVar()
Radiobutton(f5, text='Low',variable=bp, value=1).place(relx = 0.33, rely = 0.29, anchor = 'center')
Radiobutton(f5, text='Normal',variable=bp, value=2).place(relx = 0.48, rely = 0.29, anchor = 'center')
Radiobutton(f5, text='High',variable=bp, value=0).place(relx = 0.63, rely = 0.29, anchor = 'center')
canvas4.create_text(45,145,fill="black",font="Times 11 bold",text="Cholesterol")
c = IntVar()
Radiobutton(f5, text='Normal',variable=c, value=1).place(relx = 0.30, rely = 0.36, anchor = 'center')
Radiobutton(f5, text='High',variable=c, value=0).place(relx = 0.45, rely = 0.36, anchor = 'center')
canvas4.create_text(84,175,fill="black",font="Times 11 bold",text="Sodium-Potassium level")
e_nak = Entry(f5)
e_nak.place(x = 165,y = 162,width=60,height=22)
b = Button(f5, text='Predict drug',style = 'W.TButton',command= lambda :predict(e_age.get(),sex,bp,c,e_nak.get())).place(relx = 0.5, rely = 0.53, anchor = 'center')
bd = Button(f5, text='Load current dataset',style = 'W.TButton',command= lambda :load()).place(relx = 0.7, rely = 0.81, anchor = 'center')
b9 = Button(f5, text='Back to Home',style = 'W.TButton',command= lambda :home_back()).place(relx = 0.37, rely = 0.85, anchor = 'se')
b8 = Button(f5, text='Clear all',style = 'W.TButton',command= lambda :reset()).place(relx = 0.94, rely = 0.1, anchor = 'ne')
b7 = Button(f5, text='Check accuracy',style = 'W.TButton',command= lambda :accuracy()).place(relx = 0.89, rely = 0.565, anchor = 'se')
canvas2.create_text(200,170,fill="black",font="Calibri 11 bold",text="Import your .csv data file below")
canvas2.create_text(200,100,fill="darkred",font="Arial 10 bold",text="Note: File should have the first columns exactly as specified:")
canvas2.create_text(200,130,fill="darkblue",font="Arial 12 bold",text="Age, Sex, BP, Cholesterol, Na_to_K, Drug")
b = Button(f3, text='Browse file',style = 'W.TButton',command= lambda :dfimport()).place(relx = 0.5, rely = 0.5, anchor = 'center')
bb = Button(f3, text='Back',style = 'W.TButton',command= lambda : read_back()).place(relx = 0.4, rely = 0.8, anchor = 'se')
bback = Button(f4, text='Back',style = 'W.TButton',command= lambda : train_back()).place(relx = 0.4, rely = 0.8, anchor = 'se')
b = Button(f4, text='Train',style = 'W.TButton',command= lambda :train()).place(relx = 0.5, rely = 0.5, anchor = 'center')
canvas3.create_text(190,80,fill="black",font="Arial 13 bold",text="Training")
canvas1.create_text(120,80,fill="black",font="Arial 13 bold",text="Choose a dataset")
v = IntVar()
Radiobutton(f2, text='Use patient dataset for inbuilt disease prediction',variable=v, value=1).place(relx = 0.49, rely = 0.4, anchor = 'center')
Radiobutton(f2, text='Use patient dataset for custom disease prediction',variable=v, value=2).place(relx = 0.5, rely = 0.5, anchor = 'center')
b2 = Button(f2, text='Back',style = 'W.TButton',command= lambda : f1.tkraise()).place(relx = 0.4, rely = 0.8, anchor = 'se')
b3 = Button(f2, text='Next',style = 'W.TButton',command=lambda : butpress(v)).place(relx = 0.8, rely = 0.8, anchor = 'se')
canvas=Canvas(f1,width=400,height=400)
image=ImageTk.PhotoImage(Image.open("phar.png"))
canvas.create_image(0,0,anchor=NW,image=image)
canvas.place(relx = 0.5, rely = 0.5, anchor = 'center')
canvas.create_text(200,10,fill="black",font="Arial 11 ",text="Hello, guest")
canvas.create_text(200,170,fill="darkblue",font="Arial 14 bold ",text="MedPred")
canvas.create_text(200,190,fill="black",font="Arial 11 bold ",text="Personalized Drug Prediction Service")
b1 = Button(f1, text='Go!', style = 'W.TButton',command=lambda :f2.tkraise()).place(relx = 0.75, rely = 0.75, anchor = 'se')
b_about = Button(f1, text='About the app', style = 'W.TButton',command=lambda :about()).place(relx = 0.1, rely = 0.9, anchor = 'sw')
f1.tkraise()
root.mainloop()


text:
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import numpy as np
from copy import deepcopy
from gym.spaces.box import Box
import inspect

from utils.helpers import Experience            # NOTE: here state0 is always "None"
from utils.helpers import preprocessAtari, rgb2gray, rgb2y, scale
from core.env import Env

class GymEnv(Env):  # low dimensional observations
    def __init__(self, args, env_ind=0):
        super(GymEnv, self).__init__(args, env_ind)

        assert self.env_type == "gym"
        try: import gym
        except ImportError as e: self.logger.warning("WARNING: gym not found")

        self.env = gym.make(self.game)
        self.env.seed(self.seed)    # NOTE: so each env would be different

        # action space setup
        self.actions     = range(self.action_dim)
        self.logger.warning("Action Space: %s", self.actions)

        # state space setup
        self.logger.warning("State  Space: %s", self.state_shape)

        # continuous space
        if args.agent_type == "a3c":
            self.enable_continuous = args.enable_continuous
        else:
            self.enable_continuous = False

    def _preprocessState(self, state):    # NOTE: here no preprecessing is needed
        return state

    @property
    def state_shape(self):
        return self.env.observation_space.shape[0]

    def render(self):
        if self.mode == 2:
            frame = self.env.render(mode='rgb_array')
            frame_name = self.img_dir + "frame_%04d.jpg" % self.frame_ind
            self.imsave(frame_name, frame)
            self.logger.warning("Saved  Frame    @ Step: " + str(self.frame_ind) + " To: " + frame_name)
            self.frame_ind += 1
            return frame
        else:
            return self.env.render()

    def visual(self):
        pass

    def sample_random_action(self):
        return self.env.action_space.sample()

    def reset(self):
        self._reset_experience()
        self.exp_state1 = self.env.reset()
        return self._get_experience()

    def step(self, action_index):
        self.exp_action = action_index
        if self.enable_continuous:
            self.exp_state1, self.exp_reward, self.exp_terminal1, _ = self.env.step(self.exp_action)
        else:
            self.exp_state1, self.exp_reward, self.exp_terminal1, _ = self.env.step(self.actions[self.exp_action])
        return self._get_experience()


text:
#-*-encoding:utf-8-*-
#图像识别代码
import pytesseract
from PIL import Image
#import matplotlib.pyplot as plt
import numpy as np
import image_pre
from os import path,walk,getcwd
from sklearn import preprocessing,svm,externals,naive_bayes,neural_network

'''

test2=img2.histogram()

x=np.arange(1024)
test1_np=np.array(test1)
test2_np=np.array(test2)
plt.plot(x,test1_np)
plt.plot(x,test2_np)
plt.show()

'''
'''
#辅助函数————对比两个图像的空间距离
def distance(img1,img2):
    #首先将图像二值化,然后转换成np.array
    img1_array=np.array(img1.convert('1'))
    img2_array=np.array(img2.convert('1'))
    img1.show()
    img2.show()
    #返回欧式距
    return np.linalg.norm(img1_array^img2_array)
'''
#使用sklearn进行学习后判断
#辅助函数--训练（输入训练用图片文件夹地址，保存一个net.pkl到程序文件夹）
def train_p(path_str):#给出文件夹所在的地址，生成训练数据集合和目标集合，进行训练，完成后将结果保存为pkl
    train_data=[]#用来存储训练数据和目标数据
    target_data=[]
    for (root, dirs, files) in walk(path_str):
        for filename in files:
            #获取训练数据
            img_temp=image_pre.poker_co(Image.open(path_str+filename, mode="r"))
            img_array=np.array(img_temp.convert('1'))
            train_data.append(img_array)
            #获取目标数据
            #target_temp1=float(str(ord(filename[7:-4][0]))+filename[7:-4][1:])
            fn_temp=0.0
            if filename[7:-4][0]=='r':
                fn_temp=int('1'+filename[7:-4][1:])
            if filename[7:-4][0]=='b':
                fn_temp=int('2'+filename[7:-4][1:])
            if filename[7:-4][0]=='m':
                fn_temp=int('3'+filename[7:-4][1:])
            if filename[7:-4][0]=='f':
                fn_temp=int('4'+filename[7:-4][1:])
            target_data.append(fn_temp)
    #将原始数据装换为array,并归一化
    train_data_array=np.array(train_data)
    target_data_array=np.array(target_data)
    #target_data_array=preprocessing.scale(np.array(target_data))
    #用分类器训练
    nsamples, nx, ny = train_data_array.shape
    d2_train_data_array = train_data_array.reshape((nsamples,nx*ny))
    #net = neural_network.MLPClassifier(solver='lbfgs', alpha=1e-3,hidden_layer_sizes=(2*nx*ny, 1), random_state=1,max_iter=200)
    net= svm.SVC()
    net.fit(d2_train_data_array, target_data_array)
    #保存训练数据
    externals.joblib.dump(net,'net_ps.pkl')

#辅助函数--输入一个预测图片地址，返回一个预测结果
def poker_predict(img_path,net_path):#输入image,net的地址
    #处理图片数据
    img_temp=image_pre.poker_co(Image.open(img_path, mode="r"))
    img_array=np.array(img_temp.convert('1'))
    pre_data_array=np.array(img_array)
    nx, ny = pre_data_array.shape
    d2_pre_data_array = pre_data_array.reshape((1,nx*ny))
    #加载同文件夹下的net.pkl,并预测
    net=externals.joblib.load(net_path)
    result_int=net.predict(d2_pre_data_array)
    return result_int[0]

#辅助函数--金额识别（用于总金额和跟随金额的识别，输入img_path，输出int）
def aboutMoney(img_path):
    global mz_int
    img_temp=Image.open(img_path, mode="r")
    try:
        text = pytesseract.image_to_string(img_temp,config='-l chi_sim')
        text.replace(' ','')
        #这是状态的情况
        if text[0]==u'全':
            #让牌的金额
            money=999999999
            return money,text[0]
        if text[0]==u'让':
            #让牌的金额
            money=mz_int
            return money,text[0]
        if text[0]==u'跟':
            #跟的金额
            if text[-1]==u'万':
                money=int(float(text[1:-1])*10000)
            else:
                money=int(text[1:])
            return money,text[0]
        #底池的情况
        if text[2]==u':':
            if text[-1]==u'万':
                money=int(float(text[3:-1])*10000)
            else:
                money=int(text[3:])
            return money,text[0]
        #allmoney
        else:
            if text[-1]==u'万':
                money=int(float(text[0:-1])*10000)
            else:
                money=int(text)
            return money,text[0]
    except Exception,e:
    #啥都不是了
        money=mz_int
        return money,text[0]

#辅助函数--获取区域块的颜色
def get_colornum(img_path,region):#获取指定像素点的颜色值（输入图片地址和像素点坐标）
    img=Image.open(img_path, mode='r')
    img.convert('RGBA')
    a=img.getpixel(region)
    return a

#辅助函数--将识别出的扑克数字，转换为扑克花色和牌值
def poker_c(pre_num):#输入int，返回str
    pre_str=str(pre_num)
    if pre_str[0]=='1':
        re_poker='r'+pre_str[1:]
    if pre_str[0]=='2':
        re_poker='b'+pre_str[1:]
    if pre_str[0]=='3':
        re_poker='m'+pre_str[1:]
    if pre_str[0]=='4':
        re_poker='f'+pre_str[1:]
    return re_poker
#辅助函数--专门用来获取盲注值
mz_int=0
def get_mz():
    global mz_int
    mz_str=raw_input('盲注多少？')
    mz_int=int(mz_str)
    return mz_int
#函数测试
#train_p('/home/younghunter/桌面/train/')#训练样本并获net.pkl
#print poker_c(poker_predict('/home/younghunter/桌面/ps2_co.png',getcwd()+'/net_ps.pkl'))
#测试颜色获取
#a=get_colornum('/home/younghunter/桌面/ps1_co_f5.png',(8,88))#这是pk的判断位置（8,118），ps判断位置（8,88），zt判断位置（8,8）
#print a[0]

#测试汉字识别
#img=Image.open('/home/younghunter/桌面/dz/dz_co_1.32w.png',mode='r')
#text = pytesseract.image_to_string(img,config='-l chi_sim')
#print text
#get_mz()
#print aboutMoney('/home/younghunter/桌面/dz_co.png')


text:
## Hot or Cold ##
#################

# Basically there are 2 different rulesets:

# Ruleset 1:

# For every letter recieved, there are 3 possible outcomes:

# Correct (Green)
# Hot (Red)
# Cold (Blue)

# Main Constants:

import numpy as np
from string import ascii_lowercase

alphabet = ascii_lowercase

# numpy

# Main Functions:

def get_letter_score(guessed_letter, correct_letter, ruleset):
    """ Function that returns if given letter is either corret, hot or cold

            There are 2 different rulesets for determining wether a letter is hot or cold:

            - Closeness ruleset
            - Height ruleset

        - Closeness :

            - The letter is hot (resp. cold) if the index of the correct letter is closer than not from the index of the given letter.

            Example:

                If the correct letter is "G".

                    The guessed letter was "H".
                        It returns 'Hot'.

                    The guessed letter was "F":
                        It returns 'Hot'.

                    The guessed letter was "Z":
                        It returns 'Cold'.

        - Height:

            - The letter is hot (resp. cold) if the index is higher than the index of the correct letter

            Example:

                If the correct letter is "G".

                    The guessed letter was "H".
                        It returns 'Hot'.

                    The guessed letter was "F":
                        It returns 'Cold'.

            """

    # Ruleset if valid
    try:
        assert ruleset in ['closeness', 'height']
    except:
        print("Ruleset must be either closeness or height.")

    if guessed_letter == correct_letter:
        # Correct guess.
        return 'Green'

    if ruleset == 'height':

        if ord(guessed_letter) > ord(correct_letter):
            return 'Hot'

        else:
            return 'Cold'

    if ruleset == 'closeness':

        if abs(ord(guessed_letter) - ord(correct_letter)) <= 6:
            return 'Hot'

        else:
            return 'Cold'

def get_new_word(size):

    # Size in string please
    size = str(size)

    with open('hotorcold/data/word_size_' + size + '.txt', 'r') as f:
        X = f.readlines()
    random_word = np.random.randint(len(X))

    return X[random_word]

def valid_word(word):

    with open('hotorcold/data/word_size_5.txt', 'r') as f:
        X = f.readlines()

    if word+'\n' in X:
        return True
    else:
        return False

max_stars_repo_name: BruninLima/HotOrCold

max_issues_repo_name: BruninLima/HotOrCold

max_forks_repo_name: BruninLima/HotOrCold


text:
# Copyright (C) 2020 Javier Palomares

import numpy as np
import Levenshtein as lev
from .DistanceAlgorithms import DistanceAlgorithms

'''
Interface method for distance algos implementation
'''
class DistanceAlgorithm():
    def __init__(self,distance_algorithm):
        # the distance algorithm to use
        self.algorithm = distance_algorithm
        if self.algorithm == DistanceAlgorithms.LEVENSHTEIN:
            self._distance_comparer = Levenshtein()
        else:
            raise ValueError("Unknown distance algorithm")

    def edit_distance(self, string1, string2):
        return self._distance_comparer.edit_distance(string1,string2)

    def get_distance(self, string1, string2):
        return self.edit_distance(string1,string2)

# Implemenation of levenshtein distance
class Levenshtein():
    def edit_distance(self, str1, str2):
        #TODO: Need to figure out how to include the ratios in here
        return lev.distance(str1,str2)


text:
# -*- coding: utf-8 -*-
"""
Created on Mon Feb  8 21:12:01 2021

@author: kylei
"""

import pandas as pd
import matplotlib.pyplot as plt
import datetime as dt
import numpy as np
from sklearn.linear_model import LinearRegression
from sklearn.preprocessing import PolynomialFeatures, StandardScaler
from sklearn.metrics import r2_score, mean_squared_error, mean_absolute_error
from sklearn.model_selection import train_test_split

def train_test_overfitting(model, x, y, deg):
    x_train, x_test, y_train, y_test = train_test_split(
        x, y, test_size=0.33, random_state=4
    )

    poly_features = PolynomialFeatures(degree=deg)

    x_train_poly = poly_features.fit_transform(x_train)
    x_test_poly = poly_features.fit_transform(x_test)

    train_pred = model.predict(x_train_poly)
    test_pred = model.predict(x_test_poly)

    train_metric_dict = []
    test_metric_dict = []

    train_metric_dict.append(mean_squared_error(y_train, train_pred))
    train_metric_dict.append(mean_absolute_error(y_train, train_pred))
    train_metric_dict.append(r2_score(y_train, train_pred))

    test_metric_dict.append(mean_squared_error(y_test, test_pred))
    test_metric_dict.append(mean_absolute_error(y_test, test_pred))
    test_metric_dict.append(r2_score(y_train, train_pred))

    train_metric_dict = np.array(train_metric_dict).reshape(1, -1)
    test_metric_dict = np.array(test_metric_dict).reshape(1, -1)

    return_mat = np.column_stack((train_metric_dict, test_metric_dict))

    return return_mat

def find_median(matrix):
        medians = []
        for i in range(matrix.shape[1]):
            deg_med = np.median(matrix[:, [i]])
            medians.append(deg_med)

        return medians

if __name__ == "__main__":
    dataset = pd.read_csv(
        r"./data/covid_19_clean_complete.csv"
    )  # Read the training and test set data in
    dataset = dataset.loc[lambda a: pd.isna(a["Province/State"])]
    # countries = ['India', 'Russia', 'US', 'South Africa']#np.unique(x['Country/Region'])
    countries = np.unique(dataset["Country/Region"])
    # countries = ['Belize', 'Brazil','New Zealand', 'Serbia']

    # Initialize "empty" numpy arrays to hold aggregate metrics
    deg_1_agg = np.empty((1, 6))
    deg_2_agg = np.empty((1, 6))
    deg_3_agg = np.empty((1, 6))
    deg_4_agg = np.empty((1, 6))
    deg_5_agg = np.empty((1, 6))

    for i in countries:
        MSE_list = []
        MAE_list = []
        r2_list = []

        x = dataset.loc[lambda a: a["Country/Region"] == i]
        x = x.iloc[:, [4, 5, 8]]
        x["Date"] = pd.to_datetime(x["Date"])  # Convert the date column into datetime
        x["Date"] = x["Date"].map(dt.datetime.toordinal)  # Map datetime to ordinal

        x = pd.DataFrame(StandardScaler().fit_transform(x), columns=x.columns)

        # x_plot = np.array(x['Date']) # Save the domain for use with plotting
        y = np.array(x["Confirmed"]).reshape(-1, 1)  # The target value
        x = x.iloc[:, [0, 2]]  # Select ordinal date and active cases column

        x_train, x_test, y_train, y_test = train_test_split(
            x, y, test_size=0.33, random_state=4
        )
        x_train = np.sort(x_train, 0)
        x_test = np.sort(x_test, 0)
        x_test_plot = x_test[:, [0]]
        y_train = np.sort(y_train, 0)
        y_test = np.sort(y_test, 0)

        # Begin polynomial regression
        # Poly degree 1
        poly_feat = PolynomialFeatures(degree=1)
        x_train_poly = poly_feat.fit_transform(x_train)
        x_test_poly = poly_feat.fit_transform(x_test)

        regressor = LinearRegression()
        regressor.fit(x_train_poly, y_train)

        poly_prediction_1 = regressor.predict(x_test_poly)
        """Metrics"""
        poly_1_r2 = np.around(regressor.score(x_test_poly, y_test), 4)

        deg_1_metrics = train_test_overfitting(regressor, x, y, 1)

        # Poly degree 2
        poly_feat = PolynomialFeatures(degree=2)
        x_train_poly = poly_feat.fit_transform(x_train)
        x_test_poly = np.around(poly_feat.fit_transform(x_test), 4)

        regressor = LinearRegression()
        regressor.fit(x_train_poly, y_train)

        poly_prediction_2 = regressor.predict(x_test_poly)
        """Metrics"""
        poly_2_r2 = np.around(regressor.score(x_test_poly, y_test), 4)

        deg_2_metrics = train_test_overfitting(regressor, x, y, 2)

        # Poly degree 3
        poly_feat = PolynomialFeatures(degree=3)
        x_train_poly = poly_feat.fit_transform(x_train)
        x_test_poly = np.around(poly_feat.fit_transform(x_test), 4)

        regressor = LinearRegression()
        regressor.fit(x_train_poly, y_train)

        poly_prediction_3 = regressor.predict(x_test_poly)
        """Metrics"""
        poly_3_r2 = np.around(regressor.score(x_test_poly, y_test), 4)

        deg_3_metrics = train_test_overfitting(regressor, x, y, 3)

        # Poly degree 4
        poly_feat = PolynomialFeatures(degree=4)
        x_train_poly = poly_feat.fit_transform(x_train)
        x_test_poly = np.around(poly_feat.fit_transform(x_test), 4)

        regressor = LinearRegression()
        regressor.fit(x_train_poly, y_train)

        poly_prediction_4 = regressor.predict(x_test_poly)
        """Metrics"""
        poly_4_r2 = np.around(regressor.score(x_test_poly, y_test), 4)

        deg_4_metrics = train_test_overfitting(regressor, x, y, 4)

        # Poly degree 5
        poly_feat = PolynomialFeatures(degree=5)
        x_train_poly = poly_feat.fit_transform(x_train)
        x_test_poly = np.around(poly_feat.fit_transform(x_test), 4)

        regressor = LinearRegression()
        regressor.fit(x_train_poly, y_train)

        poly_prediction_5 = regressor.predict(x_test_poly)
        """Metrics"""
        poly_5_r2 = np.around(regressor.score(x_test_poly, y_test), 4)

        deg_5_metrics = train_test_overfitting(regressor, x, y, 5)

        plt.scatter(x_test_plot, y_test, color="grey")  # Plot the primary data points
        # Plot poly degree 1
        plt.plot(
            x_test_plot,
            poly_prediction_1,
            color="black",
            label="deg 1 " f"r\u00B2= {poly_1_r2}",
        )
        plt.plot(
            x_test_plot,
            poly_prediction_2,
            color="steelblue",
            label="deg 2 " f"r\u00B2= {poly_2_r2}",
        )
        # Plot poly degree 3
        plt.plot(
            x_test_plot,
            poly_prediction_3,
            color="green",
            label="deg 3 " f"r\u00B2= {poly_3_r2}",
        )
        plt.plot(
            x_test_plot,
            poly_prediction_4,
            color="darkorange",
            label="deg 4 " f"r\u00B2= {poly_4_r2}",
        )
        # Plot poly degree 5
        plt.plot(
            x_test_plot,
            poly_prediction_5,
            color="magenta",
            label="deg 5 " f"r\u00B2= {poly_5_r2}",
        )
        # plt.vlines(preventions, 0, max(y), color = 'blue', linestyles = 'dashed')
        plt.title("PolyReg - " + i)
        plt.xlabel("Date")
        plt.ylabel("Confirmed")
        plt.legend(loc="upper left", fontsize=12)
        plt.show()

        # Add all aggregate metrics for each country to ndarrays
        deg_1_agg = np.vstack((deg_1_agg, deg_1_metrics))
        deg_2_agg = np.vstack((deg_2_agg, deg_2_metrics))
        deg_3_agg = np.vstack((deg_3_agg, deg_3_metrics))
        deg_4_agg = np.vstack((deg_4_agg, deg_4_metrics))
        deg_5_agg = np.vstack((deg_5_agg, deg_5_metrics))

    # Empty numpy arrays initialize a random extreme value so delete that
    deg_1_agg = np.delete(deg_1_agg, 0, axis=0)
    deg_2_agg = np.delete(deg_2_agg, 0, axis=0)
    deg_3_agg = np.delete(deg_3_agg, 0, axis=0)
    deg_4_agg = np.delete(deg_4_agg, 0, axis=0)
    deg_5_agg = np.delete(deg_5_agg, 0, axis=0)

    # Find median for each degree
    deg_1_median = find_median(deg_1_agg)
    deg_2_median = find_median(deg_2_agg)
    deg_3_median = find_median(deg_3_agg)
    deg_4_median = find_median(deg_4_agg)
    deg_5_median = find_median(deg_5_agg)