Mapping orientation of coral reef fish - janeshdev/ggplot2 GitHub Wiki

We observed the swimming direction of coral reef fish larvae (i.e. baby fish) around an island in the Great Barrier Reef, Australia. The short term fate of those larvae in the ocean is to find a reef or to die, so there is a strong incentive towards swimming in the good direction! Larvae were observed in a drifting instrument and their orientation was recorded on video. Furthermore, in this instrument, we either put one fish at a time, or a group (a school) of fishes. The goals of the study are to detect whether fishes orient in any particular way, whether groups orient differently from individuals, and what could be the reason behind their behaviour.

The analysis of such data involves circular statistics: we are interested in the angle of orientation (whether larvae swim towards the North, South, East etc.). R has the package circular for that. Simple tests, such as whether the angles of orientation are distributed randomly or around a particular bearing, are quite trivial to interpret. They basically relate to a comparison of means. However, the exploration of the reason behind the orientation requires to put the data back into its spatial and temporal context. And this is where ggplot came into play.

First I wanted to map the observations to check whether larvae oriented with respect to the direction of the nearest reef. In addition, the observation instrument drifts, hence giving information about the currents in the area and orientation might also be related to currents. So on the same plot I wanted:

1. a representation of the island and the reefs
2. arrows that depict the direction of drift in the ocean
3. arrows that show the orientation of larvae

ggplot made it easy to overlay those elements. The data is saved in a .Rdata file attached to this page and the code is:

library("ggplot2")

load("ggplot-case-study.Rdata")


head(s)
# biological data, has columns
#	deployNb	id for the current observation
#	species		Chromis atripectoralis, a damselfish
#	date/timeIn	local date and time of the start of the observation
#	nbFish		whether we observed one fish or a group (school) of fishes
#	mean		mean angle of orientation, in cardinal reference 
#			(from the N, clockwise)
#	latIn/lonIn	starting point for the observation
#	latOut/lonOut	ending point for the observation
#			after drifting for 20 minutes
#	latMean/lonMean middle point of the drifting trajectory

head(liz)
# coordinates of the polygons mapping the island and reefs, has columns
#	lon/lat		longitude and latitude of polygon nodes
#	kind		whether the polygon is a reef or and island polygon
# several polygons are present in the file, separated by a line of NAs

# To construct the plot, I like to build each part independently and finally
# add them together to an empty plot container (a ggplot() call). 
# It creates some redundancy in each layer and demandes more typing but it
# gives greater flexibility and each part is then reusable for other plots.


# 1- Plot the island and reefs
# plot polygons of different colors depending whether they are reef of hard land
lizPoly = geom_polygon(mapping=aes(x=lon, y=lat, fill=kind), data=liz)
# give them nice colors
lizColors = scale_fill_manual(values = c("#aad0ff","#869471"))
pLiz = list(lizPoly, lizColors)


# 2- Plot drift of the observation instrument
# plot segments given start and stop coordinates
# make them slightly transparent so that they are less conspicuous and
# add arrows to indicate drift direction
pDrift = geom_segment(aes(x=lonIn, y=latIn, xend=lonOut, yend=latOut), 
         data=s, colour="#ff1a10", alpha=0.5,
         arrow=arrow(length=unit(0.01,"native"), angle=30))


# 3- Plot arrows giving the direction of orientation
# convert angle to radians in trigonometric reference
library("circular")
s$angle = circular(s$mean, units="degrees", template="geographic")
s$angle = conversion.circular(s$angle, units="radians", 
          rotation="counter", zero=0, modulo="2pi")

# compute segments along those directions
length=0.0015
s$lonEnd = s$lonMean + cos(s$angle) * length
s$latEnd = s$latMean + sin(s$angle) * length
# NB: a geom_field is in development in ggextra which would alleviate the
# need to compute that manually. The new geom takes length and angle as
# aesthetics and creates appropriate legends.
# Any help on finalizing it is welcome

# plot those segments
pOrient = geom_segment(aes(x=lonMean, y=latMean, xend=lonEnd, yend=latEnd),
          data=s, alpha=0.8, arrow=arrow(length=unit(0.015,"native"), angle=15))


## Sum the layers to create the plot
# In addition:
# - force equal coordinates to give the right aspect to the map
# - facet the plot by the number of fishes, to compare individuals and schools
# - zoom on the side of the island we are interested in
ggplot(s) + pLiz + pDrift + pOrient + 
	coord_equal() + 
	facet_grid(.~nbFish) + 
	coord_cartesian(xlim=c(145.47,145.495), ylim=c(-14.69,-14.665))
# NB: the facet_grid call requires the facetting variable to be globally
# available. That is why 's' has to be present in the ggplot call. Otherwise,
# the data is specified in each layer so I could have used and empty 
# ggplot call: ggplot() 

The attachment section below has a high resolution PNG and a PDF file of the plot.

The curvature of the drift trajectories (red arrows) at the N-E in the case of individuals reveals the existence of eddies in this region. The orientation arrows (black ones) suggest that larvae do not orient in any consistent manner in response to the eddy or in relation to the island.

Schools however, seem to be more consistently oriented Southward. A test reveals that this tendency is actually significant. The plot allows to relate this orientation and it appears that current is flowing mostly Northward. This is again significant: larvae tend to orient against the main flow, even though they drift with it.

The biological interpretation of this is twofold:

1. orienting against the main flow allows to stay roughly in place, which is sensible for these baby fish because they need to wait for the night to reach the reef (too many predators are lurking during the day)
2. schools seem to orient better than individual, which is basically because bad orientation of some members of the school is compensated by good orientation of others.

After these spatial observations, I focused on one possible explanation of the orientation behaviour, which was suggested by previous studies: larval fish might orient with respect to the sun!

As a first step (and this will be the only step presented here) I tried to relate the direction of orientation to the time of the day, which is correlated with the azimuth of the sun in the sky. To do that I plotted the mean angle and coloured it according to the time of the day. The polar coordinates of ggplot came handy to represent those angles:

library("ggplot2")

load("ggplot-case-study.Rdata")

# compute time of the day (hour)
s$hour = as.numeric(substr(s$timeIn,1,2)) + as.numeric(substr(s$timeIn,4,5))/60

# create a fake y column to visually separate schools from individuals while
# still keeping them on the same plot. The main purpose of this plot is not
# to compare schools to individuals so it does not need to oppose them as
# strongly as the preceding one.
s$y = ifelse(s$nbFish=="school",0.9,1.1)

# plot angles of orientation, coloured depending on time of the day
ggplot() + geom_point(aes(x=mean, y=y, colour=hour, shape=nbFish), data=s,
                      size=3, alpha=0.7) +
         scale_colour_gradient("Hour of day") +
         scale_shape("Number of fishes") +
         coord_polar() +
         scale_x_continuous("", limits=c(0,360),
                      breaks=seq(0,360-1,by=45),
                      labels=c("N","N-E","E","S-E","S","S-W","W","N-W")) +
         scale_y_continuous("",limits=c(0,1.2), breaks=1)
# add custom, geographical labels for the angle
# remove most the grid on Y. Y has no real meaning here

This plots also highlights the South-western direction of schools (triangles), but in a way that is more difficult to relate to reality than the first one.

The fact that colors are mostly mixed everywhere suggests that there is no evident relation with the time of the day. And indeed, a circular regression of orientation angles against either time of day or the computed azimuth of the sun is not significant.

So it would seem those baby fish were in fact not using the sun to orient. What they actually use is still a mystery…

— Jean-Olivier Irisson

Rosenstiel School of Marine and Atmospheric Sciences, University of Miami, Florida, USA.

⚠️ **GitHub.com Fallback** ⚠️