The trackframe package simplifies the process of supporting multiple common representations of animal track data as function inputs. Animal track data refers to any data that consists of a time series of coordinates that represent sequential locations of a moving entity. This package is intended for developers who want to write functions and packages that manipulate and analyze animal tracks, and that has easy integration with other R packages that are commonly used in this field (e.g., move2, sf, etc).
Explore the benefits of trackframe here: vignette("why_trackframe").
install.packages("trackframe")
library(trackframe)Refer to the following vignettes:
-
Advantages of trackframe -
vignette("why_trackframe"): Learn about the advantages of using trackframe over non-trackframe approaches to writing functions for manipulating and analyzing animal tracks. -
Quickstart guide -
vignette("trackframe"): Learn how to createtrackframeobjects, convert to and from other common representations of animal track data, and manipulate and subset columns of atrackframeobject. -
Identifying the Key Columns -
vignette("identifying_columns"): Learn about howas.trackframeidentifies the columns representing time, coordinates, and track id (usually animal id), and how you can configure or override the sources it uses. -
Compatible Coordinate Systems -
vignette("crs"): Learn about the coordinate systems that are compatible with trackframe, and how to set the crs of atrackframeobject.
The trackframe package provides the trackframe object as well as simple universal methods to convert to and from other common animal track data representations, including move2, sftrack, data.frame, and matrix object formats. trackframe objects store one location per row, with columns for easting, northing, time, and id. Coordinates are stored in UTM easting/northing format with a given EPSG zone. Relevant information about all columns are set and stored as attributes. All columns can be easily accessed by accessor functions, and the trackframe itself is manipulatable in the same way as any other dataframe object. The trackframe package also provides simple functions for extracting relevant columns, regardless of what the use has named them.
For more details about advantages of using trackframe over non-trackframe approaches to writing functions for manipulating and analyzing animal tracks, see vignette("why_trackframe").
Suppose you are writing a simple xyt function to get the average speed over time of an animal track:
average_speed_over_time <- function(x, y, t) {
t <- as.numeric(t)
weighted.mean(
sqrt(diff(x)^2 + diff(y)^2) / diff(t),
diff(t),
na.rm = TRUE
)
}
If you would like apply this function to multiple animal tracks, and you would like your code to work for different types of track input (e.g., move2, sftrack, and non-sf data.frame objects), then you can use trackframe to facilitate this functionality. With trackframe, you can first convert your input to a trackframe object, and refer to relevant columns without directly referencing their names. The above function would thus look like this:
library(trackframe)
#convert whatever representation of the data that you have to a trackframe object
tf <- as.trackframe(data, id_col = 'track_id')
#split the trackframe object by unique track id
tf_split <- split_by_id(tf)
# loop through each unique id and calculate each animal's average speed
output <- c(NA)
for (i in seq_along(unique(id(tf)))) {
tf_subset <- select_id(tf, unique(id(tf))[i])
output[i] <- average_speed_over_time(easting(tf_subset), northing(tf_subset), time(tf_subset))
}
Without trackframe, you would need to write your function such that it checks the input class, extracts the relevant columns (using class-specific functions and object-specific names), and then applies the average speed calculation. You would therefore probably end up with something like this:
# Assign variables based on input data format
if ('sf' %in% class(data)) {
coords <- sf::st_coordinates(sf::st_transform(data, 3857))
if ('sftrack' %in% class(data)) {
track_id <- sapply(data[[attr(data, "group_col")]], deparse)
timestamp <- data[[attr(data, 'time_col')]]
} else if ('move2' %in% class(data)) {
track_id <- move2::mt_track_id(data)
timestamp <- move2::mt_time(data)
}
} else {
coords <- as.matrix(data[,c('x', 'y')])
track_id <- data$track_id
timestamp <- data$t
}
# Calculate average speed for each track
sapply(
unique(track_id),
function(focal_track_id){
idx <- which(track_id == focal_track_id)
average_speed_over_time(coords[idx,1], coords[idx,2], timestamp[idx])
}
)
Trackframe thus simplifies the development of functions and packages that manipulate and analyze animal tracks, enabling the accommodation of multiple representations of animal track data. For a more detailed comparison of trackframe and non-trackframe methods, see vignette("why_trackframe")