#Click this code chunk. IF you get an error and it prompts you to install rmarkdown package, click Install
Code Source & Author: Christopher Beirne, the Wildlife Coexistence Lab, UBC, and the WildCam Network; Adapted by Kelly Klingler
This RMD script runs through the steps for installing the necessary packages and importing the 4 dataframe files. First, we will conduct a standardized data error check before beginning the analysis.
#install packages #lubridate package is critical for dealing with dates and times
#install.packages("rmarkdown") #installs the package
library("rmarkdown") #you must also load it using the library function
#install.packages("lubridate")
library(lubridate)
##
## Attaching package: 'lubridate'
## The following objects are masked from 'package:base':
##
## date, intersect, setdiff, union
#Find your path and import the practice dataset
pro <- read.csv("C:/Users/Joel Bowersox/OneDrive - Paul Smith's College/fws332_wildlife_data/data/example_data/proj.csv", header=T)
img <- read.csv("C:/Users/Joel Bowersox/OneDrive - Paul Smith's College/fws332_wildlife_data/data/example_data/img.csv", header=T)
dep <- read.csv("C:/Users/Joel Bowersox/OneDrive - Paul Smith's College/fws332_wildlife_data/data/example_data/dep.csv", header=T)
cam <- read.csv("C:/Users/Joel Bowersox/OneDrive - Paul Smith's College/fws332_wildlife_data/data/example_data/cam.csv", header=T)
#Your dataframes should appear in the upper right hand environment window.
Q1 Open each of the dataframes and write a note below about what each dataframe contains and what types of data are present (hint: click the blue drop down arrow will give you lots of information about each of the variables!) CAM_dataframe: Project_ID=AlgarRestorationProject Cameras=20 Camera_Model=Reconyx_Hyperfire_2
DEP_dataframe: Place_Name Latitude Longitude Start_Date End_Date
IMG_dataframe Image_Name File_Name Identifier Animal_Scientific_Name
PRO_dataframe Project_Objectives Project_ Admin Data_Citations About the dataset:
#Error Checking The most important part of analyzing camera trap data is checking and exploring your data!
#Step 1 Check Deployment dates Lets convert the date columns from character strings to date objects. We will use the ymd() function to make sure all of the dates are in the year-month-day format
# start dates
dep$start_date <- ymd(dep$start_date)
# end dates
dep$end_date <- ymd(dep$end_date)
Q2 What are those $ doing again? Which data frame is being manipulated here? Allow for extraction of elements by name from a named list. The start and end dates
Now let’s make a new column in the deployment data called days, and calculate the interval for all the deployments:
dep$days <- interval(dep$start_date, dep$end_date)/ddays(1)
We should then check the range of dates the cameras were active for. Things to look out for are:
0’s A value of zero would mean a deployments which started and ended on the same day -> it typically denotes a camera which malfunctioned instantly.
NA’s This is either an end_date which is NA e.g. if the camera was stolen, or it could be a date value which failed to parse e.g. if you had a typo in your date column such as ymd(“202-212-24”) it would return NA
Negative numbers Are more common that you think… someone probably got the start and end dates the wrong way round when entering data.
Lets look at the range of values we have:
summary(dep$days)
## Min. 1st Qu. Median Mean 3rd Qu. Max. NA's
## 15.0 139.0 221.0 182.9 231.0 597.0 1
#Q3 What is the output above saying? Talk it out with a friend and give it your best guess based on what you know about dataframe syntax the least value, first quarter, median value, third quarter,highest value and average value
#Let’s install a package that will allow us to create interactive tables to view the data. This willl help us find out where this NA issue is.
#install.packages("kableExtra")
library("kableExtra")
Create the table!
dep[is.na(dep$days)==T,] %>%
kbl() %>%
kable_styling(full_width = T) %>%
kableExtra::scroll_box(width = "100%")
| project_id | deployment_id | placename | longitude | latitude | start_date | end_date | bait_type | bait_description | feature_type | feature_type_methodology | camera_id | camera_name | quiet_period | camera_functioning | sensor_height | height_other | sensor_orientation | orientation_other | plot_treatment | plot_treatment_description | detection_distance | subproject_name | subproject_design | event_name | event_description | event_type | recorded_by | days | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 3 | AlgarRestorationProject | ALG027_2019-04-03 | ALG027 | -112.4735 | 56.3328 | 2019-04-03 | NA | None | NA | HumanUse | NA | NA | NA | 1 | Camera Functioning | 100 | NA | NA | NA | NA | NA | NA | Restoration | NA | NA | NA | NA | NA | NA |
#This camera was stolen! Located in a human use area. Okay good now we know where that’s coming from and it isn’t a larger data issue.
Q4 Next, we need to make sure our dates are formatted correctly in the other dataframes….which of the 4 dataframes have dates?
We need to convert the img$timestamp column. The ymd lubridate format is required for a date which looks like 2018-04-13 13:51:01. Essentially the code below is taking the timestamp column in the img data frame and converting it from hours-minutes-seconds (hms) to year-month-day (ymd).
img$timestamp <- ymd_hms(img$timestamp)
Let’s do a quick check to see if all the dates parsed correctly. First check the range:
range(img$timestamp)
## [1] "2018-04-08 04:22:13 UTC" "2019-12-16 12:41:43 UTC"
Q5 What years are represented in this practice dataset? 2018&2019
And check for NA’s:
table(is.na(img$timestamp))
##
## FALSE
## 15290
Yay! There are no NAs (remember, this is a logical data type so it’s returning FALSE if no NAs exist across all of our observations. If there were some NAs, we would see a TRUE statement)
Now that our camera trap data are loaded into R, we can very quickly find out summary information about the dataset. These can easily feed directly into the methods section of your report/paper.
First let’s count the number of unique locations:
# Count the number of camera locations
paste(length(unique(dep$placename)), "locations"); paste(length(unique(dep$deployment_id)), "deployments");paste(nrow(img), "image labels"); paste(nrow(img[img$is_blank == TRUE,]), "blanks")
## [1] "38 locations"
## [1] "114 deployments"
## [1] "15290 image labels"
## [1] "2633 blanks"
Notice something about the code above. A # was used but this time it was within the R code chunk. Rather than telling R to create a new heading, this tells R to ignore what comes after it if it’s in an R code chunk. # are a way to make comments or note within the code and tells R to ignore it
#Step 3 Error checks
Lets start with the fundamental error checks required with a new data set:
Camera locations Deployment date checks Image and deployment matching Taxonomy Diel time
Camera locations A common mistake in camera trap data sets is that the locations are not where they are supposed to be. The safest way to check your data is to plot them!
#install.packages("leaflet")
library("leaflet")
m <- leaflet() %>% # call leaflet
addTiles() %>% # add the default basemap
addCircleMarkers( # Add circles for stations
lng=dep$longitude, lat=dep$latitude)
m # return the map
Note - Leaflet is best used using tidyverse ‘pipe’ notation - %>%. It allows you to add successive operations in the order that the elements occur.If the code above does not work, you may need to install tidyverse
#fyi, this might take a couple of minutes
#install.packages("tidyverse")
library("tidyverse")
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr 1.1.4 ✔ readr 2.1.5
## ✔ forcats 1.0.0 ✔ stringr 1.5.1
## ✔ ggplot2 3.5.1 ✔ tibble 3.2.1
## ✔ purrr 1.0.2 ✔ tidyr 1.3.1
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::group_rows() masks kableExtra::group_rows()
## ✖ dplyr::lag() masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
Anyway, back to the map! This is great! We can zoom in using the +/_ in the top left hand corner, and the default basemap is OpenStreetMap. The camera stations all appear to be in the right place (we don’t have any in the Atlantic).
But wouldn’t it be great to see the dep$placename when we click over a symbol? Run the following:
m <- leaflet() %>%
addTiles() %>%
addCircleMarkers(
lng=dep$longitude, lat=dep$latitude,
popup=paste(dep$placename)) # include a popup with the placename!
m
Okay, but a map isn’t really useful until have have some satellite imagery, right? Easy:
m <- leaflet() %>%
addProviderTiles(providers$Esri.WorldImagery) %>% #Add Esri Wrold imagery
addCircleMarkers(
lng=dep$longitude, lat=dep$latitude,
popup=paste(dep$placename)) # include a popup with the placename!
m
Q5 Zoom in - what can you tell me about where the stations are located? Can you spot any differences between the stations? Clue: look for lines on the landscape. Roads and Powerlines
#Making Corrections As you can see, we have one deployment location that is a long way from the others. It almost looks like it belongs to another project?! Let’s take a look at all of the deployments from that location (ALG069):
dep[dep$placename=="ALG069",c("deployment_id", "placename", "longitude", "latitude")]
## deployment_id placename longitude latitude
## 100 ALG069_2018-04-07 ALG069 -113.5075 56.49352
## 101 ALG069_2018-11-14 ALG069 -112.5075 56.49352
## 102 ALG069_2019-04-02 ALG069 -112.5075 56.49352
It looks like there is a typo in one of the coordinates: -112.5075 -> -113.5075. Let’s correct it:
dep$longitude[dep$placename=="ALG069"] <- -112.5075
Let’s create a map that shows camera location by habitat type # viriidis package generates colors for plots
#install.packages("viridis")
library("viridis")
## Loading required package: viridisLite
# First, set a single categorical variable of interest from station covariates for summary graphs. If you do not have an appropriate category use "project_id".
category <- "feature_type"
# We first convert this category to a factor with discrete levels
dep[,category] <- factor(dep[,category])
# then use the turbo() function to assign each level a color
col.cat <- turbo(length(levels(dep[,category])))
# then we apply it to the dataframe
dep$colours <- col.cat[dep[,category]]
m <- leaflet() %>%
addProviderTiles(providers$Esri.WorldImagery, group="Satellite") %>%
addTiles(group="Base") %>% # Include a basemap option too
addCircleMarkers(lng=dep$longitude, lat=dep$latitude,
# Co lour the markers depending on the 'feature type'
color=dep$colours,
# Add a popup of the placename and feature_type together
popup=paste(dep$placename, dep[,category])) %>%
# Add a legend explaining what is going on
addLegend("topleft", colors = col.cat, labels = levels(dep[,category]),
title = category,
labFormat = labelFormat(prefix = "$"),
opacity = 1) %>%
# add a layer control box to toggle between the layers
addLayersControl(
baseGroups = c("Satellite", "Base"))
m
If you click on a point you will see it’s corresponding placename and feature_type - so you can find the problem data. You can also check your treatment categories using the key. If you zoom in, all the “offline” locations should be >100m away from a linear features, the other on top of them.
Q6 Based on the color coded map above, what do you notice about the research design? Did they place cameras randomly or not? Do certain colors or habitat types cluster together?
Next, we will check the distance between camera pairs
Sometimes the coordinates of a camera stations are accidentally repeated in the deployment data, which can actually be very hard to see on a map as the points will overlay perfectly. The way we check this is to calculate the pairwise distance between all of the unique deployments in the project
#install.packages("sf")
library("sf")
## Linking to GEOS 3.12.2, GDAL 3.9.3, PROJ 9.4.1; sf_use_s2() is TRUE
In the following code block we make first use of the simple features (sf) package - tools which make spatial operations which you would normally perform in ArcMap very easy (e.g. plotting and manipulating polygons).
# create a list of all the non-duplicated placenames
camera_locs <- dep %>%
dplyr::select(placename, latitude, longitude) %>%
unique() %>% # remove duplicated rows (rows where the placename and coordinates match)
st_as_sf(coords = c("longitude", "latitude"), crs = "+proj=longlat") # Convert to `sf` format
# Check that there are no duplicated stations
camera_locs[duplicated(camera_locs$placename)==T,]
## Simple feature collection with 0 features and 1 field
## Bounding box: xmin: NA ymin: NA xmax: NA ymax: NA
## Geodetic CRS: +proj=longlat
## [1] placename geometry
## <0 rows> (or 0-length row.names)
f it returns a list of <0 rows>, we don’t have duplicates with different coordinates. Phew!
#install.packages("usedist")
library("usedist")
# distance matrix for all cameras
camera_dist <- st_distance(camera_locs) %>%
as.dist() %>%
usedist::dist_setNames(as.character(camera_locs$placename)) %>%
as.matrix()
#Make temporary camera_dist_mins by converting diagonals/zeros to 999999 so we can avoid the zeros when using which.min function to find nearest cameras
camera_dist_mins <- camera_dist + diag(999999,dim(camera_dist)[1])
#Create new empty dataframe for appending results to
camera_dist_list <- data.frame(focal_cam = character(),nearest_cam = character(), dist = double())
#Cycle through each column of camera_dist_mins
for (i in (1:dim(camera_dist_mins)[1]))
{
#Get index of minimum value of column i
t <- which.min(camera_dist_mins[,i])
#Combine relevant data into new_row
new_row <- data.frame(colnames(camera_dist_mins)[i],names(t),camera_dist_mins[t,i])
#Append the new_row to the accumulated results dataframe
camera_dist_list[nrow(camera_dist_list) + 1,] = new_row
}
summary(camera_dist_list$dist)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 1103 1540 1978 2210 2578 5263
Q7 Interpret the values above. What is the largest, average, and minimum distance between two cameras? largest is 5263, average is 2210, minimun is 1103 # Nenxt we, need to check whether all the images have a deployment associated with them.
table(unique(img$placename) %in% unique(dep$placename))
##
## TRUE
## 38
We have 38 TRUE’s, which means all the images have deployment data.Phew!
Let’s check that all the placenames also have image data:
# check all the placenames in deployments are represented in the images data
table(unique(dep$placename) %in% unique(img$placename))
##
## TRUE
## 38
If you see any FALSE observations - you either have image data or deployments missing. Go back and fix your raw data!
#Next, let’s check individual camera activity
#install.packages("plotly")
library("plotly")
##
## Attaching package: 'plotly'
## The following object is masked from 'package:ggplot2':
##
## last_plot
## The following object is masked from 'package:stats':
##
## filter
## The following object is masked from 'package:graphics':
##
## layout
In the following plot, black dots denote start and end dates, lines denote periods where a camera is active. Each unique placename gets its own row on the plot - you can hover over the lines to get the deployment_id.
We will use a loop to build the different elements… you don’t need to understand the code itself, this is pretty intricate so just focus on how to interpret the output.
# Call the plot
p <- plot_ly()
# We want a separate row for each 'placename' - so lets turn it into a factor
dep$placename <- as.factor(dep$placename)
# loop through each place name
for(i in seq_along(levels(dep$placename)))
{
#Subset the data to just that placename
tmp <- dep[dep$placename==levels(dep$placename)[i],]
# Order by date
tmp <- tmp[order(tmp$start_date),]
# Loop through each deployment at that placename
for(j in 1:nrow(tmp))
{
# Add a line to 'p'
p <- add_trace(p,
#Use the start and end date as x coordinates
x = c(tmp$start_date[j], tmp$end_date[j]),
#Use the counter for the y coordinates
y = c(i,i),
# State the type of chart
type="scatter",
# make a line that also has points
mode = "lines+markers",
# Add the deployment ID as hover text
hovertext=tmp$deployment_id[j],
# Color it all black
color=I("black"),
# Suppress the legend
showlegend = FALSE)
}
}
# Add a categorical y axis
p <- p %>% layout(yaxis = list(
ticktext = as.list(levels(dep$placename)),
tickvals = as.list(1:length(levels(dep$placename))),
tickmode = "array"))
p
Q8 What do the gaps signify? Can you see any issues? No Data was collected at that time, inconsistent gaps
Sometimes you will see a deployment a long way to the left or right of the plot, this is usually a date error.
Corrections We checked the original datasheets for ALG036 and found out that the deployment end date for ALG036_2019-04-04 was incorrectly entered. It was written as 2020 not 2019. Let’s correct it:
dep$end_date[dep$deployment_id=="ALG036_2019-04-04"] <- ymd("2019-11-21")
#remember to format it as a date object
Detection check nce we are happy that are cameras were functioning when we expected them to be, we now need to check if all of our labelled images fall within the associated deployment periods. To do this we build on the previous plot above, but also add in the image data over the top. This plot can get very messy, so we divide it into sections of ten deployments.
As before, black lines show an active camera. Red dots show an image detections at that time.
We only show the output of the first 10 deployments, but you should do this for every single deployment you have!
Note - the code below is complex, you don’t have to understand it all at this moment in time. Happy to walk through it with you if you’d like though!
#install.packages("dplyr")
library("dplyr")
# Make a separate plot for each 20 stations For each 20 stations
# To do this make a plot dataframe
tmp <- data.frame("deployment_id"=unique(dep$deployment_id), "plot_group"=ceiling(1:length(unique(dep$deployment_id))/20))
dep_tmp <- left_join(dep,tmp, by="deployment_id")
for(i in 1:max(dep_tmp$plot_group))
{
# Call the plot
p <- plot_ly()
#Subset the data to just that placename
tmp <- dep_tmp[dep_tmp$plot_group==i,]
# Order by placename
tmp <- tmp[order(tmp$placename),]
# Loop through each deployment at that placename
for(j in 1:nrow(tmp))
{
#Subset the image data
tmp_img <- img[img$deployment_id==tmp$deployment_id[j],]
if(nrow(tmp_img)>0)
{
p <- add_trace(p,
#Use the start and end date as x coordinates
x = c(tmp_img$timestamp),
#Use the counter for the y coordinates
y = rep(j, nrow(tmp_img)),
# State the type of chart
type="scatter",
# make a line that also has points
mode = "markers",
# Add the deployment ID as hover text
hovertext=paste(tmp_img$genus,tmp_img$species),
# Color it all black
marker = list(color = "red"),
# Suppress the legend
showlegend = FALSE)
}
# Add a line to 'p'
p <- add_trace(p,
#Use the start and end date as x coordinates
x = c(tmp$start_date[j], tmp$end_date[j]),
#Use the counter for the y coordinates
y = c(j,j),
# State the type of chart
type="scatter",
# make a line that also has points
mode = "lines",
# Add the deployment ID as hover text
hovertext=tmp$deployment_id[j],
# Color it all black
color=I("black"),
# Suppress the legend
showlegend = FALSE)
}
# Add custom y axis labels
p <- p %>% layout(yaxis = list(
ticktext = as.list(tmp$deployment_id),
tickvals = as.list(1:nrow(tmp)),
tickmode = "array"))
print(p)
}
Q9 We have now plotted the images (red dots) over the deployments (black lines). What would a problem look like? some camera were collecting a lot more data than others
If you look at the deployment ALG029_2019-04-02 you will see that has what has happened here. We checked the datasheets and realised that the camera’s timestamp was set to the incorrect month when the deployment began (2019-05-02 instead of 2019-04-02).
# We set the wrong date for the camera collecting images in deployment
#":"ALG029_2019-04-02"
# We established that the deployment was 30 days out (as there are 30 days in April)
# So we add 30 days to all of the images in that deployment.
img[img$deployment_id=="ALG029_2019-04-02",]$timestamp <-
img[img$deployment_id=="ALG029_2019-04-02",]$timestamp - days(30)
# Easy!
#Taxonomy Check
Dealing with taxonomy in camera trap data sets can be a nightmare, particularly if your data labeling software does not give standardized lists of species (e.g. you are manually sorting images into folders). A species list is also something which is often produced for the appendix of a report or paper.
Let us start with building a list of our taxonomic classifications:
# First define vector of the headings you want to see (we will use this trick a lot later on)
taxonomy_headings <- c("class", "order", "family", "genus", "species", "common_name")
# Subset the image data to just those columns
tmp<- img[,colnames(img)%in% taxonomy_headings]
# Remove duplicates
tmp <- tmp[duplicated(tmp)==F,]
# Create an ordered species list
sp_list <- tmp[order(tmp$class, tmp$order, tmp$family, tmp$genus, tmp$species),]
# Create a column to the species list with genus and species pasted together
sp_list$sp <- paste(sp_list$genus, sp_list$species, sep=".")
# View the species list using kableExtra
sp_list %>%
kbl(row.names=F) %>%
kable_styling(full_width = T) %>%
kableExtra::scroll_box(width = "100%", height = "250px")
| class | order | family | genus | species | common_name | sp |
|---|---|---|---|---|---|---|
| Aves | Galliformes | Phasianidae | Tympanuchus | phasianellus | NA | Tympanuchus.phasianellus |
| Aves | Gruiformes | Gruidae | Grus | canadensis | NA | Grus.canadensis |
| Aves | Passeriformes | Corvidae | Corvus | corax | NA | Corvus.corax |
| Aves | Passeriformes | Corvidae | Perisoreus | canadensis | NA | Perisoreus.canadensis |
| Aves | Strigiformes | Strigidae | Strix | nebulosa | NA | Strix.nebulosa |
| Mammalia | Artiodactyla | Cervidae | Alces | alces | NA | Alces.alces |
| Mammalia | Artiodactyla | Cervidae | Cervus | canadensis | NA | Cervus.canadensis |
| Mammalia | Artiodactyla | Cervidae | Odocoileus | virginianus | NA | Odocoileus.virginianus |
| Mammalia | Artiodactyla | Cervidae | Rangifer | tarandus | NA | Rangifer.tarandus |
| Mammalia | Carnivora | Canidae | Canis | latrans | NA | Canis.latrans |
| Mammalia | Carnivora | Canidae | Canis | lupus | NA | Canis.lupus |
| Mammalia | Carnivora | Canidae | Vulpes | vulpes | NA | Vulpes.vulpes |
| Mammalia | Carnivora | Felidae | Lynx | canadensis | NA | Lynx.canadensis |
| Mammalia | Carnivora | Mustelidae | Lontra | canadensis | NA | Lontra.canadensis |
| Mammalia | Carnivora | Mustelidae | Martes | americana | NA | Martes.americana |
| Mammalia | Carnivora | Ursidae | Ursus | americanus | NA | Ursus.americanus |
| Mammalia | Lagomorpha | Leporidae | Lepus | americanus | NA | Lepus.americanus |
| Mammalia | Lagomorpha | Leporidae | Oryctolagus | cuniculus | NA | Oryctolagus.cuniculus |
| Mammalia | Primates | Hominidae | Homo | sapiens | NA | Homo.sapiens |
| Mammalia | Rodentia | Sciuridae | Tamiasciurus | hudsonicus | NA | Tamiasciurus.hudsonicus |
| NA | NA | NA | blank | NA | .blank | |
| NA | NA | NA | spp. | NA | .spp. | |
| NA | NA | NA | Canachites | canadensis | NA | Canachites.canadensis |
| NA | NA | NA | Unknown | unknown | NA | Unknown.unknown |
| NA | NA | NA | Weasel | spp. | NA | Weasel.spp. |
In this case, we actually have an external list of common names which we will use to update our img dataframe to make sure our naming is consistent across all species.
# Import the dataframe
tmp <- read.csv("C:/Users/Joel Bowersox/OneDrive - Paul Smith's College/fws332_wildlife_data/data/example_data/common_names.csv")
# Join it with the existing species list
sp_list$common_name <- NULL
sp_list <- left_join(sp_list, tmp)
## Joining with `by = join_by(sp)`
Then let’s write our species list into our data folder. YOu can check that this new data file exists by leaving R for a second and looking at your course directory
# Note we use the project_id from from project data frame to name the file - that way we won't overwrite it if we run things with a different project.
write.csv(sp_list, paste0("C:/Users/Joel Bowersox/OneDrive - Paul Smith's College/fws332_wildlife_data/data/example_data/",pro$project_id[1],"_raw_species_list.csv"))
Then lets update the common_name column in our img dataframe to reflect the updated common names.
We will do this using a left_join(), an operation which is invaluable when programming in R.
It uses a specified “key” variable to merge two dataframes in this case we will use the ‘sp’ column.
# first remove the common_name column
img$common_name <- NULL
# add an sp column to the img dataframe - remember the genus and species columns are not pasted together yet
img$sp <- paste(img$genus, img$species, sep=".")
# Next we do the 'left_join'
img <- left_join(img, sp_list[, c("sp", "common_name")], by="sp")
#Diel Activity Check Sometimes when setting up a camera trap, you can input the time incorrectly. This is actually very hard to detect unless you happen to be looking for it. The way we check is to plot the detections for each species by the 24 hour clock. If we get detections of nocturnal species in the day, or vice versa, it suggests there may be a problem.
Caveat A diurnal species active at night doesn’t mean there is actually a problem, camera traps have revealed that many animals are active when we thought they were not!
Cool note Researchers are increasingly using this information to determine a species “availability” for detection! More on that in the density and activity chapters.
For any species detected more than 10 times, we will plot when they were detected:
# First lets convert our timestamp to decimal hours
img$hours <- hour(img$timestamp) + minute(img$timestamp)/60 + second(img$timestamp)/(60*60)
# Count all of the captures
tmp <- img %>% group_by(common_name) %>% summarize(count=n())
yform <- list(categoryorder = "array",
categoryarray = tmp$common_name)
fig <- plot_ly(x = img$hours, y = img$common_name,type="scatter",
height=1000, text=img$deployment_id, hoverinfo='text',
mode = 'markers',
marker = list(size = 5,
color = 'rgba(50, 100, 255, .2)',
line = list(color = 'rgba(0, 0, 0, 0)',
width = 0))) %>%
layout(yaxis = yform)
fig
## Warning: Ignoring 2976 observations
Q10 Can you see any exclusively nocturnal species? Exclusively diurnal? Snowshoe Hares seem to only be out when it is dark out, much of every other species found will be out during the day
Congratulations - you have thoroughly error checked your camera data! We may find more errors in the data exploration and analysis RMD, so stay vigilant!