Introduction to Extreme Value Analysis in R – Part 4: Dealing with trends

Hey there,

welcome to part 4 of our short introduction to extreme value analysis using the extRemes package in R.

In the recent posts about the block maxima method and the threshold excess method, we have simply assumed that all assumptions for extreme value analysis are fulfilled. However, this is most likely not the case when dealing with environmental variables. Especially the assumption of stationarity may be violated in many cases. Against the background of global climate change, it is likely that there is a a considerable trend in the time series of meteorological or other environmental variables. Of course, this trend has to be incorporated into the analysis, as the resulting return levels change over time.

The following code shows a short practical example of fitting a generalized pareto distribution to a time series of precipitation data using the extRemes package in R. The sample data set features precipitation data of Bärnkopf (Lower Austria) from 1971 through 2013 and is provided by the hydrographic services of Austria via via eHYD.

# load packages

# get data from eHYD
link <- getURL("")
df <- read.csv(text = link, sep=";", dec=",", skip=23, header=FALSE)
df[df == "Lücke"] = NA
colnames(df) <- c("Date","Precip")
df$Date <- as.Date(df$Date, format = "%d.%m.%Y")
df$Precip <- as.numeric(gsub(",", ".", as.character(df$Precip)))

# derive AMS for maximum temperature
ams <- data.frame()
ams <-"rbind", by(df, format(df$Date, "%Y"),
                           function(x) x[which.max(x$Precip), ]))

# check stationarity within AMS
MannKendall(ams$Precip) # Mann-Kendall trend test

summary(lm(Precip~Date, data=ams))

p <- ggplot(ams, aes(x = Date, y = Precip)) + geom_point() + geom_line()
p + stat_smooth(method = "lm", formula = y ~ x, size = 1)


Both the results of the fitted linear model and the graphical impression given by the plot indicate an upward trend of annual maximum precipitation amounts. The Mann-Kendall trend test returns a very small p-value, confirming this trend. Hence, trend correction has to be applied in order to account for changing return levels over time.

# maximum likelihood estimation
mle.trend <- fevd(ams$Precip, ams,, method = "MLE", type="GEV")
rltrend <- return.level(mle.trend, conf = 0.05, return.period= c(2,5,10,20,50,100))

# return level plot
plot(mle.trend, type="rl", ylim=c(0,200),
     main="Return Level Plot for Bärnkopf w/ MLE")

Compared to the return level plots of the previous posts (without trend), this return level plot looks different. It displays the change of the 5 and 100 year return level over time.

Reference: Coles, Stuart (2001). An Introduction to Statistical Modeling of Extreme Values. Springer Series in Statistics. London: Springer.

About This Author

Matthias studied Environmental and Bio-Resources Management with a specialization in Environmental Information Management at the University of Natural Resources and Life Sciences (Vienna). He is currently a PhD student working at the Austrian Institute of Technology. Having written his master's thesis about extreme weather risk identification for the Austrian road network, he currently focuses on modeling of adverse weather events as a basis for risk assessment of road infrastructure networks.


You can post comments in this post.

  • Very nice and very helpful!

    How did you get the return level x-axis to be labeled “Year”? Even when I run your code I get “index.”
    Thanks in advance!

    Mike 1 year ago Reply

    • Try adding xlab="Year" inside the plot function.

      Martin 1 year ago Reply

      • That makes sense, but something in fevd doesn’t like that.

        plot(mle.trend, type="rl", ylim=c(0,200),
        +      main="Return Level Plot for Bärnkopf w/ MLE",xlab="Year")
        Error in plot.default(y, type = "l", xlab = "index", ylab = ylb, ...) : 
          formal argument "xlab" matched by multiple actual arguments

        Again your complete examples (data, code and graphics) are super helpful for beginners like me.

        Mike 1 year ago Reply

        • As a matter of fact, I am using customized plotting functions for fevd objects. In this particular case, it’s not that easy to modify the plots, as the fevd function is somehow bloated, and so are its plotting functions. So, even though the code for the plots is quite simple, you have to find the correct position where you have to make your modifications. In your code, the xlab-definition gets passed on to a plot()-function where the argument xlab is already defined as xlab = "index". If you want to customize your plot, you have to modify the function plot.fevd.mle. Around line 550 you will find the actual plot function that gets called in this specific case. You can change type = "b", xlab = "Year", pch = 16 to get a similar plot to the one I used in this post. In addition, you can dig into this plotting function if you want to change colors, return levels or plot multiple fitting methods in one plot (e.g. comparison of MLE and L-moments in one plot).

          Matthias 1 year ago Reply

Post A Reply