# -*- coding: utf-8 -*- ''' Copyright (c) 2015 by Tobias Houska This file is part of Statistical Parameter Estimation Tool (SPOTPY). :author: Tobias Houska Holds functions to analyse results out of the database. Note: This part of SPOTPY is in alpha status and not yet ready for production use. ''' import numpy as np import spotpy font = {'family' : 'calibri', 'weight' : 'normal', 'size' : 18} def load_csv_results(filename, usecols=None): """ Get an array of your results in the given file. :filename: Expects an available filename, without the csv, in your working directory :type: str :return: Result array :rtype: array """ if usecols == None: return np.genfromtxt(filename+'.csv',delimiter=',',names=True,invalid_raise=False) else: return np.genfromtxt(filename+'.csv',delimiter=',',names=True,skip_footer=1,invalid_raise=False,usecols=usecols)[1:] def load_hdf5_results(filename): """ Get an array of your results in the given file. :filename: Expects an available filename, without the .h5 ending, in your working directory :type: str :return: Result array, simulation is an ndarray, which is different to structured arrays return by the csv/sql/ram databases :rtype: array """ import h5py with h5py.File(filename+'.h5', 'r') as f: return f[filename][()] def load_csv_parameter_results(filename, usecols=None): """ Get an array of your results in the given file, without the first and the last column. The first line may have a different objectivefunction and the last line may be incomplete, which would result in an error. :filename: Expects an available filename, without the csv, in your working directory :type: str :return: Result array :rtype: array """ ofile=open(filename+'.csv') line = ofile.readline() header=line.split(',') ofile.close() words=[] index =[] for i,word in enumerate(header): if word.startswith('par'): words.append(word) index.append(i) return np.genfromtxt(filename+'.csv', delimiter=',', names=words, usecols=index, invalid_raise=False, skip_header=1) def get_header(results): return results.dtype.names def get_like_fields(results): header = get_header(results) fields=[word for word in header if word.startswith('like')] return fields def get_parameter_fields(results): header = get_header(results) fields=[word for word in header if word.startswith('par')] return fields def get_simulation_fields(results): header = get_header(results) fields=[word for word in header if word.startswith('sim')] return fields def get_modelruns(results): """ Get an shorter array out of your result array, containing just the simulations of your model. :results: Expects an numpy array which should have indices beginning with "sim" :type: array :return: Array containing just the columns beginnning with the indice "sim" :rtype: array """ fields=[word for word in results.dtype.names if word.startswith('sim')] return results[fields] def get_parameters(results): """ Get an shorter array out of your result array, containing just the parameters of your model. :results: Expects an numpy array which should have indices beginning with "par" :type: array :return: Array containing just the columns beginnning with the indice "par" :rtype: array """ fields=[word for word in results.dtype.names if word.startswith('par')] results = results[fields] return results def get_parameternames(results): """ Get list of strings with the names of the parameters of your model. :results: Expects an numpy array which should have indices beginning with "par" :type: array :return: Strings with the names of the analysed parameters :rtype: list """ fields=[word for word in results.dtype.names if word.startswith('par')] parnames=[] for field in fields: parnames.append(field[3:]) return parnames def get_maxlikeindex(results,verbose=True): """ Get the maximum objectivefunction of your result array :results: Expects an numpy array which should of an index "like" for objectivefunctions :type: array :return: Index of the position in the results array with the maximum objectivefunction value and value of the maximum objectivefunction of your result array :rtype: int and float """ try: likes=results['like'] except ValueError: likes=results['like1'] maximum=np.nanmax(likes) value=str(round(maximum,4)) text=str('Run number ' ) index=np.where(likes==maximum) text2=str(' has the highest objectivefunction with: ') textv=text+str(index[0][0])+text2+value if verbose: print(textv) return index, maximum def get_minlikeindex(results): """ Get the minimum objectivefunction of your result array :results: Expects an numpy array which should of an index "like" for objectivefunctions :type: array :return: Index of the position in the results array with the minimum objectivefunction value and value of the minimum objectivefunction of your result array :rtype: int and float """ try: likes=results['like'] except ValueError: likes=results['like1'] minimum=np.nanmin(likes) value=str(round(minimum,4)) text=str('Run number ' ) index=np.where(likes==minimum) text2=str(' has the lowest objectivefunction with: ') textv=text+str(index[0][0])+text2+value print(textv) return index, minimum def get_percentiles(results,sim_number=''): """ Get 5,25,50,75 and 95 percentiles of your simulations :results: Expects an numpy array which should of an index "simulation" for simulations :type: array :sim_number: Optional, Number of your simulation, needed when working with multiple lists of simulations :type: int :return: Percentiles of simulations :rtype: int and float """ p5,p25,p50,p75,p95=[],[],[],[],[] fields=[word for word in results.dtype.names if word.startswith('simulation'+str(sim_number))] for i in range(len(fields)): p5.append(np.percentile(list(results[fields[i]]),5)) p25.append(np.percentile(list(results[fields[i]]),25)) p50.append(np.percentile(list(results[fields[i]]),50)) p75.append(np.percentile(list(results[fields[i]]),75)) p95.append(np.percentile(list(results[fields[i]]),95)) return p5,p25,p50,p75,p95 def calc_like(results,evaluation,objectivefunction): """ Calculate another objectivefunction of your results :results: Expects an numpy array which should of an index "simulation" for simulations :type: array :evaluation: Expects values, which correspond to your simulations :type: list :objectivefunction: Takes evaluation and simulation data and returns a objectivefunction, e.g. spotpy.objectvefunction.rmse :type: function :return: New objectivefunction list :rtype: list """ likes=[] sim=get_modelruns(results) for s in sim: likes.append(objectivefunction(evaluation,list(s))) return likes def compare_different_objectivefunctions(like1,like2): """ Performs the Welch’s t-test (aka unequal variances t-test) :like1: objectivefunction values :type: list :like2: Other objectivefunction values :type: list :return: p Value :rtype: list """ from scipy import stats out = stats.ttest_ind(like1, like2, equal_var=False) print(out) if out[1]>0.05: print('like1 is NOT signifikant different to like2: p>0.05') else: print('like1 is signifikant different to like2: p<0.05' ) return out def get_posterior(results,percentage=10, maximize=True): """ Get the best XX% of your result array (e.g. best 10% model runs would be a threshold setting of 0.9) :results: Expects an numpy array which should have as first axis an index "like1". This will be sorted . :type: array :percentag: Optional, ratio of values that will be deleted. :type: float :maximize: If True (default), higher "like1" column values are assumed to be better. If False, lower "like1" column values are assumed to be better. :return: Posterior result array :rtype: array """ if maximize: index = np.where(results['like1']>=np.percentile(results['like1'],100.0-percentage)) else: index = np.where(results['like1']>=np.percentile(results['like1'],100.0-percentage)) return results[index] def plot_parameter_uncertainty(posterior_results,evaluation, fig_name='Posterior_parameter_uncertainty.png'): import pylab as plt simulation_fields = get_simulation_fields(posterior_results) fig= plt.figure(figsize=(16,9)) for i in range(len(evaluation)): if evaluation[i] == -9999: evaluation[i] = np.nan ax = plt.subplot(1,1,1) q5,q95=[],[] for field in simulation_fields: q5.append(np.percentile(list(posterior_results[field]),2.5)) q95.append(np.percentile(list(posterior_results[field]),97.5)) ax.plot(q5,color='dimgrey',linestyle='solid') ax.plot(q95,color='dimgrey',linestyle='solid') ax.fill_between(np.arange(0,len(q5),1),list(q5),list(q95),facecolor='dimgrey',zorder=0, linewidth=0,label='parameter uncertainty') ax.plot(evaluation,'r.',markersize=1, label='Observation data') bestindex,bestobjf = get_maxlikeindex(posterior_results,verbose=False) plt.plot(list(posterior_results[simulation_fields][bestindex][0]),'b-',label='Obj='+str(round(bestobjf,2))) plt.xlabel('Number of Observation Points') plt.ylabel ('Simulated value') plt.legend(loc='upper right') fig.savefig(fig_name,dpi=300) text='A plot of the parameter uncertainty has been saved as '+fig_name print(text) def sort_like(results): return np.sort(results,axis=0) def get_best_parameterset(results,maximize=True): """ Get the best parameter set of your result array, depending on your first objectivefunction :results: Expects an numpy array which should have as first axis an index "like" or "like1". :type: array :maximize: Optional, default=True meaning the highest objectivefunction is taken as best, if False the lowest objectivefunction is taken as best. :type: boolean :return: Best parameter set :rtype: array """ try: likes=results['like'] except ValueError: likes=results['like1'] if maximize: best=np.nanmax(likes) else: best=np.nanmin(likes) index=np.where(likes==best) best_parameter_set = get_parameters(results[index])[0] parameter_names = get_parameternames(results) text='' for i in range(len(parameter_names)): text+=parameter_names[i]+'='+str(best_parameter_set[i])+', ' print('Best parameter set:\n'+text[:-2]) return get_parameters(results[index]) def get_min_max(spotpy_setup): """ Get the minimum and maximum values of your parameters function of the spotpy setup :spotpy_setup: Class with a parameters function :type: class :return: Possible minimal and maximal values of all parameters in the parameters function of the spotpy_setup class :rtype: Two arrays """ parameter_obj = spotpy.parameter.generate(spotpy.parameter.get_parameters_from_setup(spotpy_setup)) randompar = parameter_obj['random'] for i in range(1000): randompar = np.column_stack((randompar, parameter_obj['random'])) return np.amin(randompar, axis=1), np.amax(randompar, axis=1) def get_parbounds(spotpy_setup): """ Get the minimum and maximum parameter bounds of your parameters function of the spotpy setup :spotpy_setup: Class with a parameters function :type: class :return: Possible minimal and maximal values of all parameters in the parameters function of the spotpy_setup class :rtype: list """ parmin,parmax=get_min_max(spotpy_setup) bounds=[] for i in range(len(parmin)): bounds.append([parmin[i],parmax[i]]) return bounds def get_sensitivity_of_fast(results,like_index=1,M=4, print_to_console=True): """ Get the sensitivity for every parameter of your result array, created with the FAST algorithm :results: Expects an numpy array which should have as first axis an index "like" or "like1". :type: array :like_index: Optional, index of objectivefunction to base the sensitivity on, default=None first objectivefunction is taken :type: int :return: Sensitivity indices for every parameter :rtype: list """ import math likes=results['like'+str(like_index)] print('Number of model runs:', likes.size) parnames = get_parameternames(results) parnumber=len(parnames) print('Number of parameters:', parnumber) rest = likes.size % (parnumber) if rest != 0: print("""" Number of samples in model output file must be a multiple of D, where D is the number of parameters in your parameter file. We handle this by ignoring the last """, rest, """runs.""") likes = likes[:-rest ] N = int(likes.size / parnumber) # Recreate the vector omega used in the sampling omega = np.zeros([parnumber]) omega[0] = math.floor((N - 1) / (2 * M)) m = math.floor(omega[0] / (2 * M)) print('m =', m) if m >= (parnumber - 1): omega[1:] = np.floor(np.linspace(1, m, parnumber - 1)) else: omega[1:] = np.arange(parnumber - 1) % m + 1 print('Omega =', omega) # Calculate and Output the First and Total Order Values if print_to_console: print("Parameter First Total") Si = dict((k, [None] * parnumber) for k in ['S1', 'ST']) print(Si) for i in range(parnumber): l = np.arange(i * N, (i + 1) * N) print(l) Si['S1'][i] = _compute_first_order(likes[l], N, M, omega[0]) Si['ST'][i] = _compute_total_order(likes[l], N, omega[0]) print(Si) if print_to_console: print("%s %f %f" % (parnames[i], Si['S1'][i], Si['ST'][i])) return Si def plot_fast_sensitivity(results,like_index=1,number_of_sensitiv_pars=10,fig_name='FAST_sensitivity.png'): """ Example, how to plot the sensitivity for every parameter of your result array, created with the FAST algorithm :results: Expects an numpy array which should have an header defined with the keyword like. :type: array :like: Default 'like1', Collum of which the sensitivity indices will be estimated on :type: list :number_of_sensitiv_pars: Optional, this number of most sensitive parameters will be shown in the legend :type: int :return: Parameter names which are sensitive, Sensitivity indices for every parameter, Parameter names which are not sensitive :rtype: Three lists """ import matplotlib.pyplot as plt parnames=get_parameternames(results) fig=plt.figure(figsize=(16,6)) ax = plt.subplot(1,1,1) Si = get_sensitivity_of_fast(results, like_index=like_index) names = [] values = [] no_names = [] no_values = [] index=[] no_index=[] try: threshold = np.sort(list(Si.values())[1])[-number_of_sensitiv_pars] except IndexError: threshold = 0 first_sens_call=True first_insens_call=True try: Si.values() except AttributeError: exit("Our SI is wrong: " +str(Si)) for j in range(len(list(Si.values())[1])): if list(Si.values())[1][j]>=threshold: names.append(j) values.append(list(Si.values())[1][j]) index.append(j) if first_sens_call: ax.bar(j, list(Si.values())[1][j], color='blue', label='Sensitive Parameters') else: ax.bar(j, list(Si.values())[1][j], color='blue') first_sens_call=False else: #names.append('') no_values.append(list(Si.values())[1][j]) no_index.append(j) if first_insens_call: ax.bar(j,list(Si.values())[1][j],color='orange', label = 'Insensitive parameter') else: ax.bar(j,list(Si.values())[1][j],color='orange') first_insens_call=False ax.set_ylim([0,1]) ax.set_xlabel('Model Paramters') ax.set_ylabel('Total Sensititivity Index') ax.legend() ax.set_xticks(np.arange(0,len(parnames))) xtickNames = ax.set_xticklabels(parnames, color='grey') plt.setp(xtickNames, rotation=90) for name_id in names: ax.get_xticklabels()[name_id].set_color("black") #ax.set_xticklabels(['0']+parnames) ax.plot(np.arange(-1,len(parnames)+1,1),[threshold]*(len(parnames)+2),'r--') ax.set_xlim(-0.5,len(parnames)-0.5) plt.tight_layout() fig.savefig(fig_name,dpi=300) def plot_heatmap_griewank(results,algorithms, fig_name='heatmap_griewank.png'): """Example Plot as seen in the SPOTPY Documentation""" import matplotlib.pyplot as plt from matplotlib import ticker from matplotlib import cm font = {'family' : 'calibri', 'weight' : 'normal', 'size' : 20} plt.rc('font', **font) subplots=len(results) xticks=[-40,0,40] yticks=[-40,0,40] fig=plt.figure(figsize=(16,6)) N = 2000 x = np.linspace(-50.0, 50.0, N) y = np.linspace(-50.0, 50.0, N) x, y = np.meshgrid(x, y) z=1+ (x**2+y**2)/4000 - np.cos(x/np.sqrt(2))*np.cos(y/np.sqrt(3)) cmap = plt.get_cmap('autumn') rows=2.0 for i in range(subplots): amount_row = int(np.ceil(subplots/rows)) ax = plt.subplot(rows, amount_row, i+1) CS = ax.contourf(x, y, z,locator=ticker.LogLocator(),cmap=cm.rainbow) ax.plot(results[i]['par0'],results[i]['par1'],'ko',alpha=0.2,markersize=1.9) ax.xaxis.set_ticks([]) if i==0: ax.set_ylabel('y') if i==subplots/rows: ax.set_ylabel('y') if i>=subplots/rows: ax.set_xlabel('x') ax.xaxis.set_ticks(xticks) if i!=0 and i!=subplots/rows: ax.yaxis.set_ticks([]) ax.set_title(algorithms[i]) fig.savefig(fig_name, bbox_inches='tight') def plot_objectivefunction(results,evaluation,limit=None,sort=True, fig_name = 'objective_function.png'): """Example Plot as seen in the SPOTPY Documentation""" import matplotlib.pyplot as plt likes=calc_like(results,evaluation,spotpy.objectivefunctions.rmse) data=likes #Calc confidence Interval mean = np.average(data) # evaluate sample variance by setting delta degrees of freedom (ddof) to # 1. The degree used in calculations is N - ddof stddev = np.std(data, ddof=1) from scipy.stats import t # Get the endpoints of the range that contains 95% of the distribution t_bounds = t.interval(0.999, len(data) - 1) # sum mean to the confidence interval ci = [mean + critval * stddev / np.sqrt(len(data)) for critval in t_bounds] value="Mean: %f" % mean print(value) value="Confidence Interval 95%%: %f, %f" % (ci[0], ci[1]) print(value) threshold=ci[1] happend=None bestlike=[data[0]] for like in data: if like<bestlike[-1]: bestlike.append(like) if bestlike[-1]<threshold and not happend: thresholdpos=len(bestlike) happend=True else: bestlike.append(bestlike[-1]) if limit: plt.plot(bestlike,'k-')#[0:limit]) plt.axvline(x=thresholdpos,color='r') plt.plot(likes,'b-') #plt.ylim(ymin=-1,ymax=1.39) else: plt.plot(bestlike) plt.savefig(fig_name) def plot_parametertrace_algorithms(result_lists, algorithmnames, spot_setup, fig_name='parametertrace_algorithms.png'): """Example Plot as seen in the SPOTPY Documentation""" import matplotlib.pyplot as plt font = {'family' : 'calibri', 'weight' : 'normal', 'size' : 20} plt.rc('font', **font) fig=plt.figure(figsize=(17,5)) subplots=len(result_lists) parameter = spotpy.parameter.get_parameters_array(spot_setup) rows=len(parameter['name']) for j in range(rows): for i in range(subplots): ax = plt.subplot(rows,subplots,i+1+j*subplots) data=result_lists[i]['par'+parameter['name'][j]] ax.plot(data,'b-') if i==0: ax.set_ylabel(parameter['name'][j]) rep = len(data) if i>0: ax.yaxis.set_ticks([]) if j==rows-1: ax.set_xlabel(algorithmnames[i-subplots]) else: ax.xaxis.set_ticks([]) ax.plot([1]*rep,'r--') ax.set_xlim(0,rep) ax.set_ylim(parameter['minbound'][j],parameter['maxbound'][j]) #plt.tight_layout() fig.savefig(fig_name, bbox_inches='tight') def plot_parametertrace(results,parameternames=None,fig_name='Parameter_trace.png'): """ Get a plot with all values of a given parameter in your result array. The plot will be saved as a .png file. :results: Expects an numpy array which should of an index "like" for objectivefunctions :type: array :parameternames: A List of Strings with parameternames. A line object will be drawn for each String in the List. :type: list :return: Plot of all traces of the given parameternames. :rtype: figure """ import matplotlib.pyplot as plt fig=plt.figure(figsize=(16,9)) if not parameternames: parameternames=get_parameternames(results) names='' i=1 for name in parameternames: ax = plt.subplot(len(parameternames),1,i) ax.plot(results['par'+name],label=name) names+=name+'_' ax.set_ylabel(name) if i==len(parameternames): ax.set_xlabel('Repetitions') if i==1: ax.set_title('Parametertrace') ax.legend() i+=1 fig.savefig(fig_name) text='The figure as been saved as "'+fig_name print(text) def plot_posterior_parametertrace(results,parameternames=None,threshold=0.1, fig_name='Posterior_parametertrace.png'): """ Get a plot with all values of a given parameter in your result array. The plot will be saved as a .png file. :results: Expects an numpy array which should of an index "like" for objectivefunctions :type: array :parameternames: A List of Strings with parameternames. A line object will be drawn for each String in the List. :type: list :return: Plot of all traces of the given parameternames. :rtype: figure """ import matplotlib.pyplot as plt fig=plt.figure(figsize=(16,9)) results=sort_like(results) if not parameternames: parameternames=get_parameternames(results) names='' i=1 for name in parameternames: ax = plt.subplot(len(parameternames),1,i) ax.plot(results['par'+name][int(len(results)*threshold):],label=name) names+=name+'_' ax.set_ylabel(name) if i==len(parameternames): ax.set_xlabel('Repetitions') if i==1: ax.set_title('Parametertrace') ax.legend() i+=1 fig.savefig(fig_name) text='The figure as been saved as '+fig_name print(text) def plot_posterior(results,evaluation,dates=None,ylabel='Posterior model simulation',xlabel='Time',bestperc=0.1, fig_name='bestmodelrun.png'): """ Get a plot with the maximum objectivefunction of your simulations in your result array. The plot will be saved as a .png file. Args: results (array): Expects an numpy array which should of an index "like" for objectivefunctions and "sim" for simulations. evaluation (list): Should contain the values of your observations. Expects that this list has the same lenght as the number of simulations in your result array. Kwargs: dates (list): A list of datetime values, equivalent to the evaluation data. ylabel (str): Labels the y-axis with the given string. xlabel (str): Labels the x-axis with the given string. objectivefunction (str): Name of the objectivefunction function used for the simulations. objectivefunctionmax (boolean): If True the maximum value of the objectivefunction will be searched. If false, the minimum will be searched. calculatelike (boolean): If True, the NSE will be calulated for each simulation in the result array. Returns: figure. Plot of the simulation with the maximum objectivefunction value in the result array as a blue line and dots for the evaluation data. """ import matplotlib.pyplot as plt index,maximum=get_maxlikeindex(results) sim=get_modelruns(results) bestmodelrun=list(sim[index][0])#Transform values into list to ensure plotting bestparameterset=list(get_parameters(results)[index][0]) parameternames=list(get_parameternames(results) ) bestparameterstring='' maxNSE=spotpy.objectivefunctions.nashsutcliffe(bestmodelrun,evaluation) for i in range(len(parameternames)): if i%8==0: bestparameterstring+='\n' bestparameterstring+=parameternames[i]+'='+str(round(bestparameterset[i],4))+',' fig=plt.figure(figsize=(16,8)) plt.plot(bestmodelrun,'b-',label='Simulation='+str(round(maxNSE,4))) plt.plot(evaluation,'ro',label='Evaluation') plt.legend() plt.ylabel(ylabel) plt.xlabel(xlabel) plt.title('Maximum objectivefunction of Simulations with '+bestparameterstring[0:-2]) fig.savefig(fig_name) text='The figure as been saved as '+fig_name print(text) def plot_bestmodelrun(results,evaluation,fig_name ='Best_model_run.png'): """ Get a plot with the maximum objectivefunction of your simulations in your result array. The plot will be saved as a .png file. :results: Expects an numpy array which should of an index "like" for objectivefunctions and "sim" for simulations. type: Array :evaluation: Should contain the values of your observations. Expects that this list has the same lenght as the number of simulations in your result array. :type: list Returns: figure. Plot of the simulation with the maximum objectivefunction value in the result array as a blue line and dots for the evaluation data. """ import pylab as plt fig= plt.figure(figsize=(16,9)) for i in range(len(evaluation)): if evaluation[i] == -9999: evaluation[i] = np.nan plt.plot(evaluation,'ro',markersize=1, label='Observation data') simulation_fields = get_simulation_fields(results) bestindex,bestobjf = get_maxlikeindex(results,verbose=False) plt.plot(list(results[simulation_fields][bestindex][0]),'b-',label='Obj='+str(round(bestobjf,2))) plt.xlabel('Number of Observation Points') plt.ylabel ('Simulated value') plt.legend(loc='upper right') fig.savefig(fig_name,dpi=300) text='A plot of the best model run has been saved as '+fig_name print(text) def plot_bestmodelruns(results,evaluation,algorithms=None,dates=None,ylabel='Best model simulation',xlabel='Date',objectivefunctionmax=True,calculatelike=True,fig_name='bestmodelrun.png'): """ Get a plot with the maximum objectivefunction of your simulations in your result array. The plot will be saved as a .png file. Args: results (list of arrays): Expects list of numpy arrays which should of an index "like" for objectivefunctions and "sim" for simulations. evaluation (list): Should contain the values of your observations. Expects that this list has the same lenght as the number of simulations in your result array. Kwargs: dates (list): A list of datetime values, equivalent to the evaluation data. ylabel (str): Labels the y-axis with the given string. xlabel (str): Labels the x-axis with the given string. objectivefunction (str): Name of the objectivefunction function used for the simulations. objectivefunctionmax (boolean): If True the maximum value of the objectivefunction will be searched. If false, the minimum will be searched. calculatelike (boolean): If True, the NSE will be calulated for each simulation in the result array. Returns: figure. Plot of the simulation with the maximum objectivefunction value in the result array as a blue line and dots for the evaluation data. A really great idea. A way you might use me is >>> bcf.analyser.plot_bestmodelrun(results,evaluation, ylabel='Best model simulation') """ import matplotlib.pyplot as plt plt.rc('font', **font) fig=plt.figure(figsize=(17,8)) colors=['grey', 'black', 'brown','red','orange', 'yellow','green','blue',] plt.plot(dates,evaluation,'ro',label='Evaluation data') for i in range(len(results)): if calculatelike: likes=[] sim=get_modelruns(results[i]) par=get_parameters(results[i]) for s in sim: likes.append(spotpy.objectivefunctions.lognashsutcliffe(evaluation,list(s))) maximum=max(likes) index=likes.index(maximum) bestmodelrun=list(sim[index]) bestparameterset=list(par[index]) print(bestparameterset) else: if objectivefunctionmax==True: index,maximum=get_maxlikeindex(results[i]) else: index,maximum=get_minlikeindex(results[i]) bestmodelrun=list(get_modelruns(results[i])[index][0])#Transform values into list to ensure plotting maxLike=spotpy.objectivefunctions.lognashsutcliffe(evaluation,bestmodelrun) if dates is not None: plt.plot(dates,bestmodelrun,'-',color=colors[i],label=algorithms[i]+': LogNSE='+str(round(maxLike,4))) else: plt.plot(bestmodelrun,'-',color=colors[i],label=algorithms[i]+': AI='+str(round(maxLike,4))) #plt.plot(evaluation,'ro',label='Evaluation data') plt.legend(bbox_to_anchor=(.0, 0), loc=3) plt.ylabel(ylabel) plt.xlabel(xlabel) plt.ylim(15,50) #DELETE WHEN NOT USED WITH SOIL MOISTUR RESULTS fig.savefig(fig_name) text='The figure as been saved as '+fig_name print(text) def plot_objectivefunctiontraces(results,evaluation,algorithms,fig_name='Like_trace.png'): import matplotlib.pyplot as plt from matplotlib import colors cnames=list(colors.cnames) font = {'family' : 'calibri', 'weight' : 'normal', 'size' : 20} plt.rc('font', **font) fig=plt.figure(figsize=(16,3)) xticks=[5000,15000] for i in range(len(results)): ax = plt.subplot(1,len(results),i+1) likes=calc_like(results[i],evaluation,spotpy.objectivefunctions.rmse) ax.plot(likes,'b-') ax.set_ylim(0,25) ax.set_xlim(0,len(results[0])) ax.set_xlabel(algorithms[i]) ax.xaxis.set_ticks(xticks) if i==0: ax.set_ylabel('RMSE') ax.yaxis.set_ticks([0,10,20]) else: ax.yaxis.set_ticks([]) plt.tight_layout() fig.savefig(fig_name) def plot_regression(results,evaluation,fig_name='regressionanalysis.png'): import matplotlib.pyplot as plt fig=plt.figure(figsize=(16,9)) simulations=get_modelruns(results) for sim in simulations: plt.plot(evaluation,list(sim),'bo',alpha=.05) plt.ylabel('simulation') plt.xlabel('evaluation') plt.title('Regression between simulations and evaluation data') fig.savefig(fig_name) text='The figure as been saved as '+fig_name print(text) def plot_parameterInteraction(results, fig_name ='ParameterInteraction.png'): '''Input: List with values of parameters and list of strings with parameter names Output: Dotty plot of parameter distribution and gaussian kde distribution''' import matplotlib.pyplot as plt import pandas as pd parameterdistribtion=get_parameters(results) parameternames=get_parameternames(results) df = pd.DataFrame(np.asarray(parameterdistribtion).T.tolist(), columns=parameternames) pd.plotting.scatter_matrix(df, alpha=0.2, figsize=(12, 12), diagonal='kde') plt.savefig(fig_name,dpi=300) def plot_allmodelruns(modelruns,observations,dates=None, fig_name='bestmodel.png'): '''Input: Array of modelruns and list of Observations Output: Plot with all modelruns as a line and dots with the Observations ''' import matplotlib.pyplot as plt fig=plt.figure(figsize=(16,9)) ax = plt.subplot(1,1,1) if dates is not None: for i in range(len(modelruns)): if i==0: ax.plot(dates, modelruns[i],'b',alpha=.05,label='Simulations') else: ax.plot(dates, modelruns[i],'b',alpha=.05) else: for i in range(len(modelruns)): if i==0: ax.plot(modelruns[i],'b',alpha=.05,label='Simulations') else: ax.plot(modelruns[i],'b',alpha=.05) ax.plot(observations,'ro',label='Evaluation') ax.legend() ax.set_xlabel = 'Best model simulation' ax.set_ylabel = 'Evaluation points' ax.set_title = 'Maximum objectivefunction of Simulations' fig.savefig(fig_name) text='The figure as been saved as '+fig_name print(text) def plot_autocorellation(parameterdistribution,parametername,fig_name='Autocorrelation.png'): '''Input: List of sampled values for one Parameter Output: Parameter Trace, Histogramm and Autocorrelation Plot''' import matplotlib.pyplot as plt import pandas as pd pd.plotting.autocorrelation_plot(parameterdistribution) plt.savefig(fig_name,dpi=300) def plot_gelman_rubin(r_hat_values,fig_name='gelman_rub.png'): '''Input: List of R_hat values of chains (see Gelman & Rubin 1992) Output: Plot as seen for e.g. in (Sadegh and Vrugt 2014)''' import matplotlib.pyplot as plt fig=plt.figure(figsize=(16,9)) ax = plt.subplot(1,1,1) ax.plot(r_hat_values) ax.plot([1.2]*len(r_hat_values),'k--') ax.set_xlabel='r_hat' plt.savefig(fig_name,dpi=300) def gelman_rubin(x): '''NOT USED YET''' if np.shape(x) < (2,): raise ValueError( 'Gelman-Rubin diagnostic requires multiple chains of the same length.') try: m, n = np.shape(x) except ValueError: return [gelman_rubin(np.transpose(y)) for y in np.transpose(x)] # Calculate between-chain variance B_over_n = np.sum((np.mean(x, 1) - np.mean(x)) ** 2) / (m - 1) # Calculate within-chain variances W = np.sum( [(x[i] - xbar) ** 2 for i, xbar in enumerate(np.mean(x, 1))]) / (m * (n - 1)) # (over) estimate of variance s2 = W * (n - 1) / n + B_over_n # Pooled posterior variance estimate V = s2 + B_over_n / m # Calculate PSRF R = V / W return R def plot_Geweke(parameterdistribution,parametername): '''Input: Takes a list of sampled values for a parameter and his name as a string Output: Plot as seen for e.g. in BUGS or PyMC''' import matplotlib.pyplot as plt # perform the Geweke test Geweke_values = _Geweke(parameterdistribution) # plot the results fig = plt.figure() plt.plot(Geweke_values,label=parametername) plt.legend() plt.title(parametername + '- Geweke_Test') plt.xlabel('Subinterval') plt.ylabel('Geweke Test') plt.ylim([-3,3]) # plot the delimiting line plt.plot( [2]*len(Geweke_values), 'r-.') plt.plot( [-2]*len(Geweke_values), 'r-.') def _compute_first_order(outputs, N, M, omega): f = np.fft.fft(outputs) Sp = np.power(np.absolute(f[np.arange(1, int((N + 1) / 2))]) / N, 2) V = 2 * np.sum(Sp) D1 = 2 * np.sum(Sp[np.arange(1, M + 1) * int(omega) - 1]) return D1 / V def _compute_total_order(outputs, N, omega): f = np.fft.fft(outputs) Sp = np.power(np.absolute(f[np.arange(1, int((N + 1) / 2))]) / N, 2) V = 2 * np.sum(Sp) Dt = 2 * sum(Sp[np.arange(int(omega / 2))]) return (1 - Dt / V) def _Geweke(samples, intervals=20): '''Calculates Geweke Z-Scores''' length=int(len(samples)/intervals/2) # discard the first 10 per cent first = 0.1*len(samples) # create empty array to store the results z = np.empty(intervals) for k in np.arange(0, intervals): # starting points of the two different subsamples start1 = int(first + k*length) start2 = int(len(samples)/2 + k*length) # extract the sub samples subsamples1 = samples[start1:start1+length] subsamples2 = samples[start2:start2+length] # calculate the mean and the variance mean1 = np.mean(subsamples1) mean2 = np.mean(subsamples2) var1 = np.var(subsamples1) var2 = np.var(subsamples2) # calculate the Geweke test z[k] = (mean1-mean2)/np.sqrt(var1+var2) return z