How to reproduce the MaxEnt Response Curves with R

“It always seems impossible until it’s done.”

—Nelson Mandela

Introduction

The default MaxEnt outputs are generally not suite for publications. However, there is no way to customize these graphics. Fortunately, the software provides raw data that can be used to reproduce the graphics using personalized features.The present tutorial presents the codes used to produce the Figure 4 in Agunbiade, M. B., Madow, J., Camara, F., Fotang, C., Tangwa, E., Tédonzong, L. R. D., Yuh, Y. G., Ogada, D. & Birkhofer, K. (2025). Mapping suitable habitat for Hooded Vultures in one of the last West African strongholds for the species, The Gambia. Ostrich. https://doi.org/10.2989/00306525.2025.2474919.

This method was also used in the following articles!

Fotang, C., Bröring, U., Roos, C. R., …, Tédonzong, L. R. D., Willie, J., Angwafo, T. E., Yuh, Y. G., Schierack, P. & Birkhofer, K. (2023). Mapping suitable habitat for Nigeria–Cameroon chimpanzees in Kom-Wum Forest Reserve, North-Western Cameroon. Primates 64, 339–350. https://doi.org/10.1007/s10329-023-01054-z

Figure 5

Tédonzong, L. R. D., Willie, J., Makengveu, S. T., Lens, L. and Tagg, N. (2020). Variation in behavioral traits of two frugivorous mammals may lead to differential responses to human disturbance. Ecology and Evolution 10, 3798-3813. https://doi.org/10.1002/ece3.6178

Figure 4

Importing packages

Some of these packages were not used in this tutorial.

package_list=c("ade4","adehabitatHR","ggplot2","maptools","readr","reshape2","reshape","spatstat",
               "spatialEco","spatstat.utils","raster","ggpubr","gridExtra","cowplot","ggThemeAssist",
               "ecospat","shapefiles","png","ggthemes")

ipak <- function(pkg){
  new.pkg <- pkg[!(pkg %in% installed.packages()[, "Package"])]
  if (length(new.pkg)) 
    install.packages(new.pkg, dependencies = TRUE, repos = "http://cran.us.r-project.org")
  sapply(pkg, require, character.only = TRUE)
}

ipak(package_list)
##           ade4   adehabitatHR        ggplot2       maptools          readr 
##           TRUE           TRUE           TRUE           TRUE           TRUE 
##       reshape2        reshape       spatstat     spatialEco spatstat.utils 
##           TRUE           TRUE           TRUE           TRUE           TRUE 
##         raster         ggpubr      gridExtra        cowplot  ggThemeAssist 
##           TRUE           TRUE           TRUE           TRUE           TRUE 
##        ecospat     shapefiles            png       ggthemes 
##           TRUE           TRUE           TRUE           TRUE

Loading dataset

The function lapply is used hare to read many .dat files at once and store them in a stack variable that can then be manipulated to create a dataframe containing all data arranged. The output is a list of all tables present in the folder.

Maxent_responses_F2 <- list.files(path="D:/Luc/Articles/Mike/Results/MaxentGambiaCrossValidate_New_Road_Livestock/plots", 
                                  pattern=".dat$", full.names=TRUE)


Maxent_stack = lapply(Maxent_responses_F2, read.csv)

typeof(Maxent_stack)
## [1] "list"

Data wrangling

Here we extract the different tables in the stack variable to build a dataframe. It is important here to verify and note the indexes of the different tables corresponding to each environmental variable. selection is to create a vector to subset the rows of the average file. It comes with 1001 rows while others come with 501 rows. VariableSelection is used to discard the repeated X and variable columns. The for loop is used here to extract the tables 0 to 9 for each variable and cbind them to an empty dataframe with empty columns.At the end, the resulting dataframes for each variable are rbind to create a unique dataframe with all variables.

Var_Num=length(Maxent_responses_F2)/11
Variables_order=seq(1,Var_Num,1)


dim_empty=dim(data.frame(Maxent_stack[[1]]))
mat_empty = matrix(ncol = 0, nrow = dim_empty[1])
df_empty=df=data.frame(mat_empty) 



selection=seq(1,1001, by=2)

VariableSelection=c(4,5,7,8,10,11,13,14,16,17,19,20,22,23,25,26,28,29,31,32)


for (i in Variables_order) {

  assign(paste0("Var_",i),cbind(data.frame(Maxent_stack[i]),data.frame(Maxent_stack[i+15]),
                                data.frame(Maxent_stack[i+15*2]),data.frame(Maxent_stack[i+15*3]),
                                data.frame(Maxent_stack[i+15*4]),data.frame(Maxent_stack[i+15*5]),
                                data.frame(Maxent_stack[i+15*6]),data.frame(Maxent_stack[i+15*7]),
                                data.frame(Maxent_stack[i+15*8]),data.frame(Maxent_stack[i+15*9]),
                                data.frame(Maxent_stack[i+15*10])[selection,])[,-VariableSelection])
}

df_Final=rbind(Var_1,Var_2, Var_3,Var_4, Var_5,Var_6,Var_7,Var_8, Var_9, Var_10,
               Var_11,Var_12,Var_13,Var_14,Var_15)

Preparing the dataframe

Here we finalize the data preparation process, by assigning a variable containing the names of the environmental variables, as they should appear on the plot.

# converting the matrix to data  


R_colNames = c("Variable0","Avg_X","Var_Y1","Var_Y2","Var_Y3","Var_Y4","Var_Y5","Var_Y6","Var_Y7","Var_Y8","Var_Y9","Var_Y10","Avg_Y")
colnames(df_Final)=R_colNames

unique(df_Final$Variable0) # This is to see the order of variables in the dataframe.
##  [1] "bio11"                        "Bio17"                       
##  [3] "Bio18"                        "Bio19"                       
##  [5] "cattle_mask"                  "Elevation"                   
##  [7] "Landuse01 Water"              "Landuse02 Trees"             
##  [9] "landuse04 Flooded vegetation" "Landuse05 Crops"             
## [11] "Landuse07 Built Area"         "Landuse08 Bare ground"       
## [13] "Landuse11 Rangeland"          "road_distance"               
## [15] "Slope"
Var_list_sorted=c("Mean temp. of coldest quarter","Precipitation of driest quarter", 
                  "Precipitation of warmest quarter","Precipitation of coldest quarter","Density of livestock",
                  "Elevation","Density of water","Density of trees","Density of flooded vegetation",
                  "Density of crops","Density of built area","Density of bare ground",
                  "Density of rangeland", "Distance to road","Slope")

Var_list_num=rep(1:Var_Num,1)
Variables11022_1 = rep(Var_list_sorted, each = 501)
Variables11022_2 = rep(Var_list_num, each = 501)
df_Final$Variable = Variables11022_1
df_Final$Variable1 = Variables11022_2

df_Final$Variable2=paste(df_Final$Variable1,"_",df_Final$Variable)
Head1=head(df_Final)
Final dataframe containing data of all variables arranged
Variable0 Avg_X Var_Y1 Var_Y2 Var_Y3 Var_Y4 Var_Y5 Var_Y6 Var_Y7 Var_Y8 Var_Y9 Var_Y10 Avg_Y Variable Variable1 Variable2
1 bio11 23.59500 0.6293595 0.643364 0.6164757 0.691259 0.6749287 0.6899486 0.5099581 0.6654155 0.5956612 0.5612458 0.6277616 Mean temp. of coldest quarter 1 1 _ Mean temp. of coldest quarter
3 bio11 23.59992 0.6293595 0.643364 0.6164757 0.691259 0.6749287 0.6899486 0.5099581 0.6654155 0.5956612 0.5612458 0.6277616 Mean temp. of coldest quarter 1 1 _ Mean temp. of coldest quarter
5 bio11 23.60484 0.6293595 0.643364 0.6164757 0.691259 0.6749287 0.6899486 0.5099581 0.6654155 0.5956612 0.5612458 0.6277616 Mean temp. of coldest quarter 1 1 _ Mean temp. of coldest quarter
7 bio11 23.60976 0.6293595 0.643364 0.6164757 0.691259 0.6749287 0.6899486 0.5099581 0.6654155 0.5956612 0.5612458 0.6277616 Mean temp. of coldest quarter 1 1 _ Mean temp. of coldest quarter
9 bio11 23.61468 0.6293595 0.643364 0.6164757 0.691259 0.6749287 0.6899486 0.5099581 0.6654155 0.5956612 0.5612458 0.6277616 Mean temp. of coldest quarter 1 1 _ Mean temp. of coldest quarter
11 bio11 23.61960 0.6293595 0.643364 0.6164757 0.691259 0.6749287 0.6899486 0.5099581 0.6654155 0.5956612 0.5612458 0.6277616 Mean temp. of coldest quarter 1 1 _ Mean temp. of coldest quarter

Calculating the variables Min_Y and Max_Y

Here we create two functions to calculate the variables Min_Y and Max_Y, that will be binded to the dataframe to build the plot.

Maxent_Var_Resp_All3=df_Final

dimension=dim(Maxent_Var_Resp_All3)
Min_Y=c()

for (i in 1:dimension[1]) {
  Min_Y1=min(Maxent_Var_Resp_All3[i,c(3:12)])
  Min_Y=c(Min_Y,Min_Y1)
}

Max_Y=c()

for (i in 1:dimension[1]) {
  Max_Y1=max(Maxent_Var_Resp_All3[i,c(3:12)])
  Max_Y=c(Max_Y,Max_Y1)
}

Maxent_Var_Resp_All3$Max_Y=Max_Y
Maxent_Var_Resp_All3$Min_Y=Min_Y

Head2=rbind(head(Maxent_Var_Resp_All3),tail(Maxent_Var_Resp_All3))
Final dataframe containing data of all variables arranged with the variables Min_Y and Max_Y
Variable0 Avg_X Var_Y1 Var_Y2 Var_Y3 Var_Y4 Var_Y5 Var_Y6 Var_Y7 Var_Y8 Var_Y9 Var_Y10 Avg_Y Variable Variable1 Variable2 Max_Y Min_Y
1 bio11 23.59500 0.6293595 0.6433640 0.6164757 0.691259 0.6749287 0.6899486 0.5099581 0.6654155 0.5956612 0.5612458 0.6277616 Mean temp. of coldest quarter 1 1 _ Mean temp. of coldest quarter 0.6912590 0.5099581
3 bio11 23.59992 0.6293595 0.6433640 0.6164757 0.691259 0.6749287 0.6899486 0.5099581 0.6654155 0.5956612 0.5612458 0.6277616 Mean temp. of coldest quarter 1 1 _ Mean temp. of coldest quarter 0.6912590 0.5099581
5 bio11 23.60484 0.6293595 0.6433640 0.6164757 0.691259 0.6749287 0.6899486 0.5099581 0.6654155 0.5956612 0.5612458 0.6277616 Mean temp. of coldest quarter 1 1 _ Mean temp. of coldest quarter 0.6912590 0.5099581
7 bio11 23.60976 0.6293595 0.6433640 0.6164757 0.691259 0.6749287 0.6899486 0.5099581 0.6654155 0.5956612 0.5612458 0.6277616 Mean temp. of coldest quarter 1 1 _ Mean temp. of coldest quarter 0.6912590 0.5099581
9 bio11 23.61468 0.6293595 0.6433640 0.6164757 0.691259 0.6749287 0.6899486 0.5099581 0.6654155 0.5956612 0.5612458 0.6277616 Mean temp. of coldest quarter 1 1 _ Mean temp. of coldest quarter 0.6912590 0.5099581
11 bio11 23.61960 0.6293595 0.6433640 0.6164757 0.691259 0.6749287 0.6899486 0.5099581 0.6654155 0.5956612 0.5612458 0.6277616 Mean temp. of coldest quarter 1 1 _ Mean temp. of coldest quarter 0.6912590 0.5099581
99116 Slope 18.51533 0.7838191 0.6991724 0.7075460 0.739804 0.7095181 0.8184615 0.6797429 0.7549191 0.7330689 0.7374325 0.7363485 Slope 15 15 _ Slope 0.8184615 0.6797429
99314 Slope 18.55618 0.7838191 0.6991724 0.7075460 0.739804 0.7095181 0.8184615 0.6797429 0.7549191 0.7330689 0.7374325 0.7363485 Slope 15 15 _ Slope 0.8184615 0.6797429
99514 Slope 18.59702 0.7838191 0.6991724 0.7075460 0.739804 0.7095181 0.8184615 0.6797429 0.7549191 0.7330689 0.7374325 0.7363485 Slope 15 15 _ Slope 0.8184615 0.6797429
99714 Slope 18.63786 0.7838191 0.6991724 0.7075460 0.739804 0.7095181 0.8184615 0.6797429 0.7549191 0.7330689 0.7374325 0.7363485 Slope 15 15 _ Slope 0.8184615 0.6797429
99914 Slope 18.67871 0.7838191 0.6991724 0.7075460 0.739804 0.7095181 0.8184615 0.6797429 0.7549191 0.7330689 0.7374325 0.7363485 Slope 15 15 _ Slope 0.8184615 0.6797429
100114 Slope 18.71955 0.7838191 0.6991724 0.7075460 0.739804 0.7095181 0.8184615 0.6797429 0.7549191 0.7330689 0.7374325 0.7363485 Slope 15 15 _ Slope 0.8184615 0.6797429

Subsetting the dataframe

Here the subset is created to select the variables to be displayed.

Maxent_Var_Resp_All2=Maxent_Var_Resp_All3

Maxent_Var_Resp_All1=subset(Maxent_Var_Resp_All2, Variable1 %in% c(11,3,7,14,6,12))

Creating the plot with ggplot2

In ggplot2, we will use the function ~factor() to define the order in which we want the graph to present the variables.

plot1 = ggplot(data = Maxent_Var_Resp_All1, aes(x=Avg_X, y=Avg_Y)) +
  geom_ribbon(aes(ymin = Min_Y, ymax = Max_Y), alpha = 1.0,
              fill = "#0000FF", color = "transparent")+
  geom_line(linetype = "solid", col = "#DF0020", size = 0.6)+
  facet_wrap( ~factor(Variable, levels=c("Density of built area",
                                          "Precipitation of warmest quarter",
                                          "Density of water","Distance to road",
                                          "Elevation","Density of bare ground")),
              scales="free",ncol = 3)+
  ##facet_grid( ~ Variable)+
  theme_bw() + scale_colour_tableau()+
  theme(axis.text.x = element_text(angle = 0, hjust = 0.5, vjust = 0.5))+
  labs(y ="    ", x = NULL)+ylim(0,1)+
  labs(y = "Logistic output", x = "Variable values")
Response of Hooded Vultures Necrosyrtes monachus to the most important variables for habitat suitability

Response of Hooded Vultures Necrosyrtes monachus to the most important variables for habitat suitability

Exporting the plot

It is possible to export the plot into publication ready format such as pdf or eps, etc. The height and the width or the resolution of the plot can be specified here.

Exporting into pdf format

ggsave("Maxent_logistic_output.pdf",plot1,  
       path = "D:/Luc/Articles/Mike/Results",
       scale = 1, width = 19, height = 12, units = "cm",
       dpi = 600)

Exporting into eps format

ggsave("Maxent_logistic_output.eps",plot1,  
       path = "D:/Luc/Articles/Mike/Results",
       scale = 1, width = 19, height = 12, units = "cm",
       dpi = 600)