jmquintana79
8/4/2017 - 7:59 AM

plot quantile forecast

Customized plots for probabilistic forecast. In particular, this functions were used for wind forecast.

def plot_quantile_forecast(DF,shead,is_save=False,path_output=None):
    
    """
    DF: columns --> [ dt, real, q5, q10, ..., q95 ]
    """
    
    import numpy as np
    import pandas as pd
    from datetime import datetime, timedelta
    import matplotlib.pyplot as plt


    # create objects
    fig, ax = plt.subplots(figsize=(12,6))

    # calculate if any number is the multiple of another number
    def multiple(value, multiple):
        rest = value % multiple
        if rest == 0:
            return True
        else:
            return False

    # collect data
    mask = np.array([multiple(i.hour, 6) for i in DF.dt.tolist()])
    lsx = [ idt.strftime("%Y-%m-%d %Hh") if imask else '' for imask,idt in zip(mask,DF.dt.tolist())]
    ix = list(range(0,len(lsx),1))
    yreal = DF.real.tolist()
    YPRED = DF[[ic for ic in DF.columns.values if 'q' in ic]]

    # get list of range
    lranges = ["q5-q95","q10-q90","q15-q85","q20-q80","q25-q75","q30-q70","q35-q65","q40-q60","q45-q55"]

    # set color list (blues)
    lblues = np.linspace(0.1,1.0,len(lranges))

    # loop of ranges
    for ir,ib in list(zip(lranges,lblues))[:]:
        # get name of range limits
        ipi,ipf = ir.split("-")
        # get required percentiles in each range
        ypqi = np.array([float(iv) for iv in YPRED[ipi].values])
        ypqf = np.array([float(iv) for iv in YPRED[ipf].values])
        # plot range of precentiles
        #ax.plot(x_final,ypi,color="grey",linestyle="dashed")
        #ax.plot(x_final,ypf,color="grey",linestyle="dashed")
        # fill ranges
        ax.fill_between(ix,ypqi,ypqf,color=(ib,ib,1.))

    # collect extreme percentiles forecast
    ypq5 = np.array([float(iv) for iv in YPRED['q5'].values])
    ypq95 = np.array([float(iv) for iv in YPRED['q95'].values])
    # plot extreme percentiles forecast
    ax.plot(ix,ypq5,color="blue")
    ax.plot(ix,ypq95,color="blue",label="Prob. Frcst")

    # plot real
    ax.plot(ix,yreal,color="black",linestyle="dashed" ,label="real")
    plt.scatter(ix,yreal,s=20,facecolor="none",edgecolors="black")

    # collect p50 forecast
    ypq50 = np.array([float(iv) for iv in YPRED['q50'].values])
    # plot p50 forecast
    ax.plot(ix,ypq50,color="red",label="q50 Frcst")

    # x limits
    ax.set_xlim([ix[0]-1,ix[-1]+1])
    # y limits
    ax.set_ylim([0,1])

    # xtick
    plt.xticks(ix,fontsize='small',rotation='vertical')
    ax.set_xticklabels(lsx)
    # hide xticks
    #plt.tick_params(axis='x',which='both',bottom='off',top='off',labelbottom='off')

    # axes labels
    plt.ylabel("wp normalized")

    # lengend
    ax.legend(loc='upper right',fancybox=True, shadow=True, ncol=1,fontsize=10)

    # title
    plt.title("%s: %s hour ahead"%(shead,((DF.dt.tolist()[-1]-DF.dt.tolist()[0]).days)*24))

    # adjust space of chart
    plt.subplots_adjust(bottom=0.2) 

    # display
    if is_save is False: plt.show()
    # save
    else:
        if path_output is None: print('WARNING: it was not specified the output path')
        else: plt.savefig(path_output,bbox_inches='tight',transparent=False)

    # close plot
    plt.close(fig)
    
    # return
    return None




## PLOT QUANTILE FORECAST USING BOXPLOTS
def plot_quantile_forecast2(DF,shead):
    import numpy as np
    import matplotlib.pyplot as plt
    from repository_scores import calculate_pinball_score,rmse,match_index
    from repository_functions import multiple
    fig, ax = plt.subplots(figsize=(30,10))

    ## data
    lcolq = [ic for ic in DF.columns.values if 'q' in ic]
    data = DF[lcolq].as_matrix()
    data = np.transpose(data)
    ldt = DF.datetime.tolist()
    x = list(range(len(ldt)))
    y = DF.real.tolist()
    predictions = DF['q50'].values
    # dates
    lmut = np.array([multiple(idt.hour, 6) for idt in ldt])
    lsdates = [idt.strftime("%Y-%m-%d %H") if imut else '' for imut,idt in zip(lmut,ldt)]
    # match
    lmatch = list(DF[['real','q5','q95']].apply(lambda x: True if x[0]>=float('%.1f'%x[1]) and x[0]<=float('%.1f'%x[2]) else False, axis=1))
    # scores
    pinball_score = np.mean(calculate_pinball_score(DF)['pinball'])
    rmse_score = rmse(predictions, y)
    vmatch_index = match_index(DF)

    # Plot box plot per each line point
    boxplot_dict = ax.boxplot(data, positions=x, notch=False)


    # Plot a line between the means of each dataset
    plt.plot(x, y,linewidth=5,color="black",linestyle="dashed" ,label='real')

    # format general labels in x axis
    plt.xticks(x,fontsize=20,rotation='vertical')
    ax.set_xticklabels(lsdates)
    plt.yticks(fontsize=20,rotation='horizontal')           

    # set limits
    #ax.set_ylim([0,1])

    # set legend
    ax.legend(loc='best',fontsize=22,shadow=True)

    # set color of box plots
    i=0
    for b in boxplot_dict['boxes']:
        if lmatch[i]: b.set_color('green')
        else:b.set_color('red')
        i += 1

    # title
    if shead!='': shead = '%s:'%shead
    plt.title('%s  Pinball = %.3f Match Index = %.3f%s RMSE = %.3f \n [ %s - %s ]'%(shead,
        pinball_score,
        vmatch_index,'%',
        rmse_score,
        ldt[0],ldt[-1]),
    fontsize=20)

    # Plot
    plt.show()

    # return
    return None