sábado, 9 de diciembre de 2017

Brownian Motion GIF with R and ImageMagick



Hi there!

Last Monday we celebrated a “Scientific Marathon” at Royal Botanic Garden in Madrid, a kind of mini-conference to talk about our research. I was talking about the relation between fungal spore size and environmental variables such as temperature and precipitation. To make my presentation more friendly, I created a GIF to explain the Brownian Motion model. In evolutionary biology, we can use this model to simulate the random variation of a continuous trait through time. Under this model, we can notice how closer species tend to maintain closer trait values due to shared evolutionary history. You have a lot of information about Brownian Motion models in evolutionary biology everywhere!
Here I will show you how I built a GIF to explain Brownian Motion in my talk using R and ImageMagick.


 # First, we simulate continuous trait evolution by adding in each iteration  
 # a random number from a normal distribution with mean equal to 0 and standard  
 # deviation equal to 1. We simulate a total of 4 processes, to obtain at first  
 # two species and a specieation event at the middle of the simulation, obtaining  
 # a total of 3 species at the end.  
 df1<- data.frame(0,0)  
 names(df1)<- c("Y","X")  
 y<-0  
 for (g in 1:750){  
 df1[g,2] <- g  
 df1[g,1] <- y  
 y <- y + rnorm(1,0,1)  
 }  
 #plot(df1$X,df1$Y, ylim=c(-100,100), xlim=c(0,1500), cex=0)  
 #lines(df1$X,df1$Y, col="red")  
 df2<- data.frame(0,0)  
 names(df2)<- c("Y","X")  
 y<-0  
 for (g in 1:1500){  
  df2[g,2] <- g  
  df2[g,1] <- y  
  y <- y + rnorm(1,0,1)  
 }  
 #lines(df2$X,df2$Y, col="blue")  
 df3<- data.frame(750,df1[750,1])  
 names(df3)<- c("Y","X")  
 y<-df1[750,1]  
 for (g in 750:1500){  
  df3[g-749,2] <- g  
  df3[g-749,1] <- y  
  y <- y + rnorm(1,0,1)  
 }  
 #lines(df3$X,df3$Y, col="green")  
 df4<- data.frame(750,df1[750,1])  
 names(df4)<- c("Y","X")  
 y<-df1[750,1]  
 for (g in 750:1500){  
  df4[g-749,2] <- g  
  df4[g-749,1] <- y  
  y <- y + rnorm(1,0,1)  
 }  
 #lines(df4$X,df4$Y, col="orange")  



 # Now, we have to plot each simmulation lapse and store them in our computer.  
 # I added some code to make lighter the gif (plotting just odd generations) and   
 # to add a label at the speciation time. Note that, since Brownan Model is a   
 # stocasthic process, my simulation will be different from yours.  
 # You should adjust labels or repeat the simulation process if you don't   
 # like the shape of your plot.  
 parp<-rep(0:1, times=7, each= 15)  
 parp<- c(parp, rep(0, 600))  
 for (q in 1:750){  
  if ( q %% 2 == 1) {  
  id <- sprintf("%04d", q+749)  
  png(paste("bm",id,".png", sep=""), width=900, height=570, units="px",   
    pointsize=18)  
  par(omd = c(.05, 1, .05, 1))  
  plot(df1$X,df1$Y, ylim=c(-70,70), xlim=c(0,1500), cex=0,   
     main=paste("Brownian motion model \n generation=", 749 + q) ,   
     xlab="generations", ylab="trait value", font.lab=2, cex.lab=1.5 )  
 lines(df1$X,df1$Y, col="red", lwd=4)  
 lines(df2$X[1:(q+749)],df2$Y[1:(q+749)], col="blue", lwd=4)  
 lines(df3$X[1:q],df3$Y[1:q], col="green", lwd=4)  
 lines(df4$X[1:q],df4$Y[1:q], col="orange", lwd=4)  
 if (parp[q]==0)  
 text(750, 65,labels="speciation event", cex= 1.5, col="black", font=2)  
 if (parp[q]==0)  
 arrows(750, 60, 750, 35, length = 0.20, angle = 30, lwd = 3)  
 dev.off()  
 }  
 }  

 Now, you just have to use ImageMagick to put all the PNG files together in a GIF using a command like this in a terminal:


 convert -delay 10 *.png bm.gif  

Et voilà!



martes, 5 de diciembre de 2017

Computing wind average in an area using rWind


Hi all!

A researcher asked me last week about how to compute wind average for an area using rWind. I wrote a simple function to do this using dplyr library (following the advice of my friend Javier Fajardo). The function will be added to rWind package as soon as possible. Meanwhile, you can test the results... enjoy!



 # First, charge the new function  
 library(dplyr)  
 wind.region <- function (X){   
  X[,3] <- X[,3] %% 360   
  X[X[,3]>=180,3] <- X[X[,3]>=180,3] - 360   
  avg<-summarise_all(X[,-1], .funs = mean)   
  wind_region <- cbind(X[1,1],avg)   
  return(wind_region)   
 }   

Once you have charged the function, let’s do an example...

 # Get some wind data and convert it into a raster to be plotted later  
 library(rWind)  
 library(raster)  
 wind_data<-wind.dl(2015,2,12,0,-10,5,35,45)  
 wind_fitted_data <- wind.fit(wind_data)  
 r_speed <- wind2raster(wind_fitted_data, type="speed")  

Now, you can use the new function to obtain wind average in the study area:


 myMean <- wind.region(wind_data)  
 myMean  

 # Now, you can use wind.fit to get wind speed and direction.  
 myMean_fitted <- wind.fit(myMean)  
 myMean_fitted  

 # Finally, let's plot the results!  
 library(rworldmap)  
 library(shape)  
 plot(r_speed)  
 lines(getMap(resolution = "low"), lwd=4)   
 alpha <- arrowDir(myMean_fitted)  
 Arrowhead(myMean_fitted$lon, myMean_fitted$lat, angle=alpha,   
      arr.length = 2, arr.type="curved")  
 text(myMean_fitted$lon+1, myMean_fitted$lat+1,   
    paste(round(myMean_fitted$speed,2), "m/s"), cex = 2)