# File params2ecdf.R # Part of the hydroPSO R package, http://www.rforge.net/hydroPSO/ ; # http://cran.r-project.org/web/packages/hydroPSO # Copyright 2008-2011 Mauricio Zambrano-Bigiarini # Distributed under GPL 2 or later ################################################################################ # 'params2ecdf' # ################################################################################ # Purpose: This function computes (weighted) empirical CDFs # # (ECDFs) for each calibrated parameter, by using the parameter # # values obtained during the optimisation with PSO, with optional# # plot # ################################################################################ # # params : matrix with the behavioural parameter values, where each row represent a different parameter set, and each column represent the value # of a different model's parameter. # param.names : character vector, which meaningful names to be used for each model's parameter in \code{params} (by default column namaes) # weights : numeric vector, with the values of the weights to be used for computing the empirical CDFs. \cr # Omitting the \code{weights} argument or specifying \code{NULL} or a zero-length vector will result in the usual unweighted estimates. # byrow : logical, indicating if the computations have to be made for each column or for each row of \code{params}. \cr # When the parameter sets are stored in rows, i.e., values for different model's parameter are stored in columns, \code{byrow} must be \kbd{FALSE}. \cr # When the parameter sets are stored in columns, i.e., values for different model's parameter are stored in rows, \code{byrow} must be \kbd{TRUE}. # plot : logical, indicating if a plot with the Empirical CDFs for each model's parameter has to be produced or not. # nrows : Number of rows to be used in the plotting window. The number # of columns is automatically computed depending on the number # of columns of 'params' params2ecdf <- function(params, ...) UseMethod("params2ecdf") ################################################################################ # Author : Mauricio Zambrano-Bigiarini # # Started: 12-Oct-2011 # # Updates: 15-Feb-2012 ; 21-Feb-2012 ; 19-Nov-2012 # ################################################################################ params2ecdf.default <- function(params, param.names=NULL, gofs=NULL, MinMax=NULL, beh.thr=NA, weights=NULL, byrow=FALSE, plot=TRUE, obs=NULL, main=NULL, nrows="auto", ylab="Probability", col="blue", leg.cex=1.2, leg.pos="topleft", cex.axis=1.2, cex.main=1.2, cex.lab=1.2, verbose=TRUE, ..., #### PNG options ### do.png=FALSE, png.width=1500, png.height=900, png.res=90, png.fname="Params_ECDFs.png" ) { if (is.null(param.names)) param.names <- deparse(substitute(params)) # number of parameters nparam <- NCOL(params) # Number of parameter sets n <- NROW(params) # Checking 'param.names' if (length(param.names) != nparam) stop("Invalid argument: 'length(param.names) = ", length(param.names), " != ", nparam, " = nparam'") # Checking 'beh.thr' if ( !is.na(beh.thr) ) { if ( is.null(MinMax) ) stop("Missing argument: 'MinMax' has to be provided before using 'beh.thr' !!") if ( is.null(gofs) ) { stop("Missing argument: 'gofs' has to be provided before using 'beh.thr' !!") } else if (length(gofs) != n) stop("Invalid argument: 'length(gofs) != nrow(params)' (", length(gofs), "!=", n, ") !!" ) } # IF end # Checking 'MinMax' if ( !is.null(MinMax) ) { if ( !(MinMax %in% c("min", "max")) ) stop("Invalid argument: 'MinMax' must be in c('min', 'max')") } # IF end # checking that the user provided 1 weight for each behavioural parameter set if ( !is.null(weights) ) { if (length(weights) != n ) stop("Invalid argument: 'length(w) != nrow(params)' (", length(weights), "!=", n, ")" ) } # IF end # creating the final output, a list with the ECDFs ecdf <- vector("list", nparam) # Checking 'do.png' and 'plot' if (do.png==TRUE & plot==FALSE) stop("Invalid argument: 'plot=FALSE' & 'do.png=TRUE' is not possible !!") if (nparam==1) params <- matrix(params, ncol=1) # Filtering out those parameter sets above/below a certain threshold if (!is.na(beh.thr)) { # Checking 'beh.thr' mx <- max(gofs, na.rm=TRUE) if (beh.thr > mx) stop("Invalid argument: 'beh.thr' must be lower than ", mx ,"!!") # Computing the row index of the behavioural parameter sets ifelse(MinMax=="min", beh.row.index <- which(gofs <= beh.thr), beh.row.index <- which(gofs >= beh.thr) ) # Removing non-behavioural parameter sets & gofs params <- params[beh.row.index, ] gofs <- gofs[beh.row.index] # Amount of behavioural parameter sets nbeh <- nrow(params) if (verbose) message( "[ Number of behavioural parameter sets: ", nbeh, " ]" ) } # IF end ######################## Plotting Preliminars ######################## # If there are too many parameters to plot,more than 1 plot is produced nfigs <- ceiling(nparam/21) # Creating a backup of the original 'params' params.bak <- params nparam.bak <- nparam # plot/parameter number p <- 1 for (f in 1:nfigs) { # Setting the filename of the PNG file ifelse(f==1, fig.png.fname <- png.fname, fig.png.fname <- paste(substr(png.fname, 1,nchar(png.fname)-4), f, ".png", sep="") ) # PNG ? if (do.png) { png(filename=fig.png.fname, width=png.width, height=png.height, res=png.res) } else if (f >1) x11() # Subsetting if (nparam.bak <= 21) { L <- nparam.bak } else ifelse(f*21 <= nparam.bak, L <- f*21, L <- nparam.bak) params <- params.bak[ ,((f-1)*21+1):L] nparam <- NCOL(params) if (nparam==1) params <- matrix(params, ncol=1) # Computing the number of rows for the plot if (nrows == "auto") { if ( nparam <= 5 ) lnr <- 1 if ( (nparam > 5) & (nparam <= 14) ) lnr <- 2 if ( nparam > 14 ) lnr <- ceiling(nparam/7) } else lnr <- nrows # Saving default plotting parameters old.par <- par(no.readonly=TRUE) if (!do.png) on.exit(par(old.par)) # Defining the plotting window nr <- lnr nc <- ceiling(nparam/lnr) par(mfrow=c(nr,nc)) if (!is.null(main)) par(oma=c(1,1,3,0)) # Computting the weighted ECDF for each parameter ncharmax <- max(nchar(param.names)) for ( i in 1:nparam ) { if ( !is.null(weights) ) { char1 <- "weighted ECDF" char2 <- "wECDF" } else { char1 <- "ECDF" char2 <- "ECDF" } # ELSE end if (verbose) message("[ Computing the ", char1, " for '", format(param.names[p], width=ncharmax), "' , ", p, "/", nparam.bak, " => ", format(round(100*i/nparam.bak, 2), width=5, nsmall=2, justify="left"), "% ]" ) # Weighted ECDF for the "i-th" desired quantile, where the unweighted # 'i-th' quantile of each behavioural parameter set is now weighted by the # weights given by 'w' (usually, the normalized less-formal likelihood) p.ecdf <- Hmisc::wtd.Ecdf(params[, i], weights = weights, normwt=TRUE) ecdf[[p]] <- p.ecdf names(ecdf)[p] <- param.names[p] ################### PLOTTING ########################################### if (plot) { if ( !is.null(obs) & is.numeric(obs) ) { if (is.na(match(class(obs), c("zoo", "numeric", "integer") ) ) ) stop("Invalid argument: 'class(obs)' must be in c('zoo', 'numeric', 'integer')") # Observed value quantile.obs <- as.numeric(obs[p]) } # IF end # plot label main.loc <- paste(char2, "of", param.names[p], sep=" ") # Drawing the plotting the area, but without Y-axis # cex fix the point size in the ecdf plot(p.ecdf$x, p.ecdf$ecdf, xlab= param.names[p], main=main.loc, ylab=ylab, col=col, yaxt = "n", type="b", cex=0.2, cex.axis=cex.axis, cex.main=cex.main, cex.lab=cex.lab, font.lab=2, ... ) # Drawing the labels in the 'y' axis Axis(side = 2, at = seq(0.0, 1, by=0.05), labels = FALSE, cex.axis=cex.axis, cex.lab=cex.lab) Axis(side = 2, at = seq(0.0, 1, by=0.1), labels = seq(0.0, 1, by=0.1), cex.axis=cex.axis, cex.lab=cex.lab, font.lab=2 ) # Drawing a vertical line on the observed quantile Q5, Q50, Q95 if ( !is.null(obs) & is.numeric(obs) ) abline(v=quantile.obs, lty=3, col="black", lwd=2) # Drawing an horizontal line on Probability = 0.5 abline(h=0.5, lty=2, col="grey", lwd=2) # Computing a function that give the 'x' value that corresponds to a # given value of cdf, by using linear interpolation f <- approxfun(p.ecdf$ecdf, p.ecdf$x) # Quantile corresponding to a ecdf=0.5 quantile.sim <- f(0.5) # Drawing a vertical line on the simulated quantile Q5, Q50, Q95 abline(v=quantile.sim, lty=3, col="grey", lwd=2) # Drawing a legend if ( !is.null(obs) & is.numeric(obs) ) { # Bias of the simulated streamflows, in percentage [%] bias <- 100 * (quantile.sim - quantile.obs) / quantile.obs if (bias == 0) txt.col <- "green" if (bias < 0) txt.col <- "red" if (bias > 0) txt.col <- "blue" # Presenting the value of the observed Q5 as a legend leg.txt <- c( paste("Obs : ", round(quantile.obs,3) ) , paste("Q50 sim: ", round(quantile.sim,3) ), paste("Bias : ", round(bias,1), "[%]", sep=" " ) ) legend(leg.pos, legend=leg.txt, inset=0.02, bty="n", cex =leg.cex, text.col=c("black", "black", txt.col)) } else { leg.txt <- paste("Q50 sim: ", round(quantile.sim,3) ) legend(leg.pos, legend=leg.txt, bty="n", cex=leg.cex) } # ELSE end } # IF end p <- p+1 } # FOR i end if (!is.null(main)) mtext(main, side=3, line=1, cex=cex.main, outer=TRUE) if (do.png) dev.off() } # FOR f end return(ecdf) } # END 'params2ecdf.default' ################################################################################ # Author : Mauricio Zambrano-Bigiarini # # Started: 12-Oct-2011 # # Updates: 12-Oct-2011 ; 19-Nov-2012 # ################################################################################ params2ecdf.matrix <- function(params, param.names=colnames(params), gofs=NULL, MinMax=NULL, beh.thr=NA, weights=NULL, byrow=FALSE, plot=TRUE, obs=NULL, main=NULL, nrows="auto", ylab="Probability", col="blue", leg.cex=1.2, leg.pos="topleft", cex.axis=1.2, cex.main=1.2, cex.lab=1.2, verbose=TRUE, ..., #### PNG options ### do.png=FALSE, png.width=1500, png.height=900, png.res=90, png.fname="Params_ECDFs.png" ) { params2ecdf.default(params=params, param.names=param.names, gofs=gofs, MinMax=MinMax, beh.thr=beh.thr, weights=weights, byrow=byrow, plot=plot, obs=obs, main=main, nrows=nrows, ylab=ylab, col=col, leg.cex=leg.cex, leg.pos=leg.pos, cex.axis=cex.axis, cex.main=cex.main, cex.lab=cex.lab, verbose=verbose, ..., # PNG options do.png=do.png, png.width=png.width, png.height=png.height, png.res=png.res, png.fname=png.fname ) } # END 'params2ecdf.data.frame' ################################################################################ # Author : Mauricio Zambrano-Bigiarini # # Started: 12-Oct-2011 # # Updates: 12-Oct-2011 ; 19-Nov-2012 # ################################################################################ params2ecdf.data.frame <- function(params, param.names=colnames(params), gofs=NULL, MinMax=NULL, beh.thr=NA, weights=NULL, byrow=FALSE, plot=TRUE, obs=NULL, main=NULL, nrows="auto", ylab="Probability", col="blue", leg.cex=1.2, leg.pos="topleft", cex.axis=1.2, cex.main=1.2, cex.lab=1.2, verbose=TRUE, ..., #### PNG options ### do.png=FALSE, png.width=1500, png.height=900, png.res=90, png.fname="Params_ECDFs.png" ) { params <- as.matrix(params) params2ecdf.default(params=params, param.names=param.names, gofs=gofs, MinMax=MinMax, beh.thr=beh.thr, weights=weights, byrow=byrow, plot=plot, obs=obs, main=main, nrows=nrows, ylab=ylab, col=col, leg.cex=leg.cex, leg.pos=leg.pos, cex.axis=cex.axis, cex.main=cex.main, cex.lab=cex.lab, verbose=verbose, ..., # PNG options do.png=do.png, png.width=png.width, png.height=png.height, png.res=png.res, png.fname=png.fname ) } # END 'params2ecdf.data.frame'