├── .Rbuildignore ├── .gitignore ├── data └── obj.rda ├── NAMESPACE ├── R ├── na.interp.R ├── runmin.R ├── runmean.R ├── runmax.R ├── season.bootstrap.R ├── season_bootstrap.R ├── simulate_demand.R ├── simulate_res.R ├── demand_model.R ├── blockstat.R ├── newpredict.R ├── simulate.temp.R ├── simulate_ddemand.R ├── maketemps.R └── temp_bootstrap.R ├── MEFM-package.Rproj ├── man ├── seasondays.Rd ├── sa.econ.Rd ├── maketemps.Rd ├── simulate_ddemand.Rd ├── simulate_demand.Rd ├── MEFM-package.Rd ├── demand_model.Rd └── sa.Rd ├── DESCRIPTION └── README.md /.Rbuildignore: -------------------------------------------------------------------------------- 1 | ^.*\.Rproj$ 2 | ^\.Rproj\.user$ 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .Rproj.user 2 | .Rhistory 3 | .RData 4 | .Ruserdata 5 | -------------------------------------------------------------------------------- /data/obj.rda: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robjhyndman/MEFM-package/HEAD/data/obj.rda -------------------------------------------------------------------------------- /NAMESPACE: -------------------------------------------------------------------------------- 1 | export(demand_model,simulate_ddemand,simulate_demand,maketemps,blockstat,simulate.temp,season.bootstrap,na.interp) 2 | 3 | importFrom(splines,ns) 4 | importFrom(forecast,msts) 5 | -------------------------------------------------------------------------------- /R/na.interp.R: -------------------------------------------------------------------------------- 1 | na.interp <- 2 | function(x) 3 | { 4 | # interpolates missing values 5 | n <- length(x) 6 | nas <- is.na(x) 7 | idx <- (1:n)[!nas] 8 | xx <- as.ts(approx(idx,x[idx],1:n)$y) 9 | tsp(xx) <- tsp(x) 10 | return(xx) 11 | } 12 | -------------------------------------------------------------------------------- /R/runmin.R: -------------------------------------------------------------------------------- 1 | runmin <- 2 | function(x, k) 3 | { 4 | n <- length(x) 5 | y <- rep(NA, n) 6 | a <- y[k] <- min(x[1:k]) 7 | for (i in (k+1):n) 8 | { 9 | y[i] <- min(x[i - 0:(k-1)],na.rm=TRUE) # calculate min of the window 10 | } 11 | return(y) 12 | } 13 | -------------------------------------------------------------------------------- /R/runmean.R: -------------------------------------------------------------------------------- 1 | runmean <- 2 | function(x, k) 3 | { 4 | n <- length(x) 5 | y <- x[ k:n ] - x[ c(1,1:(n-k)) ] 6 | # this is a difference from the previous cell 7 | y[1] <- sum(x[1:k],na.rm=T) # find the first sum 8 | y <- cumsum(y)/k # apply precomputed differences 9 | return(c(rep(NA,k-1),y)) 10 | } 11 | -------------------------------------------------------------------------------- /R/runmax.R: -------------------------------------------------------------------------------- 1 | runmax <- 2 | function(x, k) 3 | { 4 | n <- length(x) 5 | y <- rep(NA, n) 6 | a <- y[k] <- max(x[1:k]) 7 | for (i in (k+1):n) 8 | { 9 | # there are empty values in temperature file, cause errors when excuting 'if' 10 | y[i] <- max(x[i - 0:(k-1)], na.rm=TRUE) # calculate max of the window 11 | } 12 | return(y) 13 | } 14 | -------------------------------------------------------------------------------- /R/season.bootstrap.R: -------------------------------------------------------------------------------- 1 | season.bootstrap <- 2 | function(x,m,n=length(x),periods=48) 3 | { 4 | tmp <- season_bootstrap(x,m,periods) 5 | if(n > length(tmp)){ 6 | ns <- trunc(n / length(x)) + 1 7 | for(i in 2:ns) 8 | tmp <- c(tmp,season_bootstrap(x,m,periods)) 9 | } 10 | return(ts(tmp[1:n],start=start(x),frequency=frequency(x))) 11 | } 12 | -------------------------------------------------------------------------------- /MEFM-package.Rproj: -------------------------------------------------------------------------------- 1 | Version: 1.0 2 | 3 | RestoreWorkspace: Default 4 | SaveWorkspace: Default 5 | AlwaysSaveHistory: Default 6 | 7 | EnableCodeIndexing: Yes 8 | UseSpacesForTab: Yes 9 | NumSpacesForTab: 2 10 | Encoding: UTF-8 11 | 12 | RnwWeave: Sweave 13 | LaTeX: pdfLaTeX 14 | 15 | BuildType: Package 16 | PackageUseDevtools: Yes 17 | PackageInstallArgs: --no-multiarch --with-keep.source 18 | -------------------------------------------------------------------------------- /R/season_bootstrap.R: -------------------------------------------------------------------------------- 1 | season_bootstrap <- 2 | function(x,m,periods=48) 3 | { 4 | # Arrange data in a matrix with one block per row 5 | n <- length(x) 6 | nblocks <- trunc(n/periods/m) 7 | n <- periods*nblocks*m 8 | xmat <- matrix(x[1:n],ncol=m*periods,nrow=nblocks,byrow=TRUE) 9 | 10 | # Permute rows 11 | j <- sample(1:nblocks,nblocks,replace=TRUE) 12 | 13 | # Return as vector 14 | newx <- c(t(xmat[j,])) 15 | return(na.omit(newx)) 16 | } 17 | -------------------------------------------------------------------------------- /man/seasondays.Rd: -------------------------------------------------------------------------------- 1 | \name{seasondays} 2 | \alias{seasondays} 3 | \docType{data} 4 | \title{ 5 | The number of days in a season 6 | } 7 | \description{ 8 | The number of days in a season, set to 182 for a summer season. 9 | } 10 | 11 | \details{A "season" is taken to be the period over which a model is estimated. In Hyndman & Fan (2010), the season was 1 November to 31 March and so \code{seasondays=151}. The default value in the package is \code{seasondays=182}; i.e., six months. The value of \code{seasondays} can be changed by the user. 12 | } 13 | 14 | \keyword{datasets} 15 | -------------------------------------------------------------------------------- /DESCRIPTION: -------------------------------------------------------------------------------- 1 | Package: MEFM 2 | Type: Package 3 | Title: Monash Electricity Forecasting Model 4 | Version: 2.2 5 | Date: 2016-06-01 6 | Authors@R: c( 7 | person("Rob", "Hyndman", email="Rob.Hyndman@monash.edu", role=c("aut", "cre", "cph"), comment = c(ORCID = "0000-0002-2140-5352")), 8 | person("Shu", "Fan", role = "aut") 9 | ) 10 | Depends: R(>= 2.14.0), forecast, splines 11 | Description: This package includes a set of tools for implementing the Monash Electricity Forecasting Model for electricity demand. The package requires the following data as input: half-hourly/hourly electricity demands; half-hourly/hourly temperatures at one or two locations; seasonal demographical and economical data; public holiday data. The formats of the required data are described in the help files. 12 | License: GPL (>=2) 13 | LazyData: yes 14 | LazyLoad: yes 15 | -------------------------------------------------------------------------------- /R/simulate_demand.R: -------------------------------------------------------------------------------- 1 | simulate_demand <- 2 | function(sim,afcast,nyears=length(sim$hhfit)/seasondays/48,periods=48) 3 | { 4 | n <- nyears*seasondays*periods 5 | 6 | # forecast demand difference of annual average demand 7 | afit <- predict(sim$a,newdata=afcast,se=TRUE) 8 | avar <- afit$se^2 + summary(sim$a)$sigma^2 9 | # rnorm 10 | afit <- rep(rnorm(nyears,afit$fit,sqrt(avar)),each=seasondays*periods) 11 | 12 | ############################################################## 13 | # total below should equal to sim$demand 14 | total <- c(sim$hhfit)+c(sim$hhres) 15 | 16 | # use log for both annual and half-hourly sim 17 | dem <- exp(total[1:n]) * afit 18 | 19 | annmax <- blockstat(dem,seasondays,max,fill=FALSE,periods=periods) 20 | dem <- matrix(dem,nrow=seasondays*periods,byrow=FALSE) 21 | 22 | return(list(demand=ts(dem,frequency=periods,start=1),annmax=annmax)) 23 | } 24 | -------------------------------------------------------------------------------- /R/simulate_res.R: -------------------------------------------------------------------------------- 1 | simulate_res <- 2 | function(hdata,model,k,hfits,periods=48) 3 | { 4 | ########################################################### 5 | # normalize the model residuals, and then bootstrap, finally add back the average vlaues 6 | res.ave <- blockstat(model$hhres,k,median,fill=FALSE,periods=periods) # median value of each 35 days 7 | resave.ar <- ar(res.ave,order.max=1) # ar: Fit Autoregressive Models 8 | n <- length(res.ave) 9 | # resave.sim: Simulate from an ARIMA Model 10 | resave.sim <- arima.sim(list(ar=resave.ar$ar),n=n+20, innov=rnorm(n+20,0,sqrt(resave.ar$var.pred)))[-(1:20)] 11 | 12 | resave.sim <- ts(rep(resave.sim,each=k*periods)[1:length(model$hhres)]) 13 | res.ave <- ts(rep(res.ave,each=k*periods)[1:length(model$hhres)]) 14 | tsp(res.ave) <- tsp(resave.sim) <- tsp(model$hhres) 15 | hres <- season.bootstrap(model$hhres-res.ave,k,periods=periods)+resave.sim 16 | ########################################################### 17 | 18 | # Bias adjustment 19 | # mean adjustment 20 | 21 | return(hres) 22 | } 23 | -------------------------------------------------------------------------------- /man/sa.econ.Rd: -------------------------------------------------------------------------------- 1 | \name{sa.econ} 2 | \alias{sa.econ} 3 | \docType{data} 4 | \title{ 5 | Historical demographic & economic data for South Australia 6 | } 7 | \description{ 8 | Annual data for South Australia including population, GSP, residential electricity price, total electricity price and cooling/heating degree days 9 | } 10 | \format{ A multivariate time series with the following columns: 11 | \describe{ 12 | \item{pop}{State population (thousands of people)} 13 | \item{gsp}{Gross State Product chain volume estimate (in 2008-2009 millions of dollars)} 14 | \item{resiprice}{Residential price index (2008-2009 cents per kWh)} 15 | \item{totalprice}{Total price (2008-2009 cents per kWh)} 16 | \item{anndemand}{Annual electricity demand (GW).} 17 | \item{ddays}{Cooling degree days with a threshold of 18.5 degrees Celsius.} 18 | }} 19 | 20 | \references{ 21 | R. J. Hyndman and S. Fan (2010) "Density Forecasting for Long-term Peak Electricity Demand", IEEE Trans. Power Systems, 25(2), 1142--1153. 22 | } 23 | 24 | \examples{ 25 | plot(sa.econ) 26 | } 27 | \keyword{datasets} 28 | -------------------------------------------------------------------------------- /man/maketemps.Rd: -------------------------------------------------------------------------------- 1 | \name{maketemps} 2 | \alias{maketemps} 3 | \title{ 4 | Create lagged temperature variables 5 | } 6 | \description{ 7 | The function is used to create lagged temperature variables for model estimation. 8 | } 9 | \usage{ 10 | maketemps(x, temp_sites, periods = 48) 11 | } 12 | \arguments{ 13 | \item{x}{ 14 | Data frame including temperature observations from 1 or 2 weather stations 15 | } 16 | \item{temp_sites}{ 17 | The number of weather stations, select between 1 and 2 18 | } 19 | \item{periods}{ 20 | The periods within a day, choose between 48 (half-hourly data) and 24 (hourly data) 21 | } 22 | } 23 | \value{ 24 | The complete data frame that can be used for model estimation. 25 | } 26 | \references{ 27 | R. J. Hyndman and S. Fan (2010) "Density Forecasting for Long-term Peak Electricity Demand", IEEE Trans. Power Systems, 25(2), 1142--1153. 28 | } 29 | \author{ 30 | Rob J Hyndman and Shu Fan 31 | } 32 | 33 | \seealso{ 34 | \code{\link{sa}}, 35 | \code{\link{demand_model}} 36 | } 37 | 38 | \examples{ 39 | sa <- maketemps(sa,2,48) 40 | 41 | } 42 | 43 | \keyword{Temperature} 44 | \keyword{Electricity demand model} 45 | -------------------------------------------------------------------------------- /R/demand_model.R: -------------------------------------------------------------------------------- 1 | demand_model <- 2 | function(hhdata,adata,hhoptformula,aoptformula) 3 | { 4 | hhmodels <- list() 5 | periods <- length(unique(hhdata$timeofday)) 6 | 7 | # half-hourly model 8 | for(i in 1:periods) 9 | hhmodels[[i]] <- lm(hhoptformula[[i]],data=hhdata[hhdata$timeofday==(i-1),],na.action=na.exclude) 10 | 11 | # Compute hh fits and residuals 12 | fits <- matrix(NA,nrow(hhdata)/periods,periods) 13 | for(i in 1:periods) 14 | fits[,i] <- fitted(hhmodels[[i]]) 15 | fits <- c(t(fits)) 16 | hhfits <- fits 17 | hhres <- ts(log(hhdata$ddemand) - fits,frequency=seasondays*periods,start=hhdata$Year[1]) 18 | fits <- exp(fits) 19 | 20 | # annual model 21 | amodel <- lm(aoptformula, data=adata) 22 | 23 | # Compute annual fits and residuals 24 | afits <- fitted(amodel) 25 | 26 | # Compute fitted values and residuals 27 | fits <- ts(fits * rep(afits,c(table(hhdata$fyear))),frequency=seasondays*periods,start=hhdata$Year[1]) 28 | res <- ts(hhdata$demand - fits,frequency=seasondays*periods,start=hhdata$Year[1]) 29 | 30 | return(list(hh=hhmodels,hhfits=hhfits,hhres=hhres,a=amodel,afits=afits,fits=fits,res=res)) 31 | } 32 | -------------------------------------------------------------------------------- /R/blockstat.R: -------------------------------------------------------------------------------- 1 | blockstat <- 2 | function(x,m=14,FUN=max,...,fill=FALSE,periods=48) 3 | { 4 | # fill the partial block with data from the same period in the past year, if the fill flag is TRUE 5 | if (fill==TRUE & trunc(length(x)/periods/seasondays+0.9999)!=trunc(length(x)/periods/seasondays)) 6 | { 7 | n_days <- length(x)/periods 8 | n_days_w = trunc(n_days/seasondays + 0.9999) * seasondays 9 | 10 | x[(n_days*periods+1):(n_days_w*periods)] <- x[(n_days*periods+1):(n_days_w*periods) - seasondays] 11 | } 12 | # Arrange data in a matrix with one block per row 13 | if(m == 7) # when calculating weekly index, there may be partial week to be removed at the end of the year 14 | { 15 | nbyear <- trunc(seasondays/m) 16 | nyears <- trunc(length(x)/periods/seasondays) 17 | # remove the partial week at the end of each year if existed 18 | if(seasondays-nbyear*m) 19 | { 20 | tmp <- rep(((nbyear*m+1):seasondays),nyears) + rep((0:(nyears-1))*seasondays,each=(seasondays-nbyear*m)) 21 | x <- x[-(rep((tmp-1)*periods,each=periods) + rep(1:periods,length(tmp)))] 22 | } 23 | nblocks <- trunc(length(x)/periods/m) 24 | n <- periods*nblocks*m 25 | } 26 | else 27 | { 28 | nblocks <- trunc(length(x)/periods/m + 0.99999) 29 | n <- periods*nblocks*m 30 | } 31 | 32 | xmat <- matrix(x[1:n],ncol=m*periods,nrow=nblocks,byrow=TRUE) 33 | return(apply(xmat,1,FUN,na.rm=TRUE,...)) 34 | } 35 | -------------------------------------------------------------------------------- /man/simulate_ddemand.Rd: -------------------------------------------------------------------------------- 1 | \name{simulate_ddemand} 2 | \alias{simulate_ddemand} 3 | \title{ 4 | Temperature and demand Simulation 5 | } 6 | \description{ 7 | Simulate the half-hourly/hourly temperature and demand 8 | } 9 | \usage{ 10 | simulate_ddemand(model, hdata, simyears = 1000, delta = 5) 11 | } 12 | \arguments{ 13 | \item{model}{ 14 | The demand models 15 | } 16 | \item{hdata}{ 17 | The half-hourly/hourly demand, temperature and seasonality data 18 | } 19 | \item{simyears}{ 20 | The length of years of simulation 21 | } 22 | \item{delta}{ 23 | The value of blockdays offset limit for bootstrap 24 | } 25 | } 26 | \details{ 27 | Simulate the half-hourly/hourly temperature and demand using the historical data and the half-hourly/hourly demand model 28 | } 29 | \value{ 30 | An object of class \code{simdemand}, basically a list including elements 31 | \item{hhfit}{Simulated half-hourly demand} 32 | \item{hhres}{Simulated half-hourly residuals} 33 | \item{ores}{Simulated half-hourly offset demand} 34 | \item{a}{Seasonal model} 35 | } 36 | \references{ 37 | R. J. Hyndman and S. Fan (2010) "Density Forecasting for Long-term Peak Electricity Demand", IEEE Trans. Power Systems, 25(2), 1142--1153. 38 | } 39 | \author{ 40 | Rob J Hyndman and Shu Fan 41 | } 42 | \seealso{ 43 | \code{\link{demand_model}}, 44 | \code{\link{simulate_demand}}, 45 | \code{\link{sa}}, 46 | \code{\link{MEFM-package}} for examples. 47 | } 48 | 49 | \keyword{Electricity demand model} 50 | \keyword{Temperature simulate} -------------------------------------------------------------------------------- /man/simulate_demand.Rd: -------------------------------------------------------------------------------- 1 | \name{simulate_demand} 2 | \alias{simulate_demand} 3 | \title{ 4 | Simulate the electricity demand for the next season 5 | } 6 | \description{ 7 | Simulate the half-hourly/hourly, seasonal peak electricity demand for the next season 8 | } 9 | \usage{ 10 | simulate_demand(sim, afcast, nyears = length(sim$hhfit)/seasondays/48, periods = 48) 11 | } 12 | \arguments{ 13 | \item{sim}{ 14 | The simulated half-hourly demand (normalized against seasonal average demand) 15 | } 16 | \item{afcast}{ 17 | The demographic and economic forecasts for the next season 18 | } 19 | \item{nyears}{ 20 | The length of years of simulation 21 | } 22 | \item{periods}{ 23 | The periods within a day, choose between 48 (half-hourly data) and 24 (hourly data) 24 | } 25 | } 26 | \details{ 27 | Simulate/forecast the half-hourly/hourly, seasonal peak electricity demand for the next season by incorporating the seasonal 28 | demographic & economic forecasts (to be provided by the user) 29 | } 30 | \value{ 31 | \item{demand}{The forecasted half-hourly demand} 32 | \item{annmax}{The forecasted seasonal maximum demand} 33 | } 34 | \references{ 35 | R. J. Hyndman and S. Fan (2010) "Density Forecasting for Long-term Peak Electricity Demand", IEEE Trans. Power Systems, 25(2), 1142--1153. 36 | } 37 | \author{ 38 | Rob J. Hyndman and Shu Fan 39 | } 40 | \seealso{ 41 | \code{\link{demand_model}}, 42 | \code{\link{simulate_ddemand}}, 43 | \code{\link{MEFM-package}} for examples. 44 | } 45 | 46 | \keyword{Electricity demand model} 47 | \keyword{Forecast} 48 | -------------------------------------------------------------------------------- /R/newpredict.R: -------------------------------------------------------------------------------- 1 | newpredict <- 2 | function(model,hdata,blocklength,allperiods=TRUE,delta,periods=48) 3 | { 4 | # check if there is the second temperature sites 5 | temp_sites = 1 6 | if (is.element("temp2",colnames(hdata))) 7 | temp_sites = 2 8 | 9 | ############################################################################################# 10 | # simulate temperature 11 | newsa<- simulate.temp(hdata,n=nrow(hdata)+seasondays*periods,m=20,temp_sites,delta=delta,periods=periods)$newtemp 12 | 13 | #hres <- newsa[,"hhres"] 14 | #hres <- hres[-(1:(seasondays*periods))] 15 | newsa <- newsa[,c("temp1","temp2")] 16 | 17 | ##################################################################### 18 | # store the simulated temperature 19 | simtemp <- 0.5*(newsa[,"temp1"]+newsa[,"temp2"]) 20 | simtemp <- simtemp[-(1:(seasondays*periods))] 21 | ##################################################################### 22 | 23 | newsa <- maketemps(newsa,temp_sites,periods=periods) 24 | # remove the first seasondays*periods (due to the NAs in the first several days) 25 | newsa <- newsa[-(1:(seasondays*periods)),] 26 | # Patch in other variables 27 | bak <- hdata 28 | hdata[,colnames(newsa)] <- newsa 29 | newsa <- hdata 30 | hdata <- bak 31 | 32 | # Half-hourly model predictions 33 | hfits <- matrix(-10,nrow(hdata)/periods,periods) 34 | if(allperiods) 35 | j <- 1:periods 36 | else 37 | j <- 16:periods 38 | for(i in j) 39 | hfits[,i] <- predict(model$hh[[i]],newdata=newsa[newsa$timeofday==(i-1),]) 40 | 41 | # Vectorize 42 | hfits <- c(t(hfits)) 43 | 44 | # Half hourly residuals (done with the temperature simulation) 45 | #hres <- simulate_res(hdata,model,blocklength,hfits,periods=periods) # still use the original residual simulation 46 | #return(list(hfit=hfits, hres=hres, simtemp=simtemp)) 47 | return(list(hfit=hfits, simtemp=simtemp)) 48 | } 49 | -------------------------------------------------------------------------------- /R/simulate.temp.R: -------------------------------------------------------------------------------- 1 | simulate.temp <- 2 | function(x,n=nrow(x),m=9,temp_sites=2,delta=5,periods=48) 3 | { 4 | if(m > 30) 5 | stop("Blockdays too large") 6 | if(m < delta) #change to delta, the value of blockdays offset limit 7 | stop("Blockdays too small") 8 | m <- round(m) # Must be integer 9 | 10 | # different numbers of temperature sites 11 | if (temp_sites == 1) 12 | { 13 | xtemp <- x[,"temp1",drop=FALSE] # one-column dataframe, use drop=FALSE to avoid coercing to vector 14 | # add in the residuals 15 | xtemp <- data.frame(temp1=x$temp1) 16 | 17 | tmp<- temp_bootstrap(xtemp,m,delta,periods) 18 | newtemp <- data.frame(temp1=tmp$newx) 19 | newindex <- tmp$newindex 20 | } 21 | if (temp_sites == 2) 22 | { 23 | # add in the residuals 24 | xtemp <- data.frame(temp1=x$temp1,temp2=x$temp2) 25 | 26 | tmp <- temp_bootstrap(xtemp,m,delta=delta,periods) 27 | newtemp <- tmp$newx 28 | newindex <- tmp$newindex 29 | } 30 | ############################# 31 | 32 | # Repeat if necessary 33 | nn <- nrow(xtemp) 34 | if(n > nn) 35 | { 36 | if((nn %% seasondays*periods) != 0) # partial 37 | { 38 | fullyears <- trunc(nn/(seasondays*periods)) 39 | nn <- seasondays*periods*fullyears 40 | if (temp_sites == 1) 41 | newtemp <- data.frame(temp1=newtemp[1:nn,]) # coerce vector to dataframe if there is one-column dataframe 42 | else 43 | newtemp <- newtemp[1:nn,] 44 | } 45 | nsamp <- trunc(n/nn) + 1 46 | for(i in 1:nsamp){ 47 | if (temp_sites == 1){ 48 | tmp <- temp_bootstrap(xtemp,m,delta=delta) 49 | newtemp <- rbind(newtemp,data.frame(temp1=tmp$newx[1:nn,])) 50 | newindex <- c(newindex,tmp$newindex[1:nn]) 51 | } 52 | else{ 53 | tmp <- temp_bootstrap(xtemp,m,delta=delta) 54 | newtemp <- rbind(newtemp,tmp$newx[1:nn,]) 55 | newindex <- c(newindex,tmp$newindex[1:nn]) 56 | } 57 | } 58 | } 59 | 60 | # Return result as a time series 61 | if (temp_sites == 1){ 62 | newtemp <- ts(data.frame(temp1=newtemp[1:n,]),start=1,frequency=seasondays*periods) 63 | newindex <- newindex[1:n] 64 | } 65 | else{ 66 | newtemp <- ts(newtemp[1:n,],start=1,frequency=seasondays*periods) 67 | newindex <- newindex[1:n] 68 | } 69 | return(list(newtemp=newtemp,newindex=newindex)) # output the simulation index for use in the PV simulation 70 | } 71 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #MEFM package 2 | 3 | The R package *MEFM* includes a set of tools for implementing the Monash Electricity Forecasting Model based on the paper by [Hyndman and Fan (2010)](http://robjhyndman.com/papers/peak-electricity-demand/). 4 | 5 | The package requires the following data as input: half-hourly/hourly electricity demands; half-hourly/hourly temperatures at one or two locations; 6 | seasonal demographical and economical data; public holiday data. The formats of the required data are described in the help files. 7 | 8 | Some documentation of the underlying model is provided at [robjhyndman.com/publications/mefm/](https://robjhyndman.com/publications/mefm/) 9 | 10 | ## Installation 11 | 12 | You can install the latest version from 13 | [Github](https://github.com/robjhyndman/MEFM-package) 14 | 15 | ```s 16 | # install.packages("devtools") 17 | library(devtools) 18 | install_github("robjhyndman/MEFM-package") 19 | ``` 20 | 21 | ## Usage 22 | 23 | ```s 24 | library(MEFM) 25 | 26 | formula.hh <- list() 27 | for(i in 1:48) 28 | formula.hh[[i]] = as.formula(log(ddemand) ~ ns(temp, df=2) + day 29 | + holiday + ns(timeofyear, 9) + ns(avetemp, 3) + ns(dtemp, 3) + ns(lastmin, 3) 30 | + ns(prevtemp1, df=2) + ns(prevtemp2, df=2) 31 | + ns(prevtemp3, df=2) + ns(prevtemp4, df=2) 32 | + ns(day1temp, df=2) + ns(day2temp, df=2) 33 | + ns(day3temp, df=2) + ns(prevdtemp1, 3) + ns(prevdtemp2, 3) 34 | + ns(prevdtemp3, 3) + ns(day1dtemp, 3)) 35 | 36 | # formula for annual model, to be given by the user 37 | formula.a <- as.formula(anndemand ~ gsp + ddays + resiprice) 38 | 39 | # create lagged temperature variables 40 | sa <- maketemps(sa,2,48) 41 | 42 | sa.model <- demand_model(sa, sa.econ, formula.hh, formula.a) 43 | 44 | summary(sa.model$a) 45 | summary(sa.model$hh[[33]]) 46 | 47 | # Simulate future normalized half-hourly data 48 | simdemand <- simulate_ddemand(sa.model, sa, simyears=10) 49 | 50 | # seasonal economic and weather forecast, to be given by user 51 | afcast <- data.frame(pop=1694, gsp=22573, resiprice=34.65, ddays=642) 52 | 53 | # Simulate half-hourly data 54 | demand <- simulate_demand(simdemand, afcast) 55 | 56 | # Illustrate the results 57 | plot(density(demand$annmax, bw="SJ"), 58 | main="Density of seasonal maximum demand", xlab="Demand") 59 | ``` 60 | 61 | ## License 62 | 63 | This package is free and open source software, licensed under GPL (>= 2). 64 | -------------------------------------------------------------------------------- /R/simulate_ddemand.R: -------------------------------------------------------------------------------- 1 | simulate_ddemand <- 2 | function(model, hdata, simyears=1000, delta=5) 3 | { 4 | periods <- length(unique(hdata$timeofday)) 5 | 6 | # Generate nsim years of adjusted log demand 7 | nh <- nrow(hdata) 8 | nhdata <- trunc(nrow(hdata)/seasondays/periods + 0.9999) 9 | nsim <- trunc(simyears/nhdata + 0.9999) 10 | nyears <- nsim*nhdata # actual years to be simulated 11 | hfit.sim <- hres.sim <- ores <- temp.sim <- matrix(NA,seasondays*periods,nyears) 12 | for(i in 1:nsim){ 13 | # Simulated adjusted log demand 14 | tmp <- newpredict(model,hdata,blocklength=35,delta=delta,periods=periods) 15 | hfit <- tmp$hfit 16 | #hres <- tmp$hres 17 | simtemp <- tmp$simtemp 18 | 19 | # Half hourly residuals (done with the temperature simulation) 20 | hres <- simulate_res(hdata,model,35,hfit,periods=periods) # still use the original residual simulation 21 | 22 | # fix partial data at the end if any, copy and paste the corresponding part of the last year 23 | if((nhdata*seasondays*periods-nh)!=0){ 24 | hfit <- c(hfit,hfit[(length(hfit)-seasondays*periods) + 1:(nhdata*seasondays*periods-nh)]) 25 | hres <- c(hres,hres[(length(hres)-seasondays*periods) + 1:(nhdata*seasondays*periods-nh)]) 26 | } 27 | 28 | ############################################################## 29 | # fix partial data at the end if any, copy and paste the corresponding part of the last year 30 | if((nhdata*seasondays*periods-nh)!=0) 31 | simtemp <- c(simtemp,simtemp[(length(simtemp)-seasondays*periods) + 1:(nhdata*seasondays*periods-nh)]) 32 | temp.sim[,(i-1)*nhdata + 1:nhdata] <- matrix(simtemp,nrow=seasondays*periods,ncol=nhdata,byrow=F) 33 | ############################################################## 34 | 35 | hfit.sim[,(i-1)*nhdata + 1:nhdata] <- matrix(hfit,nrow=seasondays*periods,ncol=nhdata,byrow=F) 36 | hres.sim[,(i-1)*nhdata + 1:nhdata] <- matrix(hres,nrow=seasondays*periods,ncol=nhdata,byrow=F) 37 | 38 | # simple seasonal bootstrap for offset demand: season.bootstrap() 39 | ores[,(i-1)*nhdata + 1:nhdata] <- matrix(season.bootstrap(hdata$doffset[20000:nh],20,n=nhdata*seasondays*periods,periods), 40 | nrow=seasondays*periods,ncol=nhdata,byrow=F) 41 | } 42 | 43 | return(structure(list(hhfit=ts(hfit.sim[,1:simyears],frequency=periods,start=1), 44 | hhres=ts(hres.sim[,1:simyears],frequency=periods,start=1), 45 | ores=ts(ores[,1:simyears],frequency=periods,start=1),a=model$a),class="simdemand")) 46 | } 47 | -------------------------------------------------------------------------------- /man/MEFM-package.Rd: -------------------------------------------------------------------------------- 1 | \name{MEFM-package} 2 | \alias{MEFM-package} 3 | \alias{MEFM} 4 | \docType{package} 5 | \title{ 6 | Monash Electricity Forecasting Model 7 | } 8 | \description{ 9 | This package includes a set of tools for implementing the Monash Electricity Forecasting Model based on the paper by Hyndman and Fan (2010). 10 | 11 | The package requires the following data as input: half-hourly/hourly electricity demands; half-hourly/hourly temperatures at one or two locations; seasonal demographic and economic data; public holiday data. The formats of the required data are described in the help files. 12 | } 13 | \author{ 14 | Rob J Hyndman and Shu Fan. 15 | 16 | Maintainer: Rob J. Hyndman ; Shu Fan 17 | } 18 | \references{ 19 | R. J. Hyndman and S. Fan (2010) "Density Forecasting for Long-term Peak Electricity Demand", IEEE Trans. Power Systems, 25(2), 1142--1153. 20 | \url{http://robjhyndman.com/papers/peak-electricity-demand/} 21 | 22 | R. J. Hyndman and S. Fan (2014) "Monash Electricity Forecasting Model" Version 2014.1. 23 | \url{http://robjhyndman.com/working-papers/mefm/} 24 | } 25 | 26 | \examples{ 27 | # formula for half-hourly model, to be given by the user 28 | formula.hh <- list() 29 | for(i in 1:48) 30 | formula.hh[[i]] = as.formula(log(ddemand) ~ ns(temp, df=2) + day 31 | + holiday + ns(timeofyear, 9) + ns(avetemp, 3) + ns(dtemp, 3) + ns(lastmin, 3) 32 | + ns(prevtemp1, df=2) + ns(prevtemp2, df=2) 33 | + ns(prevtemp3, df=2) + ns(prevtemp4, df=2) 34 | + ns(day1temp, df=2) + ns(day2temp, df=2) 35 | + ns(day3temp, df=2) + ns(prevdtemp1, 3) + ns(prevdtemp2, 3) 36 | + ns(prevdtemp3, 3) + ns(day1dtemp, 3)) 37 | 38 | # Formula for annual model, to be given by the user 39 | formula.a <- as.formula(anndemand ~ gsp + ddays + resiprice) 40 | 41 | # Create lagged temperature variables 42 | sa <- maketemps(sa,2,48) 43 | 44 | sa.model <- demand_model(sa, sa.econ, formula.hh, formula.a) 45 | 46 | summary(sa.model$a) 47 | summary(sa.model$hh[[33]]) 48 | 49 | # Simulate future normalized half-hourly data 50 | simdemand <- simulate_ddemand(sa.model, sa, simyears=10) 51 | 52 | # Seasonal economic and weather forecast, to be given by user 53 | afcast <- data.frame(pop=1694, gsp=22573, resiprice=34.65, ddays=642) 54 | 55 | # Simulate half-hourly data 56 | demand <- simulate_demand(simdemand, afcast) 57 | 58 | # Illustrate the results 59 | plot(density(demand$annmax, bw="SJ"), 60 | main="Density of seasonal maximum demand", xlab="Demand") 61 | 62 | } 63 | \keyword{package} 64 | 65 | 66 | -------------------------------------------------------------------------------- /man/demand_model.Rd: -------------------------------------------------------------------------------- 1 | \name{demand_model} 2 | \alias{demand_model} 3 | %\alias{simulate_ddemand} 4 | %\alias{simulate_demand} 5 | \title{ 6 | Estimate the electricity demand models 7 | } 8 | \description{ 9 | Estimate the half-hourly/hourly and seasonal demand models. 10 | } 11 | \usage{ 12 | demand_model(hhdata, adata, hhoptformula, aoptformula) 13 | } 14 | \arguments{ 15 | \item{hhdata}{ 16 | The historical half-hourly/hourly demand, temperature and seasonality data 17 | } 18 | \item{adata}{ 19 | The historical seasonal (annual, summer, winter or quarterly) demographic and economic data 20 | } 21 | \item{hhoptformula}{ 22 | The formula for each half-hourly/hourly demand model 23 | } 24 | \item{aoptformula}{ 25 | The formula for seasonal demand model 26 | } 27 | } 28 | \details{ 29 | Estimate the demand model using the historical data, use additive model for half-hourly/hourly demand and linear model for seasonal demand, log demand is used for half-hourly/hourly model. 30 | } 31 | \value{ 32 | \item{hh}{half-hourly/hourly demand models} 33 | \item{hhfits}{fitted values of half-hourly/hourly models} 34 | \item{hhres}{half-hourly/hourly model residuals} 35 | \item{a}{seasonal model} 36 | \item{afits}{fitted values of seasonal model} 37 | \item{fits}{fitted values of the entire model} 38 | \item{res}{entire model residuals} 39 | } 40 | \references{ 41 | R. J. Hyndman and S. Fan (2010) "Density Forecasting for Long-term Peak Electricity Demand", IEEE Trans. Power Systems, 25(2), 1142--1153. 42 | } 43 | \author{ 44 | Rob J Hyndman and Shu Fan 45 | } 46 | \seealso{ 47 | \code{\link{simulate_ddemand}}, 48 | \code{\link{simulate_demand}}, 49 | \code{\link{sa}}, 50 | \code{\link{sa.econ}} 51 | } 52 | \examples{ 53 | # formula for half-hourly model, to be given by the user 54 | formula.hh <- list() 55 | for(i in 1:48) 56 | formula.hh[[i]] = as.formula(log(ddemand) ~ ns(temp, df=2) + day 57 | + holiday + ns(timeofyear, 9) + ns(avetemp, 3) + ns(dtemp, 3) + ns(lastmin, 3) 58 | + ns(prevtemp1, df=2) + ns(prevtemp2, df=2) 59 | + ns(prevtemp3, df=2) + ns(prevtemp4, df=2) 60 | + ns(day1temp, df=2) + ns(day2temp, df=2) 61 | + ns(day3temp, df=2) + ns(prevdtemp1, 3) + ns(prevdtemp2, 3) 62 | + ns(prevdtemp3, 3) + ns(day1dtemp, 3)) 63 | 64 | # formula for annual model, to be given by the user 65 | formula.a <- as.formula(anndemand ~ gsp + ddays + resiprice) 66 | 67 | # create lagged temperature variables 68 | sa <- maketemps(sa,2,48) 69 | 70 | sa.model <- demand_model(sa, sa.econ, formula.hh, formula.a) 71 | 72 | summary(sa.model$a) 73 | summary(sa.model$hh[[33]]) 74 | } 75 | 76 | \keyword{Electricity demand model} 77 | \keyword{Demand model} 78 | 79 | -------------------------------------------------------------------------------- /man/sa.Rd: -------------------------------------------------------------------------------- 1 | \name{sa} 2 | \alias{sa} 3 | \docType{data} 4 | \title{ 5 | Historical data for model estimation 6 | } 7 | \description{ 8 | Historical data of South Australia 9 | } 10 | \format{ 11 | A data frame with 124848 half-hourly observations on the following 19 variables. 12 | \describe{ 13 | \item{\code{demand}}{a numeric vector containing half-hourly electricity demand for South Australia.} 14 | \item{\code{offset}}{a numeric vector containing half-hourly demand from some industrial customers who are not temperature sensitive (e.g., mines and smelters).} 15 | \item{\code{timeofday}}{a numeric vector giving the time of day (0-47).} 16 | \item{\code{date}}{a numeric vector giving the date within the month (1-31).} 17 | \item{\code{month}}{a numeric vector giving the month (1-12).} 18 | \item{\code{year}}{a numeric vector giving the year (2000-2014).} 19 | \item{\code{day}}{a factor with levels \code{Mon} \code{Tue} \code{Wed} \code{Thu} \code{Fri} \code{Sat} \code{Sun}} 20 | \item{\code{idate}}{a numeric vector giving the date in days since 1 January 1900.} 21 | \item{\code{holiday}}{a factor with levels \code{Normal} \code{Day before} \code{Holiday} \code{Day after}.} 22 | \item{\code{workday}}{a character vector with values NWD (Non-WorkDay) and WD (WorkDay).} 23 | \item{\code{timeofyear}}{a numerical time series giving the time in days since midnight on 1 January of each year.} 24 | \item{\code{Year}}{a numeric time series giving the time in years.} 25 | \item{\code{fyear}}{a numeric vector giving the financial year (starting 1 July).} 26 | \item{\code{temp1}}{a numeric vector giving the temperature in Celsius at location 1} 27 | \item{\code{temp2}}{a numeric vector giving the temperature in Celsius at location 2.} 28 | \item{\code{anndemand}}{a numeric vector giving the total demand in each year.} 29 | \item{\code{annoffset}}{a numeric vector giving the total offset demand in each year.} 30 | \item{\code{ddemand}}{a numeric vector giving the normalized demand (demand/anndemand).} 31 | \item{\code{doffset}}{a numeric vector giving the normalized offset (offset/annoffset).} 32 | } 33 | } 34 | \details{ 35 | Historical data for South Australia, including half-hourly demand, temperatures from 2 locations, weekday, weekend, and holiday dates. 36 | Only data from October-March were retained for summer analysis and modelling. 37 | } 38 | \source{ 39 | \url{http://www.aemo.com.au/Electricity/Data/Price-and-Demand/Aggregated-Price-and-Demand-Data-Files} 40 | } 41 | \references{ 42 | R. J. Hyndman and S. Fan (2010) "Density Forecasting for Long-term Peak Electricity Demand", IEEE Trans. Power Systems, 25(2), 1142--1153. 43 | } 44 | \examples{ 45 | plot(ts(sa[,"demand"],freq=48*seasondays,start=c(2000,7))) 46 | } 47 | \keyword{datasets} 48 | -------------------------------------------------------------------------------- /R/maketemps.R: -------------------------------------------------------------------------------- 1 | maketemps <- 2 | function(x,temp_sites,periods=48) 3 | { 4 | colnames.x <- colnames(x) 5 | n = nrow(x) 6 | 7 | # single temperature site 8 | if (temp_sites == 1) 9 | { 10 | x <- as.data.frame(cbind(x,matrix(NA,nrow=nrow(x),ncol=16))) 11 | colnames(x) <- c(colnames.x,"temp", 12 | "prevtemp1","prevtemp2","prevtemp3","prevtemp4","prevtemp5","prevtemp6", 13 | "day1temp","day2temp","day3temp","day4temp","day5temp","day6temp", 14 | "lastmax","lastmin","avetemp") 15 | x$temp <- x$temp1 16 | } 17 | 18 | # standard 2 temperature sites 19 | if (temp_sites == 2) 20 | { 21 | x <- as.data.frame(cbind(x,matrix(NA,nrow=nrow(x),ncol=29))) 22 | colnames(x) <- c(colnames.x,"temp","dtemp", 23 | "prevtemp1","prevtemp2","prevtemp3","prevtemp4","prevtemp5","prevtemp6", 24 | "day1temp","day2temp","day3temp","day4temp","day5temp","day6temp", 25 | "prevdtemp1","prevdtemp2","prevdtemp3","prevdtemp4","prevdtemp5","prevdtemp6", 26 | "day1dtemp","day2dtemp","day3dtemp","day4dtemp","day5dtemp","day6dtemp", 27 | "lastmax","lastmin","avetemp") 28 | 29 | # Create ave temp and difference temp 30 | x$temp <- 0.5*(x$temp1+x$temp2) 31 | x$dtemp <- x$temp2 - x$temp1 32 | 33 | x$prevdtemp1 <- c(rep(NA,1),x$dtemp[1:(n-1)]) 34 | x$prevdtemp2 <- c(rep(NA,2),x$dtemp[1:(n-2)]) 35 | x$prevdtemp3 <- c(rep(NA,3),x$dtemp[1:(n-3)]) 36 | x$prevdtemp4 <- c(rep(NA,4),x$dtemp[1:(n-4)]) 37 | x$prevdtemp5 <- c(rep(NA,5),x$dtemp[1:(n-5)]) 38 | x$prevdtemp6 <- c(rep(NA,6),x$dtemp[1:(n-6)]) 39 | x$day1dtemp <- c(rep(NA,periods),x$dtemp[1:(n-periods)]) 40 | x$day2dtemp <- c(rep(NA,2*periods),x$dtemp[1:(n-2*periods)]) 41 | x$day3dtemp <- c(rep(NA,3*periods),x$dtemp[1:(n-3*periods)]) 42 | x$day4dtemp <- c(rep(NA,4*periods),x$dtemp[1:(n-4*periods)]) 43 | x$day5dtemp <- c(rep(NA,5*periods),x$dtemp[1:(n-5*periods)]) 44 | x$day6dtemp <- c(rep(NA,6*periods),x$dtemp[1:(n-6*periods)]) 45 | } 46 | 47 | # Create lagged versions of temp variables 48 | x$prevtemp1 <- c(rep(NA,1),x$temp[1:(n-1)]) 49 | x$prevtemp2 <- c(rep(NA,2),x$temp[1:(n-2)]) 50 | x$prevtemp3 <- c(rep(NA,3),x$temp[1:(n-3)]) 51 | x$prevtemp4 <- c(rep(NA,4),x$temp[1:(n-4)]) 52 | x$prevtemp5 <- c(rep(NA,5),x$temp[1:(n-5)]) 53 | x$prevtemp6 <- c(rep(NA,6),x$temp[1:(n-6)]) 54 | x$day1temp <- c(rep(NA,periods),x$temp[1:(n-periods)]) 55 | x$day2temp <- c(rep(NA,2*periods),x$temp[1:(n-2*periods)]) 56 | x$day3temp <- c(rep(NA,3*periods),x$temp[1:(n-3*periods)]) 57 | x$day4temp <- c(rep(NA,4*periods),x$temp[1:(n-4*periods)]) 58 | x$day5temp <- c(rep(NA,5*periods),x$temp[1:(n-5*periods)]) 59 | x$day6temp <- c(rep(NA,6*periods),x$temp[1:(n-6*periods)]) 60 | 61 | # x$yesterdaymax <- rep(c(NA,daily(x$temp,max)),each=periods)[1:n] 62 | x$lastmax <- runmax(na.interp(x$temp),periods) 63 | x$lastmin <- runmin(na.interp(x$temp),periods) 64 | x$avetemp <- runmean(na.interp(x$temp),periods) 65 | 66 | return(x) 67 | } 68 | -------------------------------------------------------------------------------- /R/temp_bootstrap.R: -------------------------------------------------------------------------------- 1 | # Variable length double seasonal bootstrap 2 | temp_bootstrap <- 3 | function(x,m,delta=5,periods=48) 4 | { 5 | x <- as.matrix(x) 6 | n <- nrow(x) 7 | nyear <- seasondays*periods # Number of observations per year 8 | years <- round(n/nyear + 0.499) # Total number of years for which there is some data 9 | index <- 1:n 10 | period <- rep(1:years,each=nyear)[1:n] 11 | 12 | # Arrange index in a matrix with one year per row 13 | # Matrix has more columns than necessary 14 | pad <- rep(NA,seasondays*periods*years - n) 15 | imat <- matrix(c(index,pad),ncol=seasondays*periods,nrow=years,byrow=TRUE) 16 | imat <- cbind(imat,imat) 17 | imat[,seasondays*periods+(1:seasondays*periods)] <- imat[c(2:years,years),seasondays*periods+(1:seasondays*periods)] 18 | imat <- imat[,1:((seasondays+50)*periods)] 19 | 20 | nbperyear <- round(seasondays/(m-delta) + 0.499) # Maximum number of blocks per year 21 | # generate random series of block days 22 | # minimum days per block: m-delta; maximum: m+delta 23 | if(delta==0) 24 | bdaysrange <- rep(m,nbperyear) 25 | else 26 | bdaysrange <- sample((m-delta):(m+delta),nbperyear,replace=TRUE) 27 | 28 | # find the combination of block days series with sum equal to seasondays 29 | bsum <- cumsum(bdaysrange) 30 | nbperyear <- length(bsum[bsum < seasondays]) + 1 # Number of blocks per year 31 | bdaysrange[nbperyear] <- seasondays-bsum[nbperyear-1] # last block (1~max) 32 | blengthseries <- periods * bdaysrange[1:nbperyear] # block length series 33 | 34 | # generate the start index for each block 35 | blockstartbasic <- cumsum(c(1,blengthseries))[1:nbperyear] 36 | blockstart <- rep(blockstartbasic,years) 37 | if(delta>0) 38 | { 39 | for (i in 1:years) 40 | { 41 | startadj <- periods*sample((-delta):delta,nbperyear,replace=TRUE) # using delta as adjustment range, can be changed later 42 | # first adjustment positive, last negative 43 | startadj[1] <- abs(startadj[1]) 44 | startadj[nbperyear] = - abs(startadj[nbperyear]) 45 | blockstart[(i-1)*nbperyear + (1:nbperyear)] <- blockstart[(i-1)*nbperyear + (1:nbperyear)] + startadj 46 | } 47 | blockstart <- pmax(blockstart,1) 48 | } 49 | 50 | # Generate random sample of years for each block 51 | nblocks <- years * nbperyear 52 | syear <- sample(1:years,nblocks,replace=TRUE) # Random year for each block. 53 | 54 | # Fix years with partial observations 55 | lastobs <- (n/seasondays/periods - trunc(n/seasondays/periods)) * periods * seasondays - (m+delta*2) * periods 56 | j <- (syear==years & blockstart>lastobs) 57 | syear[j] <- sample(1:(years-1),sum(j),replace=TRUE) # avoid to sample data from the blank part of the last year 58 | 59 | # Sample from each block 60 | newindex <- numeric(seasondays*periods*years) 61 | for(i in 1:years) 62 | for (j in 1:nbperyear) 63 | newindex[seasondays*periods*(i-1)+blockstartbasic[j]+(1:blengthseries[j])-1] <- imat[syear[(i-1)*nbperyear+j],blockstart[(i-1)*nbperyear+j]+(1:blengthseries[j])-1] 64 | 65 | # Remove missing observations (from partial blocks) 66 | newindex <- newindex[!is.na(newindex)] 67 | if(length(newindex) >= n) #n <- nrow(x) 68 | newindex <- newindex[1:n] 69 | else 70 | { 71 | warning("Insufficient data generated") 72 | n <- length(newindex) 73 | } 74 | 75 | # Create new x matrix 76 | # Need to modify this so the noise is added to variable blocks instead of fixed blocks. 77 | newx <- as.matrix(x[newindex,]) 78 | bmax <- blockstat(newx[,1],m,fill=FALSE) 79 | noise <- 0.4*pmax(bmax-42,0)*rnorm(length(bmax),0,1) + rnorm(length(bmax),0,0.2) 80 | #noise <- matrix(rep(noise,rep(m*periods,length(noise)))[1:n],nrow=n,ncol=ncol(newx)) 81 | noise <- matrix(rep(rep(noise,rep(m*periods,length(noise))), trunc(n/10000))[1:n],nrow=n,ncol=ncol(newx)) 82 | 83 | newx <- newx + noise 84 | 85 | # Return final simulated data 86 | return(list(newx=newx,newindex=newindex)) # output the simulation index for use in the PV simulation 87 | } 88 | --------------------------------------------------------------------------------