def linspace(nest, mu = 0, sigma = 1, outlier = 0, distribuition = 'normal',data = 0, points = False, grid = False):

    """
    Returns a generic plot of a selected distribuition based on linspace discretization.

    Parameters
    ----------
    nest: int
        The number of estimation points.
    mu: int, optional
        Specifies the mean of distribuition.
        Defaut is 0.
    sigma: int, optional
        Specifies the standard desviation of a distribuition.
        Defaut is 1.
    outlier: int, optional
        Is the point of an outlier event, e.g outlier = 50 will put an event in -50 and +50 if mu = 0.
        Defaut is 0
    distribuition: str, optional
        Select the distribuition to analyze.
        ('normal', 'lognormal')
        Defaut is 'normal'
    data: int, optional
        If data > 0, a randon data will be inserted insted analitcs data.
        Defaut is 0.
    points: bool, optional
        If True, it will plot with the discratization points on its PDF.
        Defaut is False.
    grid: bool, optional
        If True, a grid of discatization will be show in the plot.
        Defaut is False.
    """
    import numpy as np
    import scipy.stats as sp
    import matplotlib.pyplot as plt

    ngrid = int(100e3)

    if distribuition == 'normal':
        outlier_inf = outlier_sup = outlier
        if not data:
              a,b = sp.norm.interval(0.9999, loc = mu, scale = sigma)
        else:
              d = np.random.normal(loc = mu, scale = sigma, size = data)
              a,b = min(d),max(d)

        xgrid = np.linspace(a-outlier_inf,b+outlier_sup,ngrid)
        xest = np.linspace(min(xgrid), max(xgrid), nest)
        ygrid = sp.norm.pdf(xgrid,loc = mu, scale = sigma)
        yest = sp.norm.pdf(xest, loc = mu, scale = sigma)

    elif distribuition == 'lognormal':
        outlier_inf = 0
        outlier_sup = outlier
        if not data:
              a,b = sp.lognorm.interval(0.9999, sigma, loc = 0, scale = np.exp(mu))
        else:
              d = np.random.lognormal(mean = mu, sigma = sigma, size = data)
              a,b = min(d),max(d)

        xgrid = np.linspace(a-outlier_inf,b+outlier_sup,ngrid)
        xest = np.linspace(min(xgrid), max(xgrid), nest)
        ygrid = sp.lognorm.pdf(xgrid, sigma, loc = 0, scale = np.exp(mu))
        yest = sp.lognorm.pdf(xest, sigma, loc = 0, scale = np.exp(mu))

    plt.figure(figsize = (12,8))
    plt.plot(xgrid,ygrid, label = 'PDF')

    plt.xlabel('X',fontsize = 30)
    plt.ylabel('Probability',fontsize = 30)

    if points:
        plt.plot(xest,yest,'ok', label = 'Linspace Points')
    if grid:
          plt.vlines(xest,0,yest,linestyle=':')
          plt.hlines(yest,a-outlier_inf,xest,linestyle = ':')
          plt.plot(np.zeros(nest)+a-outlier_inf,yest,'rx', ms = 5, label = 'Y points')
    plt.legend(prop = {'size':18})
    plt.xticks(size = 18)
    plt.yticks(size = 18)
    #plt.title('$\mu$ = %.1f, $\sigma$ = %.1f - Linspace' %(mu,sigma))
    plt.tight_layout()
    #plt.savefig("/media/rafael/DiscoCompartilhado/Faculdade/Bolsa - Atlas/KernelDensityEstimation-Python/Kernel-Discretization-Processes/Figures_log/linspace.png")

def CDFm(nest, mu = 0, sigma = 1, data = 0, outlier = 0, distribuition = 'normal', points = None, grid = False, PDF = False):
    """
    Returns a generic plot from CDF of a selected distribuition based on cdf discretization.

    Parameters
    ----------
    nest: int
        The number of estimation points.
    mu: int, optional
        Specifies the mean of distribuition.
        Defaut is 0.
    sigma: int, optional
        Specifies the standard desviation of a distribuition.
        Defaut is 1.
    data: int, optional
        If data > 0, a randon data will be inserted insted analitcs data.
        Defaut is 0.
    outlier: int, optional
        Is the point of an outlier event, e.g outlier = 50 will put an event in -50 and +50 if mu = 0.
        Defaut is 0
    distribuition: str, optional
        Select the distribuition to analyze.
        ('normal', 'lognormal')
        Defaut is 'normal'
    points: str, optional
        Show the estimation points along the follow plots ('PDF' or 'CDF')
        Defaut is None.
    grid: bool, optional
        If True, a grid of discatization will be show in the plot.
        Defaut is False.
    PDF: bool, optional
        If True, the PDF plot will be show.
        Defaut is False
    """

    import numpy as np
    import scipy.stats as sp
    import matplotlib.pyplot as plt
    from statsmodels.distributions import ECDF
    from scipy.interpolate import interp1d

    ngrid = int(1e6)
    eps = 5e-5
    blue = '#1f77b4ff'
    orange = '#ff7f0eff'

    if distribuition == 'normal':
        outlier_inf = outlier_sup = outlier
        if not data:
              a,b = sp.norm.interval(0.9999, loc = mu, scale = sigma)
              a,b = a-outlier_inf, b+outlier_sup

              yest = np.linspace(eps, 1-eps, nest)
              xest = sp.norm.ppf(yest, loc = mu, scale = sigma)
              ygrid = np.linspace(eps, 1-eps, ngrid)
              xgrid = sp.norm.ppf(ygrid, loc = mu, scale = sigma)

        else:
              d = np.random.normal(loc = mu, scale = sigma, size = data)
              ecdf = ECDF(d)
              a,b = min(d),max(d)
              xest = np.linspace(a,b,data)
              yest = ecdf(xest)
              interp = interp1d(yest,xest,fill_value = 'extrapolate', kind = 'linear')
              yest = np.linspace(eps,1-eps,nest)
              xest = interp(yest)
              ygrid = np.linspace(eps, 1-eps, ngrid)
              xgrid = interp(ygrid)

              #a,b = min(d)-outlier_inf,max(d)+outlier_sup

              #yest,xest = np.histogram(d,bins = 'fd',normed = True)
              #xest = np.mean(np.array([xest[:-1],xest[1:]]),0)
              #yest = np.cumsum(yest)/max(np.cumsum(yest))
              #yest = np.linspace(min(yest),max(yest),nest)

    elif distribuition == 'lognormal':
        outlier_inf = 0
        outlier_sup = outlier
        if not data:
              a,b = sp.lognorm.interval(0.9999, sigma, loc = 0, scale = np.exp(mu))
              a,b = a-outlier_inf, b+outlier_sup

              yest = np.linspace(eps, 1-eps, nest)
              xest = sp.lognorm.ppf(yest, sigma, loc = 0, scale = np.exp(mu))
              ygrid = np.linspace(eps, 1-eps, ngrid)
              xgrid = sp.lognorm.ppf(ygrid, sigma, loc = 0, scale = np.exp(mu))

        else:
              d = np.random.lognormal(mean = mu, sigma = sigma, size = data)
              #a,b = min(d),max(d)+outlier_sup
              ecdf = ECDF(d)
              a,b = min(d),max(d)
              xest = np.linspace(a,b,nest)
              yest = ecdf(xest)
              interp = interp1d(yest,xest,fill_value = 'extrapolate', kind = 'linear')
              yest = np.linspace(eps,1-eps,nest)
              xest = interp(yest)
              ygrid = np.linspace(eps, 1-eps, ngrid)
              xgrid = interp(ygrid)#sp.lognorm.ppf(ygrid, sigma, loc = 0, scale = np.exp(mu))

              #yest,xest = np.histogram(d,bins = 'fd',normed = True)
              #xest = np.mean(np.array([xest[:-1],xest[1:]]),0)
              #yest = np.cumsum(yest)/max(np.cumsum(yest))
              #yest = np.linspace(min(yest),max(yest),nest)

        x = np.linspace(a,b,ngrid)
        y = sp.lognorm.pdf(x,sigma, loc = 0, scale = np.exp(mu))

    fig, ax1 = plt.subplots(figsize = (12,8), dpi = 200)

    #ax1.set_title('$\mu$ = %.1f, $\sigma$ = %.1f - CDFm' %(mu,sigma))
    cdf, = ax1.plot(xgrid,ygrid, color = orange)
    ax1.set_ylabel('Cumulative Probability', color = orange, fontsize = 30)
    ax1.legend([cdf], ['CDF'])
    ax1.set_xlabel('x', fontsize = 30)

    if points == 'CDF':
        pts, = ax1.plot(xest,yest,'ok')
        ax1.legend([cdf,pts], ['CDF', 'Points'])

    if PDF or points == 'PDF':
        ax2 = ax1.twinx()

        pdf, = ax2.plot(x,y, color = blue)
        ax1.legend([pdf,cdf], ['PDF', 'CDF'])
        ax2.set_ylabel('Probability', color = blue, fontsize = 30)
        ax1.tick_params(axis='y', labelcolor=orange, labelsize = 18)
        ax2.tick_params(axis='y', labelcolor=blue, labelsize = 18)
        ax1.tick_params(axis='x', labelsize = 18)

        if points == 'PDF':
            if distribuition == 'lognormal':
                pts, = ax2.plot(xest,sp.lognorm.pdf(xest, sigma, loc = 0, scale = np.exp(mu)), 'ok')
            elif distribuition == 'normal':
                pts, = ax2.plot(xest,sp.norm.pdf(xest, loc = mu, scale = sigma), 'ok')
            ax1.legend([pdf,cdf,pts], ['PDF', 'CDF', 'Points'])

    else:
          ax2 = ax1
    if grid:
          ax2.vlines(xest,0,yest,linestyle=':')
          ax2.hlines(yest,a,xest,linestyle = ':')
          ypts, = ax1.plot(xest,np.zeros(nest),'rx', ms = 5)
          ax1.legend([pdf,cdf,pts,ypts], ['PDF', 'CDF', 'CDF Points', 'X Points'], prop = {'size':18}, loc = 1)
          plt.tight_layout()
          #plt.savefig("/media/rafael/DiscoCompartilhado/Faculdade/Bolsa - Atlas/KernelDensityEstimation-Python/Kernel-Discretization-Processes/Figures_log/CDFm.png")

def PDFm(nest, mu = 0, sigma = 1, data = 0, outlier = 0, distribuition = 'normal', grid = False, points = False):
    """
    Returns a generic plot from PDF of a selected distribuition based on PDFm discretization.

    Parameters
    ----------
    nest: int
        The number of estimation points.
    mu: int, optional
        Specifies the mean of distribuition.
        Defaut is 0.
    sigma: int, optional
        Specifies the standard desviation of a distribuition.
        Defaut is 1.
    data: int, optional
        If data > 0, a randon data will be inserted insted analitcs data.
        Defaut is 0.
    outlier: int, optional
        Is the point of an outlier event, e.g outlier = 50 will put an event in -50 and +50 if mu = 0.
        Defaut is 0
    distribuition: str, optional
        Select the distribuition to analyze.
        ('normal', 'lognormal')
        Defaut is 'normal'
    points: str, optional
        Show the estimation points along the follow plots ('PDF' or 'CDF')
        Defaut is False.
    grid: bool, optional
        If True, a grid of discretization will be show in the plot.
        Defaut is False.
    """

    import numpy as np
    import scipy.stats as sp
    import matplotlib.pyplot as plt
    from scipy.interpolate import interp1d

    ngrid = int(1e6)
    if not nest %2:
          nest = nest -1
    if distribuition == 'normal':

        outlier_inf = outlier_sup = outlier
        if not data:
              a,b = sp.norm.interval(0.9999, loc = mu, scale = sigma)
              a,b = a-outlier_inf, b+outlier_sup
              x = np.linspace(a,b,ngrid)
              y = sp.norm.pdf(x,loc = mu, scale = sigma)

              X1 = np.linspace(a,mu,ngrid)
              Y1 = sp.norm.pdf(X1,loc = mu, scale = sigma)
              interp = interp1d(Y1,X1)
              y1 = np.linspace(Y1[0],Y1[-1],nest//2+1)
              x1 = interp(y1)

              X2 = np.linspace(mu,b,ngrid)
              Y2 = sp.norm.pdf(X2, loc = mu, scale = sigma)
              interp = interp1d(Y2,X2)
              y2 = np.flip(y1,0)
              x2 = interp(y2)
        else:
              d = np.random.normal(mu,sigma,data)
              a,b = min(d)-outlier_inf,max(d)+outlier_sup
              yest,xest = np.histogram(d,bins = 'fd',normed = True)
              xest = np.mean(np.array([xest[:-1],xest[1:]]),0)
              M = np.where(yest == max(yest))[0][0]
              m = np.where(yest == min(yest))[0][0]
              interpL = interp1d(yest[:M+1],xest[:M+1], assume_sorted = False, fill_value= 'extrapolate')
              interpH = interp1d(yest[M:],xest[M:], assume_sorted= False, fill_value='extrapolate')

              y1 = np.linspace(yest[m],yest[M],nest//2+1)
              x1 = interpL(y1)

              y2 = np.flip(y1,0)
              x2 = interpH(y2)

        X = np.concatenate([x1[:-1],x2])
        Y = np.concatenate([y1[:-1],y2])

    elif distribuition == 'lognormal':
        outlier_inf = 0
        outlier_sup = outlier
        if not data:
              mode = np.exp(mu-sigma**2)
              a,b = sp.lognorm.interval(0.9999, sigma, loc = 0, scale = np.exp(mu))
              a,b = a-outlier_inf, b+outlier_sup
              x = np.linspace(a,b,ngrid)
              y = sp.lognorm.pdf(x,sigma, loc = 0, scale = np.exp(mu))

              X1 = np.linspace(a,mode,ngrid)
              Y1 = sp.lognorm.pdf(X1,sigma, loc = 0, scale = np.exp(mu))
              interp = interp1d(Y1,X1)
              y1 = np.linspace(Y1[0],Y1[-1],nest//2+1)
              x1 = interp(y1)

              X2 = np.linspace(mode,b,ngrid)
              Y2 = sp.lognorm.pdf(X2, sigma, loc = 0, scale = np.exp(mu))
              interp = interp1d(Y2,X2)
              y2 = np.flip(y1,0)
              x2 = interp(y2)
        else:
              d = np.random.lognormal(mu,sigma,data)
              a,b = min(d)-outlier_inf,max(d)+outlier_sup
              y,x = np.histogram(d,bins = 'fd',normed = True)
              x = np.mean(np.array([x[:-1],x[1:]]),0)
              M = np.where(y == max(y))[0][0]
              m = np.where(y == min(y))[0][0]
              interpL = interp1d(y[:M+1],x[:M+1], assume_sorted = False, fill_value= 'extrapolate')
              interpH = interp1d(y[M:],x[M:], assume_sorted= False, fill_value='extrapolate')

              y1 = np.linspace(y[m],y[M],nest//2+1)
              x1 = interpL(y1)

              y2 = np.flip(y1,0)
              x2 = interpH(y2)

        X = np.concatenate([x1[:-1],x2])
        Y = np.concatenate([y1[:-1],y2])

    #plt.figure(figsize=(12,8),dpi=200)
    plt.plot(x,y,label = 'PDF')
    plt.ylabel('Probability', fontsize = 30)
    plt.xlabel('x', fontsize = 30)
    #plt.title('$\mu$ = %.1f, $\sigma$ = %.1f - PDFm' %(mu,sigma))
    if points:
      plt.plot(X,Y,'ok', label = 'PDF points')

    if grid:
        plt.vlines(X,0,Y,linestyle=':')
        plt.hlines(Y,a,X,linestyle = ':')
        plt.plot(X,np.zeros(nest),'rx', ms = 5, label = 'X points')

    plt.legend(prop = {'size':18})
    plt.tick_params(labelsize=18)
    plt.tight_layout()
    #plt.savefig("/media/rafael/DiscoCompartilhado/Faculdade/Bolsa - Atlas/KernelDensityEstimation-Python/Kernel-Discretization-Processes/Figures_log/PDFm.png")

def iPDF1(nest, mu = 0, sigma = 1, outlier = 0, data = 0, distribuition = 'normal', points = None, grid = False,CDF = True, PDF = False, dPDF = False):
    """
    Returns a generic plot from CDF of a selected distribuition based on iPDF1 discretization.

    Parameters
    ----------
    nest: int
        The number of estimation points.
    mu: int, optional
        Specifies the mean of distribuition.
        Defaut is 0.
    sigma: int, optional
        Specifies the standard desviation of a distribuition.
        Defaut is 1.
    outlier: int, optional
        Is the point of an outlier event, e.g outlier = 50 will put an event in -50 and +50 if mu = 0.
        Defaut is 0
    data: int, optional
        If data > 0, a randon data will be inserted insted analitcs data.
        Defaut is 0.
    distribuition: str, optional
        Select the distribuition to analyze.
        ('normal', 'lognormal')
        Defaut is 'normal'
    points: str, optional
        Show the estimation points along the follow plots ('PDF' or 'CDF')
        Defaut is None.
    grid: bool, optional
        If True, a grid of discatization will be show in the plot.
        Defaut is False.
    CDF: bool, optional
        If True, the CDF plot will be show.
        Defaut is True.
    PDF: bool, optional
        If True, the PDF plot will be show.
        Defaut is False.
    dPDF: bool, optional
        If True, a plot from a derivative PDF will be ploted.
        Defaut is False.
    """

    import numpy as np
    import scipy.stats as sp
    import matplotlib.pyplot as plt
    from distAnalyze import dpdf
    from scipy.interpolate import interp1d

    ngrid = int(1e6)
    eps = 5e-5
    blue = '#1f77b4ff'
    orange = '#ff7f0eff'

    if distribuition == 'normal':
        outlier_inf = outlier_sup = outlier
        if not data:
              a,b = sp.norm.interval(0.9999, loc = mu, scale = sigma)
              a,b = a-outlier_inf, b+outlier_sup
              x = np.linspace(a,b,ngrid)
              y = dpdf(x, mu, sigma, distribuition)
              xgrid = x
        else:
              d = np.random.normal(mu,sigma,data)
              a,b = min(d)-outlier_inf,max(d)+outlier_sup

              y,x = np.histogram(d,bins = 'fd',normed = True)
              x = np.mean(np.array([x[:-1],x[1:]]),0)
              y2 = y
              y = abs(diff(mediaMovel(y,10)))
              x = xgrid = x[:-1]

    elif distribuition == 'lognormal':
        outlier_inf = 0
        outlier_sup = outlier
        if not data:
              a,b = sp.lognorm.interval(0.9999, sigma, loc = 0, scale = np.exp(mu))
              a,b = a-outlier_inf, b+outlier_sup

              x = np.linspace(a,b,ngrid)
              y = dpdf(x, mu, sigma, distribuition)
              xgrid = x

        else:
              d = np.random.lognormal(mu,sigma,data)
              a,b = min(d)-outlier_inf,max(d)+outlier_sup

              y,x = np.histogram(d,bins = 'fd',normed = True)
              x = np.mean(np.array([x[:-1],x[1:]]),0)
              y2 = y
              y = abs(diff(mediaMovel(y,10)))
              x = xgrid = x[:-1]

    difx = diff(x)[0]
    y = abs(y/(sum(y)*difx))

    ygrid = np.cumsum(y)
    ygrid = ygrid/max(ygrid)

    interp = interp1d(ygrid,xgrid, fill_value = 'extrapolate')
    yest = np.linspace(eps, max(ygrid)-eps,nest)
    xest = interp(yest)

    fig, ax1 = plt.subplots(figsize=(12,8),dpi=200)
    #ax1.set_title('$\mu$ = %.1f, $\sigma$ = %.1f - iPDF1' %(mu,sigma))

    if CDF == True:
          cdf, = ax1.plot(xgrid,ygrid, color = orange)
          ax1.set_ylabel('Cumulative Probability', color = orange, fontsize = 30)
          ax1.legend([cdf], ['CDF'])
          ax2 = ax1.twinx()
    else: ax2 = ax1

    ax1.set_xlabel('x', fontsize = 30)

    if PDF:
        if distribuition == 'normal':
              pdf, = ax2.plot(x,sp.norm.pdf(x,loc = mu, scale = sigma), color = blue)
        elif distribuition == 'lognormal':
              pdf, = ax2.plot(x,sp.lognorm.pdf(x,sigma,loc = 0, scale = np.exp(mu)), color = blue, label = 'PDF')
        ax2.set_ylabel('Probability', color = blue, fontsize = 30)

    if points == 'CDF':
        pts, = ax1.plot(xest,yest,'ok')
        ax1.legend([cdf,pts], ['CDF', 'Points'])

    if dPDF or points == 'PDF':

        if CDF:
              dpdf, = ax2.plot(x,y, color = blue)
              ax1.tick_params(axis='y', labelcolor=orange, labelsize = 18)
              ax1.tick_params(axis='x', labelsize = 18)
              ax2.tick_params(axis='y', labelcolor=blue, labelsize = 18)

        else:
              dpdf, = ax2.plot(x,y, color = orange, label = 'dPDF')
              ax2.set_ylabel('Probability',color = 'black')
              ax2.tick_params(labelsize = 18)

        #ax1.legend([dpdf,cdf], ['dPDF', 'CDF'])

        if points == 'PDF':
            if data:
                  pdf, = ax2.plot(x,y2[:-1], color = blue)
                  pts, = ax2.plot(xest,sp.lognorm.pdf(xest, sigma, loc = 0, scale = np.exp(mu)), 'ok')
            else:
                  if distribuition == 'lognormal':
                      pdf, = ax2.plot(x,sp.lognorm.pdf(x, sigma, loc = 0, scale = np.exp(mu)), color = blue)
                      pts, = ax2.plot(xest,sp.lognorm.pdf(xest, sigma, loc = 0, scale = np.exp(mu)), 'ok')

                  elif distribuition == 'normal':
                      pdf, = ax2.plot(x,sp.norm.pdf(x,loc = mu, scale = sigma), color = blue)
                      pts, = ax2.plot(xest,sp.norm.pdf(xest, loc = mu, scale = sigma), 'ok')

            ax1.legend([pdf,cdf,pts], ['PDF', 'CDF', 'iPDF1 Points'],prop = {'size':18})
    if grid:
          ax1.vlines(xest,0,yest,linestyle=':')
          ax1.hlines(yest,a,xest,linestyle = ':')
          ypts, = ax1.plot(xest,np.zeros(nest),'rx', ms = 5)
          ax1.legend([pdf,cdf,pts,ypts], ['PDF', 'CDF', 'iPDF1 Points', 'X Points'],prop = {'size':18}, loc = 1)
# =============================================================================
#     ax1.tick_params(axis='y', labelcolor=orange, labelsize = 18)
#     ax1.tick_params(axis='x', labelsize = 18)
#     ax2.tick_params(axis='y', labelcolor=blue, labelsize = 18)
# =============================================================================
    #ax1.legend(prop = {'size':18}, loc = 1)
    plt.tight_layout()
    #plt.savefig("/media/rafael/DiscoCompartilhado/Faculdade/Bolsa - Atlas/KernelDensityEstimation-Python/Kernel-Discretization-Processes/Figures_log/iPDF1_1.png")

def iPDF2(nest, mu = 0, sigma = 1, outlier = 0, data = 0, distribuition = 'normal', points = None, grid = False, PDF = False, dPDF = False, CDF = True):
    """
    Returns a generic plot from CDF of a selected distribuition based on iPDF1 discretization.

    Parameters
    ----------
    nest: int
        The number of estimation points.
    mu: int, optional
        Specifies the mean of distribuition.
        Defaut is 0.
    sigma: int, optional
        Specifies the standard desviation of a distribuition.
        Defaut is 1.
    outlier: int, optional
        Is the point of an outlier event, e.g outlier = 50 will put an event in -50 and +50 if mu = 0.
        Defaut is 0.
    data: int, optional
        If data > 0, a randon data will be inserted insted analitcs data.
        Defaut is 0.
    distribuition: str, optional
        Select the distribuition to analyze.
        ('normal', 'lognormal')
        Defaut is 'normal'
    points: str, optional
        Show the estimation points along the follow plots ('PDF' or 'CDF')
        Defaut is None.
    grid: bool, optional
        If True, a grid of discatization will be show in the plot.
        Defaut is False.
    PDF: bool, optional
        If True, the PDF plot will be show.
        Defaut is False.
    dPDF: bool, optional
        If True, a plot from a derivative PDF will be ploted.
        Defaut is False.
    """

    import numpy as np
    import scipy.stats as sp
    import matplotlib.pyplot as plt
    from distAnalyze import ddpdf
    from scipy.interpolate import interp1d

    ngrid = int(1e6)
    eps = 5e-5
    blue = '#1f77b4ff'
    orange = '#ff7f0eff'

    if distribuition == 'normal':
        outlier_inf = outlier_sup = outlier
        if not data:
              a,b = sp.norm.interval(0.9999, loc = mu, scale = sigma)
              a,b =   a-outlier_inf, b+outlier_sup

              x = np.linspace(a,b,ngrid)
              y = ddpdf(x, mu, sigma, distribuition)
              xgrid = x

        else:
              d = np.random.normal(mu,sigma,data)
              a,b = min(d)-outlier_inf,max(d)+outlier_sup

              y,x = np.histogram(d,bins = 'fd',normed = True)
              x = np.mean(np.array([x[:-1],x[1:]]),0)

              y = abs(diff(mediaMovel(y,10),2))
              xgrid = x[:-2]

    elif distribuition == 'lognormal':
        outlier_inf = 0
        outlier_sup = outlier
        if not data:
              a,b = sp.lognorm.interval(0.9999, sigma, loc = 0, scale = np.exp(mu))
              a,b = a-outlier_inf, b+outlier_sup

              x = np.linspace(a,b,ngrid)
              y = ddpdf(x, mu, sigma, distribuition)
              xgrid = x
        else:
              d = np.random.lognormal(mu,sigma,data)
              a,b = min(d)-outlier_inf,max(d)+outlier_sup

              y,x = np.histogram(d,bins = 'fd',normed = True)
              x = np.mean(np.array([x[:-1],x[1:]]),0)

              y = abs(diff(mediaMovel(y,10),2))
              xgrid = x[:-2]

    difx = np.diff(x)[0]
    y = y/(sum(y)*difx)
    #ygrid = np.sum(np.tri(ngrid)*y,1)
    ygrid = np.cumsum(y)

    ygrid = ygrid/max(ygrid)

    interp = interp1d(ygrid,xgrid, fill_value = 'extrapolate')
    yest = np.linspace(eps, max(ygrid)-eps,nest)
    xest = interp(yest)

    fig, ax1 = plt.subplots(figsize = (12,8),dpi=200)

    #ax1.set_title('$\mu$ = %.1f, $\sigma$ = %.1f - iPDF2' %(mu,sigma))
    if CDF:
          ax2 = ax1.twinx()
          cdf, = ax1.plot(xgrid,ygrid, color = orange)
          ax1.set_ylabel('Cumulative Probability', color = orange, fontsize = 30)
          ax1.legend([cdf], ['CDF'])
          ax1.set_xlabel('x', fontsize = 30)
          ax1.tick_params(axis='x', labelsize = 18)
          ax1.tick_params(axis='y', labelsize = 18, labelcolor = orange)
          ax2.tick_params(axis='y', labelsize = 18, labelcolor = blue)
          ax2.set_ylabel('Probability',color = blue, fontsize = 30)
    else:
          ax2 = ax1
          ax1.set_ylabel('Probability', fontsize = 30)
          ax1.tick_params(labelsize = 18)
          ax1.set_xlabel('x', fontsize = 30)

    if points == 'CDF':
        pts, = ax1.plot(xest,yest,'ok')
        ax1.legend([cdf,pts], ['CDF', 'Points'])

    if PDF:
        if distribuition == 'lognormal':
            pdf, = ax2.plot(x,sp.lognorm.pdf(x, sigma, loc = 0, scale = np.exp(mu)), color = blue, label = 'PDF')
        elif distribuition == 'normal':
            pdf, = ax2.plot(x,sp.norm.pdf(x,loc = mu, scale = sigma), color = blue)

    if dPDF or points == 'PDF':

        if CDF:
              color = blue
              ax2.set_ylabel('Probability', color = color, fontsize = 30)
              ax1.tick_params(axis='y', labelcolor=orange, labelsize = 18)
              ax2.tick_params(axis='y', labelcolor=blue, labelsize = 18)

        else:
              color = orange
        dpdf, = ax2.plot(x,y, color = color, label = 'dPDF2')
        #ax1.legend([dpdf,cdf], ['dPDF', 'CDF'])

        if points == 'PDF':
            if distribuition == 'lognormal':
                pdf, = ax2.plot(x,sp.lognorm.pdf(x, sigma, loc = 0, scale = np.exp(mu)), color = blue)
                pts, = ax2.plot(xest,sp.lognorm.pdf(xest, sigma, loc = 0, scale = np.exp(mu)), 'ok')

            elif distribuition == 'normal':
                pdf, = ax2.plot(x,sp.norm.pdf(x,loc = mu, scale = sigma), color = blue)
                pts, = ax2.plot(xest,sp.norm.pdf(xest, loc = mu, scale = sigma), 'ok')

            ax1.legend([pdf,cdf,pts], ['PDF', 'CDF', 'Points'])
    if grid:
          ax1.vlines(xest,0,yest,linestyle=':')
          ax1.hlines(yest,a,xest,linestyle = ':')
          ypts, = ax1.plot(xest,np.zeros(nest),'rx', ms = 5)
          ax1.legend([pdf,cdf,pts,ypts], ['PDF', 'CDF', 'iPDF2 Points', 'X Points'], prop = {'size':18}, loc = 1)
    plt.legend(prop = {'size':18}, loc = 1)
    plt.tight_layout()
    #plt.savefig("/media/rafael/DiscoCompartilhado/Faculdade/Bolsa - Atlas/KernelDensityEstimation-Python/Kernel-Discretization-Processes/Figures_log/iPDF2_1.png")

def logs(sigma = 1, mu = 0, area = 0.9999):
      from scipy.stats import lognorm
      import numpy as np
      import matplotlib.pyplot as plt
      from numpy import log, exp

      scale = median = exp(mu)
      mode = exp(mu - sigma**2)
      mean = exp(mu + (sigma**2/2))
      shape = sigma
      a,b = lognorm.interval(area, shape, loc = 0, scale = np.exp(mu))
      x = np.linspace(a,b,1000000)
      mode = exp(mu - shape**2)
      mean = exp(mu + (shape**2/2))
      pdf = lognorm.pdf(x,shape, loc = 0, scale = scale)
      plt.figure(figsize = (12,8), dpi=200)
      plt.plot(x,pdf, label = 'PDF ($\sigma = %.2f$)' %shape)
      plt.vlines(mode,0,pdf.max(), linestyle = ':', label = 'Mode = %.2f' %mode)
      plt.vlines(mean,0,lognorm.pdf(mean,shape,loc=0,scale=scale),color = 'green', linestyle = '--',label = 'Mean = %.2f' %mean)
      plt.vlines(median,0,lognorm.pdf(median,shape,loc=0,scale=scale),color = 'blue', label = 'Median = %.2f' %median)
      plt.legend(loc = 1)

      plt.legend(prop = {'size':18})
      plt.xlabel('x', fontsize = 45)
      plt.ylabel('Probability', fontsize = 45)
      plt.xticks(size = 18)
      plt.yticks(size = 18)
      plt.tight_layout()
      #plt.savefig("/media/rafael/DiscoCompartilhado/Faculdade/Bolsa - Atlas/KernelDensityEstimation-Python/Kernel-Discretization-Processes/Figures_log/sigma_%.2f.png" %sigma)

def mediaMovel(x,n):
      for i in range(len(x)):
            if i < n//2:
                  x[i] = mean(x[:n//2])
            else:
                  x[i] = mean(x[i-n//2:i+n//2])
      return x

import numpy,from numpy,import scipy,from scipy,from statsmodels


# Sheldon D'Souza
# Objective of the weekly task is to display a plot of the functions f(x)=x, g(x)=x2 and h(x)=x3 in the range [0, 4] on the one set of axes.
# I have assumed that 4 elements need to be plotted range [0,4] although range(0,4) would exclude the last element 4.
# The graph can be plotted using numpy functions as well as using lists with for loops
# I have for the purposes of demonstration used one of each although it would be simpler here to use numpy only
# I used some of the matplotlib.org, datacamp.com and realpython tutorials for some of the design items below

import numpy as np
import matplotlib.pyplot as plt

x = np.array([0,1,2,3,4]) # using numpy to create a array with 5 elements i.e in the range from 0 to 4 inclusive

g_x = np.array(x ** 2) # generates g_x as x squared

h_x = [] # define a blank list and fill it using a for loop
x_1 = []
for n in range(0,5):
    x_1.append(n) # creates a list for the x axis
    h_x.append(n ** 3) # creates a list for the y axis

# The following code will plot and format:

plt.plot(x, x, color = 'g', label = 'f(x) = x', ls = '--', linewidth = 3.0) #f(x) = x is just a linear graph so x and y will be the same
plt.plot(x, g_x, color='b', label = 'g(x) = x**2', ls = '-.', linewidth = 2.0)
plt.plot(x_1, h_x, color = 'r', label = 'h(x) = x**3', ls = ':', linewidth = 4.0) # Plot the h(x) function using lists and the for loop

plt.title("Plotting Functions", fontsize = 15, loc = 'center')
plt.xlabel("X axis", fontsize = 10)
plt.xlim(0)
plt.ylabel('Y axis', fontsize = 10)
plt.legend(loc='best')

plt.tight_layout()

# plt.savefig('functions.png') # Can add the save function if needed

plt.show()


import matplotlib.pyplot as plt
import numpy as np

def f(x,y):
	return 4.0*x

def Euler1(f,x_start,y_start,h,x_max):
	x_array, y_array, dydx_array = [], [], []
	while abs(x_start)<abs(x_max):
		dydx = f(x_start,y_start)
		x_array.append(x_start);y_array.append(y_start);dydx_array.append(dydx)
		y_start = y_start + h*dydx
		x_start = x_start + h
	return x_array, y_array

it = ['blue','green','yellow']

x_start , y_start, h, x_max = 0.0, 0.0, 0.01, 10.0
xx = np.arange(0.0,10.0,0.5)
i = 0
h = 0.01
while h<1.05:
	x_start = 0
	y_start = 0
	Xarr , Yarr = Euler1(f,x_start,y_start,h,x_max)
	#print(Xarr)
	#print(Yarr)
	tt = "h=%.4f"%(h)
	plt.plot(Xarr,Yarr,it[i],label=tt)
	h += 0.5
	i += 1
plt.plot(xx,2*xx**2,'red',label="Exact Solution")
plt.legend(loc='best',prop={'size':12})
plt.xlabel("x");plt.ylabel("y")
plt.title("Comparison between true and Euler solution:")
plt.show()


# Stochastic processes course 2018/2019-2 @ ELTE - Pál Balázs
# Assignment 4: Random-walk simulation with Monte Carlo method
## 03.11.2019

## Random-Walk on a 1D lattice with equilibrium:
#
# Initially a particle rests on a 1D lattice with lattice constant $a$ in one of its grid points.
# It is also attached with a rubber rope with $D$ spring constant to the $x=0$ point. In every step,
# the particle moves in the positive, or negative direction, always jumping between discrete, adjacent
# lattice points. This means that at each step it will jump to one of the grid points adjacent to its
# current position.
# At every step, we calculate the difference of the potential energy between the origin and the end
# point of a single jump. If $\Delta E < 0$, then we execute the step in the simulation.
# If $\Delta E > 0$, we choose a random value $X$, where $X \in \left[ 0; 1 \right]$.
# If $X < e^{- \beta \Delta E}$ we also execute the step, else we do nothing, leave the particle
# unmoved and continue with the next step in the simulation.
#
# Characteristic parameters of the system (for me):
#
# $$
# \beta D a^{2}
# =
# \frac{D a^{2}}{k_{B} T}
# =
# \left\{ 0.06,\ 0.24,\ 0.52,\ 1.35 \right\} \quad - \quad \text{Pál Balázs}
# $$

################
### NEW CELL ###
################

import numpy as np
import matplotlib.pyplot as plt
import sys
import os
import random
import seaborn as sns

sns.set_style(style='whitegrid')

################
### NEW CELL ###
################

# Boltzmann constant
k_B = 1.380648e-23

# Arbitrary spring constant of the rubber band, fixing the moving particle
D = 1e-03

# Arbitrary lattice constant of the grid
# Distance between discrete points in the 1D lattice
# a = 5 * 1e-10 approximatly corresponds to real materials
a = 5 * 1e-10

# Scale factor for the microscopic system
# beta = 1/(k_B * T)
beta_1 = 0.06 / (D * a**2)
print('At first case T = {0} K'.format(1 / (beta_1 * k_B)))
beta_2 = 0.24 / (D * a**2)
print('At second case T = {0} K'.format(1 / (beta_2 * k_B)))
beta_3 = 0.52 / (D * a**2)
print('At third case T = {0} K'.format(1 / (beta_3 * k_B)))
beta_4 = 1.35 / (D * a**2)
print('At fourth case T = {0} K'.format(1 / (beta_4 * k_B)))

################
### NEW CELL ###
################

## 1.: Choose direction for the 1D random-walk on the X-axis
def choose_sign():
    return -1 if random.random() < 0.5 else 1

################
### NEW CELL ###
################

### Testing sign choosing function
### Means of random +1 and -1 values have an expected value of 0
nrows = 5
ncols = 5
picsize = 30
titlesize = 20
axislabelsize = 18
fig, axes = plt.subplots(nrows=nrows, ncols=ncols, figsize=(picsize,picsize))

n = 1000

for k in range(0,nrows):
    for j in range(0,ncols):
        indeces = [i for i in range(0,n)]
        randoms = [choose_sign() for i in range(0,n)]
        means = [np.mean(randoms[:(i+1)]) for i in range(0,n)]

        axes[j][k].plot(indeces, means, c='red')

        axes[j][k].set_ylim(-1,1)

        axes[j][k].set_xlabel('Number of +/- flips [n]', fontsize=axislabelsize)
        axes[j][k].set_ylabel('Mean of +/- values after n flip', fontsize=axislabelsize)

        axes[j][k].axhline(y=0, color='green')

fig.tight_layout()
plt.show()

################
### NEW CELL ###
################

## 2.: $E_{n}$ energy of the current energy level
def E(energy_level, a, D):
    return 1/2 * D * a**2 * energy_level**2

################
### NEW CELL ###
################

## 3.: Step with the simulation
def simulation_step(beta, start, steps):

    # Chronologies for reached energy levels' indeces and actual energies in order
    energy_levels_chronology = np.zeros((steps+1), dtype='int')
    energy_chronology = np.zeros((steps+1))
    # Starting position on X axis
    energy_levels_chronology[0] = start
    energy_chronology[0] = E(energy_level = energy_levels_chronology[0],
                             a = a,
                             D = D)

    for current_step in range(0,steps):
        # Choose a direction for the 1D random-walk on the X-axis
        # +1 return value indicates stepping in the positive (right) direction
        # -1 return value indicates steping in the negative (left) direction
        direction = choose_sign()

        # Calculate energy difference between the jump's origin and end points
        E_1 = E(energy_level = energy_levels_chronology[current_step],
                a = a,
                D = D)

        E_2 = E(energy_level = energy_levels_chronology[current_step] + direction * 1,
                a = a,
                D = D)

        delta_E = E_2 - E_1

        # Update chronology of reached energy levels' indeces
        # If delta_E <= 0, then execute the step
        if(delta_E <= 0):
            energy_levels_chronology[current_step+1] = energy_levels_chronology[current_step] + direction * 1

        # If delta_E > 0, choose a random number X, between 0 and 1
        # If X < e^(-beta * delta_E), then execute the step
        # If not, then leave the particle at its current place and continue with the next step
        else:
            if(random.random() < np.exp(- beta * delta_E)):
                energy_levels_chronology[current_step+1] = energy_levels_chronology[current_step] + direction * 1
            else:
                energy_levels_chronology[current_step+1] = energy_levels_chronology[current_step]

        # Update chronology of actual reached energy levels
        energy_chronology[current_step+1] = E(energy_level = energy_levels_chronology[current_step+1],
                                              a = a,
                                              D = D)

    return energy_levels_chronology, energy_chronology

################
### NEW CELL ###
################

## Run functions with arbitrary initial conditons
steps = 10000
start = -250

betas = {'1': beta_1,
         '2': beta_2,
         '3': beta_3,
         '4': beta_4
        }

# Dicts for storing the energy levels' indeces and actual energies for various betas
energy_levels_chronology = {}
energy_chronology = {}

# Run the simulation
for i in range(0,len(betas)):
    energy_levels_chronology[str(i+1)], energy_chronology[str(i+1)] = simulation_step(beta=betas[str(i+1)],
                                                                                start=start,
                                                                                steps=steps)

step_indeces = np.asarray([i for i in range(0,steps+1)])

################
### NEW CELL ###
################

## Plotting propagation of the particle throughout energy levels
nrows = 2
ncols = 2
fig, axes = plt.subplots(nrows=nrows, ncols=ncols, figsize=(20,20))

for i in range(0,nrows):
    for j in range(0,ncols):

        axes[i][j].axhline(y=0, color='green', label='Equilibrium')
        axes[i][j].plot(step_indeces, energy_levels_chronology[str(i*nrows + j + 1)], c='red', label='Discrete energy levels of the particle')

        axes[i][j].set_title('Discrete energy level propagation at $T =${0:.3f} K'.format(1 / (betas[str(i*nrows + j + 1)] * k_B)), fontsize=50/ncols)

        axes[i][j].set_xlabel('Number of steps [$N$]', fontsize=40/ncols)
        axes[i][j].set_ylabel('Energy level [$n$]', fontsize=40/ncols)

        axes[i][j].tick_params(axis='both', which='major', labelsize=30/ncols)

        axes[i][j].legend(fontsize=30/ncols)

fig.tight_layout()
plt.show()

################
### NEW CELL ###
################

nrows = 2
ncols = 2
fig, axes = plt.subplots(nrows=nrows, ncols=ncols, figsize=(20,20))

for i in range(0,nrows):
    for j in range(0,ncols):

        axes[i][j].axhline(y=0, color='green', label='Equilibrium')
        axes[i][j].plot(step_indeces, energy_chronology[str(i*nrows + j + 1)], c='red', label='Energy of the particle')

        axes[i][j].set_title('Actual energies $T =${0:.3f} K'.format(1 / (betas[str(i*nrows + j + 1)] * k_B)), fontsize=50/ncols)

        axes[i][j].set_xlabel('Time [$N \\cdot \\tau$]', fontsize=40/ncols)
        axes[i][j].set_ylabel('Energy [J]', fontsize=40/ncols)

        axes[i][j].tick_params(axis='both', which='major', labelsize=30/ncols)
        axes[i][j].yaxis.get_offset_text().set_fontsize(25/ncols)

        axes[i][j].legend(fontsize=30/ncols)
fig.tight_layout()
plt.show()

################
### NEW CELL ###
################

## Test various initial conditions for one choosen $T$ temperature
### The choosen $T$ will be here $T = \frac{1}{\beta_{1} k_{B}} = 301.791\ \text{K}$
steps = 10000
choosen_beta = beta_1

# Dicts for storing the energy levels' indeces and actual energies for various betas
energy_levels_chronology_fixedT = {}
energy_chronology_fixedT = {}

# Run the simulation
for i in range(0,25):
    start = -250 + i * 20
    energy_levels_chronology_fixedT[str(i+1)], energy_chronology_fixedT[str(i+1)] = simulation_step(beta=choosen_beta,
                                                                                start=start,
                                                                                steps=steps)

step_indeces = np.asarray([i for i in range(0,steps+1)])

################
### NEW CELL ###
################

nrows = 5
ncols = 5
fig, axes = plt.subplots(nrows=nrows, ncols=ncols, figsize=(50,50))

titlesize=25
axislabelsize=18

for i in range(0,nrows):
    for j in range(0,ncols):

        axes[i][j].axhline(y=0, color='green', label='Equilibrium')
        axes[i][j].plot(step_indeces, energy_levels_chronology_fixedT[str(i*nrows + j + 1)], c='red', label='Discrete energy levels of the particle')

        axes[i][j].set_title('Discrete energy level propagation at $T =${0:.3f} K\nStarting point at level {1}'.format(1 / (choosen_beta * k_B),
                                                                                -250 + (i*nrows + j) * 20),
                             fontsize=titlesize)

        axes[i][j].set_xlabel('Number of steps [$N$]', fontsize=axislabelsize)
        axes[i][j].set_ylabel('Energy level [$n$]', fontsize=axislabelsize)

        axes[i][j].tick_params(axis='both', which='major', labelsize=18)

        axes[i][j].legend(fontsize=15)

fig.tight_layout()
plt.show()

################
### NEW CELL ###
################

nrows = 5
ncols = 5
fig, axes = plt.subplots(nrows=nrows, ncols=ncols, figsize=(50,50))

titlesize=22
axislabelsize=19

for i in range(0,nrows):
    for j in range(0,ncols):

        axes[i][j].axhline(y=0, color='green', label='Equilibrium')
        axes[i][j].plot(step_indeces, energy_chronology_fixedT[str(i*nrows + j + 1)], c='red', label='Energy of the particle')

        axes[i][j].set_title('Discrete energy levels at $T =${0:.3f} K\nStarting point at level {1}'.format(1 / (choosen_beta * k_B),
                                                                                -250 + (i*nrows + j) * 20),
                             fontsize=titlesize)

        axes[i][j].set_xlabel('Number of steps [$N$]', fontsize=axislabelsize)
        axes[i][j].set_ylabel('Energy [J]', fontsize=axislabelsize)

        axes[i][j].tick_params(axis='both', which='major', labelsize=15)
        axes[i][j].yaxis.get_offset_text().set_fontsize(10)

        axes[i][j].legend(fontsize=15)

fig.tight_layout()
plt.show()

################
### NEW CELL ###
################

## Examine $\left< n \right>$ and $\left< n^{2} \right>$ expected values
steps = 10000
start = -250
runs = 25

# Dicts for storing the energy levels' indeces and actual energies for various betas
energy_levels_chronology_expected = {}
energy_chronology_expected = {}

# Run the simulation
for j in range(0,len(betas)):
    for i in range(0,runs):
        (energy_levels_chronology_expected[str(j*runs + i + 1)],
        energy_chronology_expected[str(j*runs + i + 1)]) = simulation_step(beta=betas[str(j+1)],
                                                                                start=start,
                                                                                steps=steps)

# Storages for mean data of n and n^2 values for various T temperatures
# These dictionaries store means for every runs, for every T temperature (len == runs * len(betas))
temp_means_levels_expected = {}
temp_means_levels_expected_squared = {}
temp_means_energy_expected = {}
temp_means_energy_expected_squared = {}
# Calculate means of x and x^2 displacements
for j in range(0,len(betas)*runs):
    temp_means_levels_expected[str(j+1)] = np.asarray([np.mean(energy_levels_chronology_expected[str(j+1)][:(i+1)]) for i in range(0, steps+1)])
    temp_means_levels_expected_squared[str(j+1)] = np.asarray([np.mean((energy_levels_chronology_expected[str(j+1)][:(i+1)])**2) for i in range(0, steps+1)])

    temp_means_energy_expected[str(j+1)] = np.asarray([np.mean(energy_chronology_expected[str(j+1)][:(i+1)]) for i in range(0, steps+1)])
    temp_means_energy_expected_squared[str(j+1)] = np.asarray([np.mean((energy_chronology_expected[str(j+1)][:(i+1)])**2) for i in range(0, steps+1)])

# Storages for means of the above data
# These dictionaries stores the mean of N runs for various T temperature (len == len(betas))
means_levels_expected = {}
means_levels_expected_squared = {}
means_energy_expected = {}
means_energy_expected_squared = {}
# Temporary storages for means of each previous runs, when iterating throug them
temp_mean_levels = np.zeros((len(temp_means_levels_expected['1'])))
temp_mean_levels_squared = np.zeros((len(temp_means_levels_expected_squared['1'])))
temp_mean_energy = np.zeros((len(temp_means_energy_expected['1'])))
temp_mean_energy_squared = np.zeros((len(temp_means_energy_expected_squared['1'])))
# Calculate overall means for each betas (or temperatures)
for i in range(0,len(betas)):
    for j in range(0,runs):
        temp_mean_levels += temp_means_levels_expected[str(i*runs + j + 1)]
        temp_mean_levels_squared += temp_means_levels_expected_squared[str(i*runs + j + 1)]
        temp_mean_energy += temp_means_energy_expected[str(i*runs + j + 1)]
        temp_mean_energy_squared += temp_means_energy_expected_squared[str(i*runs + j + 1)]

    means_levels_expected[str(i+1)] = temp_mean_levels/runs
    means_levels_expected_squared[str(i+1)] = temp_mean_levels_squared/runs
    means_energy_expected[str(i+1)] = temp_mean_energy/runs
    means_energy_expected_squared[str(i+1)] = temp_mean_energy_squared/runs

    # Reset the temporary arrays for the next beta (or temperature)
    temp_mean_levels = np.zeros((len(temp_means_levels_expected['1'])))
    temp_mean_levels_squared = np.zeros((len(temp_means_levels_expected_squared['1'])))
    temp_mean_energy = np.zeros((len(temp_means_energy_expected['1'])))
    temp_mean_energy_squared = np.zeros((len(temp_means_energy_expected_squared['1'])))

step_indeces = np.asarray([i for i in range(0,steps+1)])

################
### NEW CELL ###
################

nrows = 6
ncols = 4
fig, axes = plt.subplots(nrows=nrows, ncols=ncols, figsize=(30,45))

titlesize = 20
axislabelsize = 15

# Initialize graphs with equilibrium line and axis subtitles
for i in range(0,nrows):
    for j in range(0,ncols):
        axes[i][j].axhline(y=0, color='green', label='Equilibrium')

        axes[i][j].set_xlabel('Number of steps [$N$]', fontsize=axislabelsize)

        axes[i][j].tick_params(axis='both', which='major', labelsize=12)
        axes[i][j].yaxis.get_offset_text().set_fontsize(10)

# Mean of occupied discrete energy levels (this is the <n> expected value for displacement)
for j in range(0,4):
    axes[0][j].plot(step_indeces, means_levels_expected[str(j+1)],
                    c='red', label='Mean displacement')
    axes[0][j].set_title('Mean of occupied discrete\nenergy levels ($\\left< n \\right>$) at $T = ${0:.3f}'.format(1 / (betas[str(j+1)] * k_B)),
                         fontsize=titlesize)

    axes[0][j].set_ylabel('Energy level [$n$]', fontsize=axislabelsize)

# Mean of occupied discrete energy levels squared (this is the <n^2> expected value for displacement squared)
for j in range(0,4):
    axes[1][j].plot(step_indeces, means_levels_expected_squared[str(j+1)],
                      c='red', label='Mean displacement squared')

    axes[1][j].set_title('Mean of occupied discrete\nenergy levels squared ($\\left< n^2 \\right>$) at $T = ${0:.3f}'.format(1 / (betas[str(j+1)] * k_B)),
                           fontsize=titlesize)

    axes[1][j].set_ylabel('Energy level$^{2}$ [n$^{2}$]', fontsize=axislabelsize)

# Mean of occupied discrete energy levels (this is the <x> expected value for displacement)
for j in range(0,4):
    axes[2][j].plot(step_indeces, means_levels_expected[str(j+1)] * a,
                    c='red', label='Mean displacement')
    axes[2][j].set_title('Mean of displacements ($\\left< x \\right>$)\nat $T = ${0:.3f}'.format(1 / (betas[str(j+1)] * k_B)),
                         fontsize=titlesize)

    axes[2][j].set_ylabel('Displacement [$a \\cdot n$]', fontsize=axislabelsize)

# Mean of occupied discrete energy levels squared (this is the <x^2> expected value for displacement squared)
for j in range(0,4):
    axes[3][j].plot(step_indeces, means_levels_expected_squared[str(j+1)] * a**2,
                      c='red', label='Mean displacement squared')

    axes[3][j].set_title('Mean of displacements squared ($\\left< x^2 \\right>$)\nat $T = ${0:.3f}'.format(1 / (betas[str(j+1)] * k_B)),
                           fontsize=titlesize)

    axes[3][j].set_ylabel('Displacement$^{2}$ [$a^{2} \\cdot n^{2}$]', fontsize=axislabelsize)

# Mean of actual occupied energies
for j in range(0,4):
    axes[4][j].plot(step_indeces, means_energy_expected[str(j+1)],
                      c='red', label='Mean energy')

    axes[4][j].set_title('Mean of occupied energies ($\\left< E \\right>$)\nat $T = ${0:.3f}'.format(1 / (betas[str(j+1)] * k_B)),
                           fontsize=titlesize)

    axes[4][j].set_ylabel('Energy [J]', fontsize=axislabelsize)

# Mean of actual occupied energies squared
for j in range(0,4):
    axes[5][j].plot(step_indeces, means_energy_expected_squared[str(j+1)],
                      c='red', label='Mean energy squared')

    axes[5][j].set_title('Mean of occupied energies\nsquared ($\\left< E^2 \\right>$) at $T = ${0:.3f}'.format(1 / (betas[str(j+1)] * k_B)),
                           fontsize=titlesize)

    axes[5][j].set_ylabel('Energy$^{2}$ [J$^{2}$]', fontsize=axislabelsize)

# Add legends on graphs
for i in range(0,nrows):
    for j in range(0,ncols):
        axes[i][j].legend(fontsize=15)

fig.tight_layout()
plt.show()

################
### NEW CELL ###
################

## Oscillation of $\left< x^{2} \right> - \left< x \right>^{2}$ around equilibrium
#
# We can get $\left< x \right>^{2} = a^{2} \cdot \left< n \right>^{2}$ and similarly
# $\left< x^{2} \right> = a^{2} \cdot \left< n^{2} \right>$.

oscillation_levels = {}
oscillation_energy = {}
for i in range(0, len(means_energy_expected)):
    oscillation_levels[str(i+1)] = means_levels_expected_squared[str(i+1)] * a**2 - means_levels_expected[str(i+1)]**2 * a**2
    oscillation_energy[str(i+1)] = means_energy_expected_squared[str(i+1)] - means_energy_expected[str(i+1)]**2

################
### NEW CELL ###
################

nrows = 2
ncols = 4
fig, axes = plt.subplots(nrows=nrows, ncols=ncols, figsize=(40,20))

titlesize = 25
axislabelsize = 20

# Initialize graphs with equilibrium line and axis subtitles
for i in range(0,nrows):
    for j in range(0,ncols):
        axes[i][j].axhline(y=0, color='green', label='Equilibrium')

        axes[i][j].set_xlabel('Number of steps', fontsize=axislabelsize)

        axes[i][j].tick_params(axis='both', which='major', labelsize=15)
        axes[i][j].yaxis.get_offset_text().set_fontsize(12)

# Oscillation of energy levels (this is the <x^2> - <x>^2 expected value)
for j in range(0,4):
    axes[0][j].plot(step_indeces, oscillation_levels[str(j+1)],
                    c='red', label='$\\left< x^{2} \\right> - \\left< x \\right>^{2}$')

    axes[0][j].set_title('Oscillation of $\\left< x^2 \\right> - \\left< x \\right>^2$ at $T = ${0:.3f}'.format(1 / (betas[str(j+1)] * k_B)),
                         fontsize=titlesize)

    axes[0][j].set_ylabel('Displacement$^{2}$ [$a^{2} \\cdot n^{2}$]', fontsize=axislabelsize)

# Mean of actual occupied energies (this is the <E^2> - <E>^2 expected value)
for j in range(0,4):
    axes[1][j].plot(step_indeces, oscillation_energy[str(j+1)],
                      c='red', label='$\\left< E^{2} \\right> - \\left< E \\right>^{2}$')

    axes[1][j].set_title('Oscillation of $\\left< E^2 \\right> - \\left< E \\right>^2$ at $T = ${0:.3f}'.format(1 / (betas[str(j+1)] * k_B)),
                           fontsize=titlesize)

    axes[1][j].set_ylabel('Energy$^{2}$ [J$^{2}$]', fontsize=axislabelsize)

# Add legends on graphs
for i in range(0,nrows):
    for j in range(0,ncols):
        axes[i][j].legend(fontsize=15)

fig.tight_layout()
plt.show()

Lecture 4/Homework 4/original sent file/Pal_Balazs_04_code.py


from sympy import *

# A psychologist contends that the number of facts of a certain type that are remembered after t hours is given by the following function.

t = symbols( 't' )
F = 86*t / ( 99*t - 86 )
dF = diff( F, t )

# Find the rate of change at which the number of facts remembered is changing after 1 hour and after 10 hours.

# The rate of change after 1 hour is about
one_hr = dF.subs( { t: 1 } )
# (Round to the nearest integer as needed)
round( one_hr )

# The rate of change after 10 hours is about
ten_hr = dF.subs( { t: 10 } ).evalf()
# (Round to the nearest integer as needed)
round( ten_hr )


# numexpr is a package which allows for direct-access to C-speed operations without
# intermediate object generation. Pandas methods eval() and query() are dependent on it

# this chapter covers the pandas eval() and query() operations, as per usual,
# the basics, good rules-of-thumb, and coventions generally followed.

### Motivating query() and eval() --- Compound Expressions (and numexpr demonstration):
# Recall Vectorized operations in NP and PD:
import numpy as np
rng = np.random.RandomState(42)
x = rng.rand(1000000)
y = rng.rand(1000000)
%timeit x + y           # recall anaconda/ipython magics

%timeit np.fromiter((xi + yi for xi, yi in zip(x, y)), dtype=x.dtype, count=len(x))

# however, our vectorized ops are less efficient for compound expressions:
mask = (x > 0.5) & (y < 0.5)
# this is because NP evals each subexpression seperately, roughly, doing this...
tmp1 = (x > 0.5)
tmp2 = (y < 0.5)
mask = tmp1 & tmp2
# ... allocating our intermediate steps in memory, introducing the same inefficiency as before

import numexpr
mask_numexpr = numexpr.evaluate('(x > 0.5) & (y < 0.5)')
np.allclose(mask, mask_numexpr)

### pandas.eval() for Efficient Operations
# eval() in PD uses string expressions to compute operations using DataFrames, efficiently.
import pandas as pd
nrows, ncols = 100000, 100
rng = np.random.RandomState(42)
df1, df2, df3, df4 = (pd.DataFrame(rng.rand(nrows, ncols))
                      for i in range(4))

%timeit df1 + df2 + df3 + df4

%timeit pd.eval('df1 + df2 + df3 + df4')

### Operations supported by pd.eval()
# demonstration of supported ops as of PD v0.16 in pd.eval()
df1, df2, df3, df4, df5 = (pd.Dataframe(rng.randint(0, 1000, (100, 3)))
                           for i in range(5))

## Arithmetic Operators
# All arithmetic operators are supported
result1 = -df1 * df2 / (df3 + df4) - df5
result2 = pd.eval('-df1 * df2 / (df3 + df4) - df5')
np.allclose(result1, result2)

## Comparison Operators
# all comparison operators, including chained expressions:
result1 = (df1 < df2) & (df2 <= df3) & (df3 != df4)
result2 = pd.eval('df1 < df2 <= df3 != df4')
np.allclose(result1, result2)

## Bitwise Operators
# supports the & and | bitwise operators
result1 = (df1 < 0.5) & (df2 < 0.5) | (df3 < df4)
result2 = pd.eval('(df1 < 0.5) & (df2 < 0.5) | (df3 < df4)')
np.allclose(result1, result2)

# also supports the use of literal "and" and "or" in Bool expressions
result3 = pd.eval('(df1 < 0.5) and (df2 < 0.5) or (df3 < df4)')
np.allclose(result1, result3)

## Other Attributes and Indices
# pd.eval() supports access to:
    # object attributes     via     obj.attr    syntax
    # index  attributes     via     obj[index]  syntax
result1 = df2.T[0] + df3.iloc[1]
result2 = pd.eval('df2.T[0] + df3.iloc[1]')
np.allclose(result1, result2)

## Other Operations
# otherwise not mentioned ops [function calls, conditional statements, loops]
# are all NOT implemented within pd.eval() - you would need full numexpr lib

### DataFrame.eval() for Column-Wise Operations
# pd.eval() is top-level, DFs have their own eval() which is similar
# benefit: columns can be referred to by name
df = pd.DataFrame(rng.rand(1000, 3), columns=['A', 'B', 'C'])
df.head()

result1 = (df['A'] + df['B']) / (df['C'] - 1)
result2 = pd.eval("df.A + df.B) / (df.C - 1)")
np.allclose(result1, result2)
# even MORE concise: treat col names as variables!
result3 = df.eval('(A + B) / (C - 1)')
np.allclose(result1, result3)

## Assignment in DataFrame.eval()
# create column "D" as result of arithmetic operations on A,B, & C
df.eval('D = (A + B) / C', inplace=True)
df.head()
# re-assign existing column "D" (in same way)
df.eval('D = (A - B) / C', inplace=True)
df.head()

## Local variables in DataFrame.eval()
# adding @ sign; df.eval() will plug local Python vars, not DF cols
column_mean = df.mean(1)
result1 = df['A' + column_mean]
result2 = df.eval('A + @column_mean')
np.allclose(result1, result2)

### DataFrame.query() Method
# DataFrame has another method on evaluated strings called query()
# This is both a more efficient and easy-to-comprehend op to masking.
result1 = df[(df.A < 0.5) & (df.B < 0.5)]
result2 = pd.eval('df[(df.A < 0.5) & (df.B < 0.5)]')
np.allclose(result1, result2)

# Do not express using DataFrame.eval() syntax - use query() method. (?)
result2 = df.query('A < 0.5 and B < 0.5')
np.allclose(result1, result2)

# query also supports @ flag to mark local vars
Cmean = df['C'].mean()
result1 = df[(df.A < Cmean) & (df.B < Cmean)]
result2 = df.query('A < @Cmean and B < @Cmean')
np.allclose(result1, result2)

### Performance Tradeoffs: When to use these functions
# Two considerations: Computation Time, Memory Use
# recall: all compound expressions involving NP arrs or PD DFs creates tmp arrs

# For example, these two blocks are performing roughly equivalent work:
x = df[(df.A < 0.5) & (df.B < 0.5)]

tmp1 = df.A < 0.5
tmp2 = df.B < 0.5
tmp3 = tmp1 & tmp2
x = df[tmp3]

# if the size of your tmp DataFrames are significant compared to system RAM
# (several GBs) then you would want to use eval() or query() exprs.

# recall: can get approximate size of arr in bytes with:
df.values.nbytes

# sometimes eval() is faster even without maxing-out system memory.
# You'd want to compare temp arr size to the size of L1 or L2 CPU caches on your system
# primarily though, it's about preventing memory utilization issues.

# For more performance notes, see referenced documentation:
    # General index, lots of performance-specific tutorials
    # http://pandas-docs.github.io/pandas-docs-travis/

# Cython extension notes (for use with ipython)
# http://pandas-docs.github.io/pandas-docs-travis/user_guide/enhancingperf.html
%load_ext Cython


'''
Python program to get all strobogrammatic numbers that are of length

A strobogrammatic number is a number whose numeral is
rotationally symmetric, so that it appears the same when rotated
180 degrees. In other words, the numeral looks the same rightside
up and upside down (e.g., 69, 96, 1001).
For example,
Given n = 2, return ["11", "69", "88", "96"].
Given n = 3, return ['818', '111', '916', '619', '808', '101', '906', '609',
'888', '181', '986', '689']

'''

#https://github.com/keon/algorithms/blob/master/math/generate_strobogrammtic.py

from numpy import result_type

def gen_strobogrammatic (n):
    """
    :type n: int
    :rtype: List [str]
    """

    result = helper (n,n)
    return result

def helper (n, length):
    if n == 0:
        return [""]
    if n == 1:
        return ["1", "0", "8"]
    middles = helper (n-2, length)
    result = []
    for middle in middles:
        if n != length:
            result.append ("0" + middle + "0")
        result.append ("8" + middle + "8")
        result.append ("1" + middle + "1")
        result.append ("9" + middle + "6")
        result.append ("6" + middle + "9")
    return result
print ("n = 2: \n" , gen_strobogrammatic(2))
print ("n = 3: \n" , gen_strobogrammatic(3))
print ("n = 4: \n" , gen_strobogrammatic (4) )


from __future__ import division

import numpy as np
from scipy.optimize import fsolve

def dahlquist(_, x, lam):
    """
    dahlquist test equation ode.
    :param _: place holder for time, not used
    :param x: x value
    :param lam: lambda
    :return: slope dx/dt
    """
    dx = lam * x
    return dx

def dahlquist_ref(t, x0, lam):
    """
    reference solution for dahlquist test equation ode. x' = lam*x -> y = x0 * exp(lam*t)
    :param t: time
    :param x0: initial value
    :param lam: lambda
    :return: samples of reference solution for time t
    """
    x_ref = np.exp(lam * t) * x0  # analytical solution of the dahlquist test equation
    return x_ref

def definition_area(t, x):
    """
    for the initial value x0 = 1 this ODE only has a solution for x in (-sqrt(2),sqrt(2)). Therefore the ode is only
    defined in a certain area.
    :param t: time
    :param x: x value
    :return: slope dx/dt
    """
    dx = t * x ** 2
    return dx

def definition_area_ref(t, x0):
    """
    reference solution for ode with respricted definition area.
    :param t: time
    :param x0: initial value
    :return: samples of reference solution for time t
    """
    x_ref = 1. / (1. / x0 - 1. / 2. * (t ** 2))  # analytical solution of this ODE
    return x_ref

def logistic_equation(_, x, k, g):
    """
    ode for the logistic equation
    :param _: place holder for time, not used
    :param x: x value
    :param k: slope of logistic equation
    :param g: upper bound of logistic equation
    :return: slope dx/dt
    """
    dx = k * x * (g - x)
    return dx

def logistic_equation_ref(t, x0, k, g):
    """
    reference solution for logistic equation ode
    :param t: time
    :param x0: initial value
    :param k: slope of logistic equation
    :param g: upper bound of logistic equation
    :return: samples of reference solution for time t
    """
    if 0 != x0:
        x_ref = g * 1 / (1 + np.exp(-k * g * t) * (g / x0 - 1))
    else:
        x_ref = 0
    return x_ref

def oscillator_equation(_, x, omega):
    """
    two dimensionaly ode describing the harmonic oszillator
    :param _: place holder for time, not used
    :param x: x value
    :param omega: frequency of oszillation
    :return: slope dx/dt
    """
    A = np.array([[0, 1], [-omega ** 2, 0]])
    dx = np.dot(A, x)
    return dx

def oscillator_equation_ref(t, x0, omega, v0=0):
    """
    reference solution for two dimensional ode describing the harmonic oszillator
    :param t: time
    :param x0: initial displacements
    :param omega: frequency of oszillation
    :param v0: initial velocity
    :return: samples of reference solution (only displacement) for time t
    """
    x = x0 * np.exp(1j * omega * t) + v0 * np.exp(-1j * omega * t)
    return np.real(x)

def ref_sol(f_ref, x0, t_min = 0, t_max = 1, n_samples = 1000):
    """
    computes samples of the reference solution for a given timespan
    :param f_ref: reference solution function handle
    :param x0: initial value of ode
    :param t_min: starting time
    :param t_max: end time
    :param n_samples: number of samples to be produced
    :return: tuple of time and x value samples of the reference solution
    """
    t_ref = np.linspace(t_min, t_max, n_samples)
    x_ref = f_ref(t_ref, x0)
    return t_ref, x_ref

def expl_euler(f, x0, h, timespan):
    """
    explicit euler solver. Computes the solution for a given ode using explicit euler scheme.
    :param f: function handle for ode
    :param x0: initial value
    :param h: constant step size
    :param timespan: integration time
    :return: numerical solution in time and x
    """
    n = int(np.ceil(timespan / h))
    t = np.empty(n + 1)
    x = np.empty([x0.shape[0], n + 1])

    t[0] = 0
    x[:, 0] = x0
    for k in range(n):
        dx = f(t[k], x[:, k])
        t[k + 1] = (k + 1) * h
        x[:, k + 1] = x[:, k] + dx * h

    return t, x

def impl_euler(f, x0, h, timespan):
    """
    implicit euler solver. Computes the solution for a given ode using implicit euler scheme.
    :param f: function handle for ode
    :param x0: initial value
    :param h: constant step size
    :param timespan: integration time
    :return: numerical solution in time and x
    """
    n = int(np.ceil(timespan / h))
    t = np.empty(n + 1)
    x = np.empty([x0.shape[0], n + 1])

    t[0] = 0
    x[:, 0] = x0
    for k in range(n):
        t[k + 1] = (k + 1) * h
        try:
            x[:, k + 1] = fsolve(lambda arg: x[:, k] - arg + h * f(t[k + 1], arg), x[:, k])
        except RuntimeError:
            print("newton did not converge!")
            for k in range(k, n):
                t[k + 1] = (k + 1) * h
            break
    return t, x

def impl_midpoint(f, x0, h, timespan):
    """
    implicit midpoint rule solver. Computes the solution for a given ode using the implicit midpoint rule scheme.
    :param f: function handle for ode
    :param x0: initial value
    :param h: constant step size
    :param timespan: integration time
    :return: numerical solution in time and x
    """
    n = int(np.ceil(timespan / h))
    t = np.empty(n + 1)
    x = np.empty([x0.shape[0], n + 1])

    t[0] = 0
    x[:, 0] = x0
    for k in range(n):
        t[k + 1] = (k + 1) * h
        try:
            dx_left = f(t[k], x[:, k])
            x[:, k + 1] = fsolve(lambda arg: x[:, k] - arg + h / 2 * (f(t[k + 1], arg) + dx_left), x[:, k])
        except RuntimeError:
            print("newton did not converge!")
            for k in range(k, n):
                t[k + 1] = (k + 1) * h
            break
    return t, x


import numpy as np

def perceptron(X, y):
    '''
    PERCEPTRON Perceptron Learning Algorithm.

       INPUT:  X: training sample features, P-by-N matrix.
               y: training sample labels, 1-by-N row vector.

       OUTPUT: w:    learned perceptron parameters, (P+1)-by-1 column vector.
               iter: number of iterations

    '''
    P, N = X.shape
    w = np.zeros((P + 1, 1))
    iters = 0
    MAX_ITERS = 1e4
    # YOUR CODE HERE

    # begin answer
    while(iters < MAX_ITERS):
        done = True
        for i in range(N):
                # for every sample
                iters += 1
                xi = np.hstack((np.ones((1)), X[:,i]))
                xi = xi.reshape(xi.shape[0], 1)
                tmp = np.matmul(w.T, xi)
                yi_hat = np.sign(tmp)
                if(yi_hat[0][0] != y[0][i]):
                        done = False
                        w = w + xi*y[0][i]
        if(done == True):
                # ok
                break
    # end answer

    return w, iters


import numpy as np
import pdb

def p1(range_max, factor_list):
    """Problem 1
    If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23.

    Find the sum of all the multiples of 3 or 5 below 1000.

    """
    range_min = 0

    sum_multiples = 0

    for num in range(range_max):
        if num % factor_list[0] == 0:
            sum_multiples = sum_multiples + num
        elif num % factor_list[1] == 0:
            sum_multiples = sum_multiples + num

            # print(num)

    print("Summation of multiples is: %g" % sum_multiples)

    return sum_multiples

def p2(max):
    """Problem 2
    Each new term in the Fibonacci sequence is generated by adding the previous two terms. By starting with 1 and 2, the first 10 terms will be:

    1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ...

    By considering the terms in the Fibonacci sequence whose values do not exceed four million, find the sum of the even-valued terms.
    """

    fib_num = [1,2]
    sum_even = 0
    while fib_num[-1] < max:
        # check if next fibonacci number is even
        if (fib_num[-1] % 2) == 0:
            sum_even = sum_even + fib_num[-1]

        next_num = fib_num[-2]+fib_num[-1]

        fib_num.append(next_num)

    print("Sum of even Fibonacci numbers less than %f is %g" % (max, sum_even))

    return sum_even
def prime_sieve_erathosthenes(n):
    """Sieve of Erathosthenes

    Output the list of all primes less than n
    """

    not_prime = []
    prime = []

    if n < 2:
        print("No primes less than 2")
        return 1

    for i in range(2, n+1):
        if i not in not_prime:
            prime.append(i)
            for j in range(i*i,n+1, i):
                not_prime.append(j)

    return prime

def gen_primes(n):
    """Yield primes less than n
    """
    D = {}
    q = 2
    while q < n:
        if q not in D:
            yield q

            D[q * q] = [q]
        else:
            for p in D[q]:
                D.setdefault(p + q, []).append(p)
            del D[q]

        q += 1

def primesfrom2to(num):
    """ Input n>=6, Returns a array of primes, 2 <= p < n """

    sieve = np.ones(num//3 + (num%6==2), dtype=np.bool)
    for i in range(1,int(num**0.5)//3+1):
        if sieve[i]:
            k=3*i+1|1
            sieve[       k*k//3     ::2*k] = False
            sieve[k*(k-2*(i&1)+4)//3::2*k] = False
    return np.r_[2,3,((3*np.nonzero(sieve)[0][1:]+1)|1)]

def primesfrom3to(n):
    """ Returns a array of primes, 3 <= p < n """
    sieve = numpy.ones(n/2, dtype=numpy.bool)
    for i in xrange(3,int(n**0.5)+1,2):
        if sieve[i/2]:
            sieve[i*i/2::i] = False
    return 2*numpy.nonzero(sieve)[0][1::]+1

def p3(num):
    """Problem 3
    The prime factors of 13195 are 5, 7, 13 and 29.

    What is the largest prime factor of the number 600851475143 ?
    """
    if num < 2:
        print("No primes less than 2")
        return []
    largest_factor = 0
    for p in gen_primes(num//2 + 1):
        if num % p == 0:
            # this is a prime factor
            if p > largest_factor:
                largest_factor = p

    return largest_factor


from typing import Callable, Any

import numpy as np

def root(f: Callable[[float], float], x0: float, df: Callable[[float], float] = None,
         epsilon: float = 10e-9, delta: float = 10e-6, stop: int = 10e3, xn1: float = None, progress=False):
    """
    find a root of the function, using the newton's method if derivative is available, or the secant method if not.

    :param f: univariate function
    :param df: derivative of f(x)
    :param x0: initial guess for the solution of f(x) = 0
    :param epsilon: largest permissible value of abs(f(x)) when solution is found
    :param delta: largest permissible distance between two consecutive approximations when solution is found
    :param stop: maximum iterations
    :param xn1: one of the first initial guesses needed to initialize secant method
    :param progress: whether to print the convergence progress

    :return: a root
    """
    if df is None:
        # secant method
        if xn1 is None:
            xn1 = x0 + 0.1
        new, old = x0, xn1
        fnx, fox = f(new), f(old)
        while (abs(fnx > epsilon) or abs(new - old) > delta) and stop > 0:
            if progress:
                print(new)
            oldest = old
            old = new
            new = old - fnx * (old - oldest) / (fnx - fox)
            fnx, fox = f(new), f(old)
            stop -= 1
    else:
        # newton method
        new, old = x0, x0 + 1
        fx, dfx = f(new), df(new)
        while (abs(fx > epsilon) or abs(new - old) > delta) and stop > 0:
            old = new
            new = old - fx / dfx
            fx, dfx = f(new), df(new)
            stop -= 1
            if progress:
                print(new)
    return new

def integrate(f: Callable[[float], float], a: float, b: float, epsilon: float = 10e-8, stop: int = 10e3):
    """
    calculate the definite integral of a function

    :param f: function
    :param a: left endpoint
    :param b: right endpoint
    :param epsilon: maximum tolerance
    :param stop: maximum iterations

    :return: approximated definite integral with error less than epsilon
    """
    old = simpsons(f, a, b, 4)
    new = simpsons(f, a, b, 8)
    n = 8
    while abs(old - new) > epsilon and stop > 0:
        old = new
        n *= 2
        new = simpsons(f, a, b, n)
        stop -= 1
    return new

def simpsons(f: Callable[[Any], Any], a: float, b: float, n: int):
    """
    approximate the definite integral of a function using simpson's method

    :param f: function
    :param a: left endpoint
    :param b: right endpoint
    :param n: partitions

    :return: approximated definite integral
    """
    x = np.linspace(a, b, n + 1)
    y = f(x)
    s = np.sum(y[0:-1:2] + y[2::2] + 4 * y[1::2])
    return s * (b - a) / (3 * n)


import numpy as np
import timeit
import warnings

class KMeansBase:
    def __init__(self, data, k):
        self.data = data
        self.k = k

    def cluster(self):
        return self._lloyds_iterations()

    def _initial_centroids(self):
        # get the initial set of centroids
        # get k random numbers between 0 and the number of rows in the data set
        centroid_indexes = np.random.choice(range(self.data.shape[0]), self.k, replace=False)
        # get the corresponding data points
        return self.data[centroid_indexes, :]

    def _lloyds_iterations(self):
        #warnings.simplefilter("error")
        centroids = self._initial_centroids()
        #print('Initial Centroids:', centroids)

        stabilized = False

        j_values = []
        iterations = 0
        while (not stabilized) and (iterations < 1000):
            print ('iteration counter: ', iterations)
            try:
                # find the Euclidean distance between a center and a data point
                # centroids array shape = k x m
                # data array shape = n x m
                # In order to broadcast it, we have to introduce a third dimension into data
                # data array becomes n x 1 x m
                # now as a result of broadcasting, both array sizes will be n x k x m
                data_ex = self.data[:, np.newaxis, :]
                euclidean_dist = (data_ex - centroids) ** 2
                # now take the summation of all distances along the 3rd axis(length of the dimension is m).
                # This will be the total distance from each centroid for each data point.
                # resulting vector will be of size n x k
                distance_arr = np.sum(euclidean_dist, axis=2)

                # now we need to find out to which cluster each data point belongs.
                # Use a matrix of n x k where [i,j] = 1 if the ith data point belongs
                # to cluster j.
                min_location = np.zeros(distance_arr.shape)
                min_location[range(distance_arr.shape[0]), np.argmin(distance_arr, axis=1)] = 1

                # calculate J
                j_val = np.sum(distance_arr[min_location == True])
                j_values.append(j_val)

                # calculates the new centroids
                new_centroids = np.empty(centroids.shape)
                for col in range(0, self.k):
                    if self.data[min_location[:, col] == True,:].shape[0] == 0:
                        new_centroids[col] = centroids[col]
                    else:
                        new_centroids[col] = np.mean(self.data[min_location[:, col] == True, :], axis=0)

                # compare centroids to see if they are equal or not
                if self._compare_centroids(centroids, new_centroids):
                    # it has resulted in the same centroids.
                    stabilized = True
                else:
                    centroids = new_centroids
            except:
                print ('exception!')
                continue
            else:
                iterations += 1

        print ('Required ', iterations, ' iterations to stabilize.')
        return iterations, j_values, centroids, min_location

    def _compare_centroids(self, old_centroids, new_centroids, precision=-1):
        if precision == -1:
            return np.array_equal(old_centroids, new_centroids)
        else:
            diff = np.sum((new_centroids - old_centroids)**2, axis=1)
            if np.max(diff) <= precision:
                return True
            else:
                return False

    def initCost(self):
        t = timeit.Timer(lambda: self._initial_centroids())
        return t.timeit(number=10)


# %%
import matplotlib.pyplot as plt
import numpy as np
from sklearn.datasets import fetch_openml, make_swiss_roll
from sklearn.decomposition import PCA, IncrementalPCA, KernelPCA
from sklearn.linear_model import LogisticRegression
from sklearn.manifold import LocallyLinearEmbedding, TSNE
from sklearn.metrics import mean_squared_error
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.pipeline import Pipeline

# %%
np.random.seed(4)

# %%
m = 60
w1, w2 = 0.1, 0.3
noise = 0.1

# %%
angles = np.random.rand(m) * 3 * np.pi / 2 - 0.5
X = np.empty((m, 3))
X[:, 0] = np.cos(angles) + np.sin(angles) / 2 + noise * np.random.randn(m) / 2
X[:, 1] = np.sin(angles) * 0.7 + noise * np.random.randn(m) / 2
X[:, 2] = X[:, 0] * w1 + X[:, 1] * w2 + noise * np.random.randn(m)

# %%
X_centered = X - X.mean(axis=0)
U, s, Vt = np.linalg.svd(X_centered)
c1 = Vt.T[:, 0]
c2 = Vt.T[:, 1]
print(f"Shapes: U: {U.shape}, s: {s.shape}, Vt: {Vt.shape}")

# %%
m, n = X_centered.shape

S = np.zeros(X_centered.shape)
S[:n, :n] = np.diag(s)

# %%
print(f"X_centered = U @ S @ Vt? {np.allclose(X_centered, U @ S @ Vt)}")

# %%
W2 = Vt.T[:, :2]
X2D_v1 = X_centered.dot(W2)

# %%
pca = PCA(n_components=2)
X2D = pca.fit_transform(X)
print(f"X2d == X2d_v1? {np.allclose(np.abs(X2D), np.abs(X2D_v1))}")
# print(f"Components:\n{pca.components_.T}")
# print(f"First component: {pca.components_.T[:, 0]}")
print(f"First component: {pca.components_[0]}")
print(f"Explained variance: {pca.explained_variance_}")
print(f"Explained variance ratio: {pca.explained_variance_ratio_}")

# %%
X_train, X_val = train_test_split(X, test_size=0.2)

# %%
pca = PCA()
pca.fit(X_train)
cumsum = np.cumsum(pca.explained_variance_ratio_)
d = np.argmax(cumsum >= 0.95) + 1

# %%
pca = PCA(n_components=0.95)
X_reduced = pca.fit_transform(X_train)
print(f"Explained variance ratio: {pca.explained_variance_ratio_} "
      f"({np.sum(pca.explained_variance_ratio_):.3f})")

# %%
if "mnist" in globals():
    mnist = globals()["mnist"]
else:
    mnist = fetch_openml("mnist_784", version=1, as_frame=False)
    mnist.target = mnist.target.astype(np.uint8)

# %%
X = mnist["data"]
y = mnist["target"]

X_train, X_test, y_train, y_test = train_test_split(X, y)

# %%
pca = PCA()
pca.fit(X_train)
cumsum = np.cumsum(pca.explained_variance_ratio_)
d = np.argmax(cumsum >= 0.95) + 1

# %%
plt.figure(figsize=(6, 4), dpi=300)
plt.plot(cumsum, linewidth=3)
plt.axis([0, 400, 0, 1])
plt.xlabel("Dimensions")
plt.ylabel("Explained Variance")
plt.plot([d, d], [0, 0.95], "k:")
plt.plot([0, d], [0.95, 0.95], "k:")
plt.plot(d, 0.95, "ko")
plt.grid(True)
plt.show()

# %%
pca = PCA(n_components=0.95)
X_reduced = pca.fit_transform(X_train)
X_recovered = pca.inverse_transform(X_reduced)

# %%
print(f"Components: {pca.components_.shape[0]}")

# %%
def plot_digits(instances, images_per_row=5, **options):
    size = 28
    images_per_row = min(len(instances), images_per_row)
    n_rows = (len(instances) - 1) // images_per_row + 1
    n_empty = n_rows * images_per_row - len(instances)
    padded_instances = np.concatenate([instances, np.zeros((n_empty, size * size))], axis=0)
    image_grid = padded_instances.reshape((n_rows, images_per_row, size, size))
    big_image = image_grid.transpose(0, 2, 1, 3).reshape(n_rows * size, images_per_row * size)
    plt.imshow(big_image, cmap="binary", **options)
    plt.axis("off")

# %%
# noinspection PyShadowingNames
def plot_recovered(X_recovered):
    plt.figure(figsize=(7, 4), dpi=300)
    plt.subplot(121)
    plot_digits(X_train[::2100])
    plt.title("Original")
    plt.subplot(122)
    plot_digits(X_recovered[::2100])
    plt.title("Compressed")

# %%
plot_recovered(X_recovered)
plt.show()

# %%
pca = PCA(n_components=32)
X_reduced = pca.fit_transform(X_train)
X_recovered = pca.inverse_transform(X_reduced)

# %%
plot_recovered(X_recovered)
plt.show()

# %%
rnd_pca = PCA(n_components=154, svd_solver="randomized")
X_reduced = rnd_pca.fit_transform(X_train)
X_recovered = rnd_pca.inverse_transform(X_reduced)

# %%
plot_recovered(X_recovered)
plt.show()

# %%
n_batches = 100
inc_pca = IncrementalPCA(n_components=154)

# %%
for X_batch in np.array_split(X_train, n_batches):
    inc_pca.partial_fit(X_batch)

# %%
X_reduced = inc_pca.transform(X_train)
X_recovered = inc_pca.inverse_transform(X_reduced)

# %%
plot_recovered(X_recovered)
plt.show()

# %%
X, t = make_swiss_roll(n_samples=1000, noise=0.2, random_state=42)
y = t > 6.9

# %%
rbf_pca = KernelPCA(n_components=2, kernel="rbf", gamma=0.04)
X_reduced = rbf_pca.fit_transform(X)

# %%
plt.scatter(X_reduced[:, 0], X_reduced[:, 1], c=t, cmap="hot")
plt.show()

# %%
clf = Pipeline([
    ("kpca", KernelPCA(n_components=2)),
    ("log_reg", LogisticRegression()),
])

# %%
param_grid = [{
    "kpca__gamma": np.linspace(0.03, 0.05, 10),
    "kpca__kernel": ["rbf", "sigmoid"]
}]

# %%
grid_search = GridSearchCV(clf, param_grid, cv=3)
grid_search.fit(X, y)

# %%
print(f"Best parameters: {grid_search.best_params_}")

# %%
rbf_pca = KernelPCA(n_components=2, kernel="rbf", gamma=0.0433, fit_inverse_transform=True)
X_reduced = rbf_pca.fit_transform(X)
X_preimage = rbf_pca.inverse_transform(X_reduced)

# %%
print(f"MSE for Kernel PCA: {mean_squared_error(X, X_preimage):.1f}")

# %%
lle = LocallyLinearEmbedding(n_components=2, n_neighbors=10, random_state=42)
X_reduced = lle.fit_transform(X)

# %%
plt.scatter(X_reduced[:, 0], X_reduced[:, 1], c=t, cmap="hot")
plt.show()

# %%
tsne = TSNE(n_components=2, learning_rate="auto", init="random", n_jobs=48, random_state=42)
X_reduced = tsne.fit_transform(X)

# %%
plt.scatter(X_reduced[:, 0], X_reduced[:, 1], c=t, cmap="hot")
plt.show()


## 2. Our dataset ##

import pandas

# Set index_col to False to avoid pandas thinking that the first column is row indexes (it's age).
income = pandas.read_csv("income.csv", index_col=False)
print(income.head(5))

## 3. Converting categorical variables ##

# Convert a single column from text categories into numbers.
col = pandas.Categorical.from_array(income["workclass"])
income["workclass"] = col.codes
print(income["workclass"].head(5))
cols = ['education', 'marital_status', 'occupation', 'relationship', 'race', 'sex', 'native_country','high_income']
for item in cols:
    cat = pandas.Categorical.from_array(income[item])
    income[item]=cat.codes

## 5. Performing a split ##

# Enter your code here.
private_incomes = income[income['workclass']==4]
public_incomes = income[income['workclass']!=4]

## 8. Entropy ##

import math
# We'll do the same calculation we did above, but in Python.
# Passing 2 as the second parameter to math.log will take a base 2 log.
entropy = -(2/5 * math.log(2/5, 2) + 3/5 * math.log(3/5, 2))
print(entropy)
countts = income['high_income'].value_counts()
income_entropy = -(countts[0]/len(income['high_income']) * math.log(countts[0]/len(income['high_income']),2) + countts[1]/len(income['high_income']) * math.log(countts[1]/len(income['high_income']),2))
print(income_entropy)

## 9. Information gain ##

import numpy

def calc_entropy(column):
    """
    Calculate entropy given a pandas Series, list, or numpy array.
    """
    # Compute the counts of each unique value in the column.
    counts = numpy.bincount(column)
    # Divide by the total column length to get a probability.
    probabilities = counts / len(column)

    # Initialize the entropy to 0.
    entropy = 0
    # Loop through the probabilities, and add each one to the total entropy.
    for prob in probabilities:
        if prob > 0:
            entropy += prob * math.log(prob, 2)

    return -entropy

# Verify our function matches our answer from earlier.
entropy = calc_entropy([1,1,0,0,1])
print(entropy)

information_gain = entropy - ((.8 * calc_entropy([1,1,0,0])) + (.2 * calc_entropy([1])))
print(information_gain)
income_entropy = calc_entropy(income["high_income"])

median_age = income["age"].median()

left_split = income[income["age"] <= median_age]
right_split = income[income["age"] > median_age]

age_information_gain = income_entropy - ((left_split.shape[0] / income.shape[0]) * calc_entropy(left_split["high_income"]) + ((right_split.shape[0] / income.shape[0]) * calc_entropy(right_split["high_income"])))

## 10. Finding the best split ##

def calc_information_gain(data, split_name, target_name):
    """
    Calculate information gain given a dataset, column to split on, and target.
    """
    # Calculate original entropy.
    original_entropy = calc_entropy(data[target_name])

    # Find the median of the column we're splitting.
    column = data[split_name]
    median = column.median()

    # Make two subsets of the data based on the median.
    left_split = data[column <= median]
    right_split = data[column > median]

    # Loop through the splits, and calculate the subset entropy.
    to_subtract = 0
    for subset in [left_split, right_split]:
        prob = (subset.shape[0] / data.shape[0])
        to_subtract += prob * calc_entropy(subset[target_name])

    # Return information gain.
    return original_entropy - to_subtract

# Verify that our answer is the same as in the last screen.
print(calc_information_gain(income, "age", "high_income"))

columns = ["age", "workclass", "education_num", "marital_status", "occupation", "relationship", "race", "sex", "hours_per_week", "native_country"]
information_gains = []
for item in columns:
    info = calc_information_gain(income,item,'high_income')
    information_gains.append(info)
highest_gain_index = information_gains.index(max(information_gains))
highest_gain = columns[highest_gain_index]

Decision Trees/Introduction to Decision Trees-137.py


#!/usr/bin/env python3

import numpy as np

# 1
myarray = np.array([1, 22.4, 5, 35, 4, 6.7, 3, 8, 40])

print(myarray.ndim)
print(myarray.shape)
print(myarray.size)
print(myarray.dtype)

# 2
mymatrix = np.array([['a', 'b'], ['c', 'd'], [3, 3]])

print(mymatrix.ndim)
print(mymatrix.shape)
print(mymatrix.size)
print(mymatrix.dtype)

# 3
array_arange = np.arange(3, 10)
array_linspace = np.linspace(3, 10)
array_rand = np.random.rand(1, 3)

print(array_arange)
print(array_linspace)
print(array_rand)

# 4
matrix_zeros = np.zeros((3, 3))
matrix_ones = np.ones((3, 3))
matrix_eye = np.eye(3)
matrix_rand = np.random.rand(3, 3)

print(matrix_zeros)
print(matrix_ones)
print(matrix_eye)
print(matrix_rand)

# 5
sevens_array = np.ones(20) * 7
reshaped_sevens = sevens_array.reshape(4, 5)
print(reshaped_sevens)

# 6
random_matrix = np.random.rand(6, 6)
print(random_matrix[0, 0])
print(random_matrix[0:2])
print(random_matrix[4:6])
print(random_matrix[2:4])
print(random_matrix[5:6])
print(random_matrix[2:4, 2:4])

for i in range(0, random_matrix[0].size):
    print(random_matrix[:, i].sum())
    print(random_matrix[:, i].mean())
    print(random_matrix[:, i].max())
    print(random_matrix[:, i].min())


from __future__ import annotations

import numpy as np

from edutorch.typing import NPArray

from .module import Module

class SpatialGroupNorm(Module):
    def __init__(self, num_features: int, G: int, eps: float = 1e-5) -> None:
        super().__init__()
        self.eps = eps
        self.G = G

        self.gamma = np.zeros(num_features)
        self.beta = np.zeros(num_features)
        self.set_parameters("gamma", "beta")

    def forward(self, x: NPArray) -> NPArray:
        """
        Computes the forward pass for spatial group normalization.
        In contrast to layer normalization, group normalization splits each entry
        in the data into G contiguous pieces, which it then normalizes independently.
        Per feature shifting and scaling are then applied to the data, in a manner
        identical to that of batch normalization and layer normalization.

        Inputs:
        - x: Input data of shape (N, C, H, W)
        - gamma: Scale parameter, of shape (C,)
        - beta: Shift parameter, of shape (C,)
        - G: Integer mumber of groups to split into, should be a divisor of C
        - gn_param: Dictionary with the following keys:
        - eps: Constant for numeric stability

        Returns a tuple of:
        - out: Output data, of shape (N, C, H, W)
        - cache: Values needed for the backward pass
        """
        N, C, H, W = x.shape
        self.gamma = self.gamma.reshape((1, C, 1, 1))
        self.beta = self.beta.reshape((1, C, 1, 1))

        x = x.reshape(N * self.G, -1).T
        sample_mean = np.mean(x, axis=0)
        sample_var = np.var(x, axis=0)
        v = np.sqrt(sample_var + self.eps)
        x_hat = (x - sample_mean) / v
        x_hat = x_hat.T.reshape(N, C, H, W)
        out = self.gamma * x_hat + self.beta

        self.cache = (x_hat, v)

        return out

    def backward(self, dout: NPArray) -> tuple[NPArray, ...]:
        """
        Computes the backward pass for spatial group normalization.

        Inputs:
        - dout: Upstream derivatives, of shape (N, C, H, W)
        - cache: Values from the forward pass

        Returns a tuple of:
        - dx: Gradient with respect to inputs, of shape (N, C, H, W)
        - dgamma: Gradient with respect to scale parameter, of shape (C,)
        - dbeta: Gradient with respect to shift parameter, of shape (C,)
        """
        x_hat, v = self.cache

        N, C, H, W = dout.shape
        dx_hat = dout * self.gamma
        dgamma = np.sum(dout * x_hat, axis=(0, 2, 3), keepdims=True)
        dbeta = np.sum(dout, axis=(0, 2, 3), keepdims=True)

        x_hat = x_hat.reshape(N * self.G, -1).T
        dx_hat = dx_hat.reshape(N * self.G, -1).T
        dx = (
            dx_hat - np.mean(dx_hat, axis=0) - x_hat * np.mean(dx_hat * x_hat, axis=0)
        ) / v
        dx = dx.T.reshape(N, C, H, W)

        return dx, dgamma, dbeta


import math
import numpy as np
import itertools

def approximate_equilibrium(row_payoff, col_payoff, epsilon, max_k=30):
    """
    Returns an tuple representing a strategy pair that is an epsilon-approximate
    Nash equilibrium solution based on the given payoff matrices.
    """
    # Do some error checking
    if isinstance(row_payoff, list) and isinstance(col_payoff, list):
        row_payoff = np.array(row_payoff)
        col_payoff = np.array(col_payoff)

    if row_payoff.shape != col_payoff.shape:
        raise ValueError("Payoff matrices differ in shape!")

    row_shape = row_payoff.shape
    if len(row_shape) != 2 or row_shape[0] != row_shape[1]:
        raise ValueError("Payoff matrix is not a square matrix")

    # Calculate our parameters
    n = row_shape[0]
    k = math.ceil((12 * math.log(n)) / (epsilon * epsilon))
    print("Chose k = ", k)

    # Normalize our payoff matrices and epsilon
    row_payoff = normalize(row_payoff)
    col_payoff = normalize(col_payoff)

    if max_k is not None and k > max_k:
        print(f"k={k} has too long of an expected runtime. Switching to k={max_k}")
        k = max_k

    # Calculate the k multisets
    indices = np.arange(n)
    row_multisets = itertools.product(indices, repeat=k)
    col_multisets = itertools.product(indices, repeat=k)

    # Iterate over the (k^2) strategies profiles
    for row_multiset in row_multisets:
        for col_multiset in col_multisets:

            # Convert the pair of multisets into mixed strategies
            row_strategy = _to_mixed_strategy(row_multiset, k, n)
            col_strategy = _to_mixed_strategy(col_multiset, k, n)

            # Spend O(n)  to check whether our solution is correct
            if _is_approximate(row_strategy, col_strategy, row_payoff, epsilon, n):
                if _is_approximate(col_strategy, row_strategy, col_payoff.T, epsilon, n):
                    return (row_strategy, col_strategy)

    # Should never happen
    return None

def _is_approximate(row_strategy, col_strategy, payoff, epsilon, n, debug=False):
    """
    Returns True if the given k-uniform mixed strategy is an epsilon-approximate solution.
    Returns False otherwise.
    """
    col = np.dot(payoff, col_strategy)
    row_reward = np.inner(row_strategy, col)

    deviations = np.identity(n)
    pure_rewards = np.inner(deviations, col)

    if debug:
        print(pure_rewards - row_reward)

    return np.all(np.abs(pure_rewards - row_reward) <= epsilon)

def normalize(matrix):
    """
    Returns the given numpy array with values scaled to [0, 1]
    """
    min_val = np.min(matrix)
    max_val = np.max(matrix)
    scaling_factor = (max_val - min_val)

    normalized = (matrix - min_val) / scaling_factor
    return normalized

def _to_mixed_strategy(multiset, k, size):
    """
    Returns a k-uniform mixed strategy based on the given multiset
    """
    # Convert to numpy array for speed
    multiset = np.array(multiset)

    # Track the probability of events
    values, alpha = np.unique(multiset, return_counts=True)

    # Convert to mixed strategy
    strategy = np.zeros(size)
    strategy[values] = alpha / k

    return strategy

def main():
    print("Try running the test function instead!")

if __name__ == '__main__':
    main()


import numpy as np

## Mean ##
data = np.array([])
mean = np.mean(data)

## Standard Deviation ##
data = np.array([])
std = np.std(data)

## Variance ##
data = np.array([])
var = np.std(data)**2

## Sample estimator of standard deviation ##
data = np.array([])

def seStd(data):
    n = 0
    for e in data:
        n += (e-np.mean(data))**2
    n = n/(len(data)-1)
    n = np.sqrt(n)
    return n

samstd = seStd(data)

## Standard Error of mean ##
def calculateStandardErrorMean(data):
    return np.std(data)/np.sqrt(len(data))

## Confidence interval for population mean (95%) ##
def calculateConfidenceIntervalMean(data):
    return f"{np.mean(data)} +/- {1.96*(np.std(data)/np.sqrt(len(data)))}"

## Standard Error of proportion ##
# p = proportion
def calculateStandardErrorProportion(data,p):
    return np.sqrt(p*(1-p)/len(data))

## Confidence interval for proportion (95%) ##
# p = proportion
# n = sample size
def calculateConfidenceIntervalProportion(p,n):
    return f"{p} +/- {1.96*np.sqrt(p*(1-p)/n)}"

## Agresti-Coull interval for proportion (95%) ##
# p = proportion
# n = sample size
def calculateAgrestiCoullInterval(p,n):
    p2 = (p*n+2)/(n+4)
    n2 = n+4
    x = (1.96*np.sqrt(p2*(1-p2)/n2))
    return f"{p2} +/- {x}"

## Sample size for mean (95%) ##
# s = Expected sample variance
# e = Desired margin of error (eg. 0.05 as 5% margin of error)
def calculateSampleSizeMean(s,e):
    return (1.96**2)*(s**2/e**2)

## Sample size for proportion (95%) ##
# p = Expected proportion (Worst case scenaria: 0.5)
# e = Desired margin of error (eg. 0.05 as 5% margin of error)
def calculateSampleSizeProportion(p,e):
    return (1.96**2)*(p*(1-p)/e**2)

## Covariance ##
data = np.array([])
cov = np.cov(data)

## Correlation
def calculateCorrelation(data):
    temp=np.cov(data)
    x=[char for char in temp[0]]
    y=[char for char in temp[1]]
    return (x[1]/(np.sqrt(x[0])*np.sqrt(y[1])))


import numpy as np

def rotation_matrix(angle):
    """Create an array corresponding to the 2D transformation matrix
    of a rotation over `angle`.
    """
    return np.array([[np.cos(angle), -np.sin(angle)],
                     [np.sin(angle), np.cos(angle)]])

def scaling_matrix(kappa, dims=2):
    """Create a numpy array containing the
    `dims`-dimensional scaling matrix scaling the first dimension by kappa.
    """
    return np.diag([kappa]+[1]*(dims-1))

def apply_transformation_matrix(vecs, matrix):
    """Apply transformation matrix `matrix` to a list of vectors `vecs`.
    `vecs` can either be a list of vectors, or a NxM array, where N is
    the number of M-dimensional vectors.
    """
    avecs = np.asanyarray(vecs)
    return avecs @ matrix.T

def rotate(vecs, angle):
    "Rotate 2D vectors `vecs` by `angle` around the origin."
    return apply_transformation_matrix(vecs, rotation_matrix(angle))

def wrapToPi(x):
    """Wrap all values of x to the interval -pi,pi"""
    r = (x + np.pi) % (2*np.pi) - np.pi
    return r


import numpy as np
def prime_sieve(top_lim,limit=500000):
    """ Use the sieve of Erathosthenes to generate an array of primes
    arguments:
    top_lim - the highest number you'd like to generate up to
    limit - the highest number of iterations you're willing to allow (Defaut 500000)"""
    arr = np.concatenate((np.array([2]),np.arange(3,top_lim,2)))
    change=True
    min_num=2
    len_arr = 1
    # will loop up until the sqrt of your top limit
    limit_check = top_lim**0.5
    while change==True:
        # find the lowest number that you haven't already divided by
        min_num = np.min(arr[arr>min_num])
        # sieve - remove all numbers divisible or less than your min
        arr=arr[(arr%min_num!=0)|(arr<=min_num)]
        if min_num>=limit_check:
            change=False
        limit_check+=1
        if limit_check>limit:
            change=False
    # convert to np.float64
    arr=np.array(arr, dtype=np.float64)
    return arr


from sympy import ( symbols, solve, diff, integrate, exp, sqrt, lambdify, Integral, ln, pprint, oo )

# The lifetime of a printer costing $200 is exponentially distributed with mean 5 years.
# The manufacturer agrees to pay a full refund to a buyer if the printer fails during the first year following its purchase, and a one-half refund if it fails during the second year.
# If the manufacturer sells 100 printers, how much should it expect to pay in refunds?

cost = 100

mu = 3
a = 1 / mu
x = symbols( 'x' )

pdf = a * exp( -x / mu )

year_1 = integrate( pdf, ( x, 0, 1 ) ).evalf()
year_2 = integrate( pdf, ( x, 1, 2 ) ).evalf()

refund = year_1 * cost + year_2 * ( .5 * cost )
refund = refund * 100

round( refund, 2 )