Skip to content

Commit

Permalink
Added TrajResampleTime function
Browse files Browse the repository at this point in the history
  • Loading branch information
JimMcL committed Sep 5, 2018
1 parent 7dfc348 commit 3142c00
Show file tree
Hide file tree
Showing 10 changed files with 133 additions and 6 deletions.
1 change: 1 addition & 0 deletions NAMESPACE
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export(TrajLogSequence)
export(TrajMeanVectorOfTurningAngles)
export(TrajMeanVelocity)
export(TrajRediscretize)
export(TrajResampleTime)
export(TrajReverse)
export(TrajRotate)
export(TrajScale)
Expand Down
3 changes: 2 additions & 1 deletion NEWS.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# Changes to the `trajr` package

## trajr 1.2.1
## trajr 1.3.0

* Altered handling of parameter `readcsvFn` to `TrajsBuild` to make it
possible to use `readr::read_csv` without a wrapper function.
* Added function `TrajResampleTime` to resample a trajectory to fixed step times.

## trajr 1.2.0

Expand Down
6 changes: 4 additions & 2 deletions R/generate.R
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
#' generate the lengths of each step.
#' @param fps Simulated frames-per-second - used to generate times for each
#' point in the trajectory.
#' @param ... Additional arguments are passed to \code{\link{TrajFromCoords}}.
#'
#' @return A new Trajectory with \code{n} segments and \code{n + 1} coordinate
#' pairs.
Expand Down Expand Up @@ -78,7 +79,8 @@ TrajGenerate <- function(n = 1000, random = TRUE, stepLength = 2,
angularErrorDist = function(n) stats::rnorm(n, sd = angularErrorSd),
linearErrorSd = 0.2,
linearErrorDist = function(n) stats::rnorm(n, sd = linearErrorSd),
fps = 50) {
fps = 50,
...) {
angularErrors <- angularErrorDist(n)
linearErrors <- linearErrorDist(n)
stepLengths <- stepLength + linearErrors
Expand All @@ -100,5 +102,5 @@ TrajGenerate <- function(n = 1000, random = TRUE, stepLength = 2,
coords <- c(complex(length.out = 1), cumsum(steps))
}

TrajFromCoords(data.frame(x = Re(coords), y = Im(coords)), fps = fps)
TrajFromCoords(data.frame(x = Re(coords), y = Im(coords)), fps = fps, ...)
}
28 changes: 28 additions & 0 deletions R/resample.R
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#' Resample a trajectory to a constant time interval.
#'
#' Constructs a new trajectory by resampling the input trajectory to a fixed
#' time interval. Points are linearly interpolated along the trajectory. Spatial
#' and time units are preserved.
#'
#' @param trj The trajectory to be resampled.
#' @param stepTime New sampled step time. Each step in the new trajectory will
#' have this duration.
#' @param newFps Value to be stored as the FPS value in the new trajectory (see
#' \code{\link{TrajGetFPS}}). It is not otherwise used by this function.
#' @return A new trajectory with a constant time interval for each step. Points
#' in the new trajectory are calculated by linearly interpolating along
#' \code{trj}.
#'
#' @export
TrajResampleTime <- function(trj, stepTime, newFps = NULL) {
# Determine times of new points
times <- seq(from = min(trj$time), to = max(trj$time), by = stepTime)
# Interpolate x and y separately
x <- stats::approx(trj$time, trj$x, times)$y
y <- stats::approx(trj$time, trj$y, times)$y

TrajFromCoords(data.frame(x, y, times), timeCol = 3,
fps = newFps,
spatialUnits = TrajGetUnits(trj),
timeUnits = TrajGetTimeUnits(trj))
}
2 changes: 2 additions & 0 deletions RELEASE-PROCEDURE.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
Release procedure
-----------------

Check that all exported functions have been mentioned in the vignette by running check-vignette.sh (from bash). This may require editing to convert to UNIX line endings.

Run devtools::build_win() and wait for the result

Update version number and date in DESCRIPTION file. See https://semver.org/
Expand Down
2 changes: 1 addition & 1 deletion check-vignette.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
#
# Searches for, and reports, any exported functions which aren't mentioned in trajr-vignette.Rmd

grep export NAMESPACE | sed 's/export(//; s/)//' | while read fn
grep export NAMESPACE | sed 's/export(//; s/)//' | tr -d "\r" | while read fn
do
n=$(grep "$fn" vignettes/trajr-vignette.Rmd | wc -l)
[ "$n" -eq 0 ] && echo $fn
Expand Down
4 changes: 3 additions & 1 deletion man/TrajGenerate.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

27 changes: 27 additions & 0 deletions man/TrajResampleTime.Rd

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

34 changes: 34 additions & 0 deletions tests/testthat/test_basic.R
Original file line number Diff line number Diff line change
Expand Up @@ -528,3 +528,37 @@ test_that("Convert times", {
.checkTimes(c("1:01:01:001", "2:02:02:002", "3:03:03:003", "4:04:04:004"),
c(3661.001, 7322.002, 10983.003, 14644.004))
})

test_that("Resampling", {
# Plot one trajectory over another
plotTwoTrjs <- function(trj1, trj2) {
plot(trj1, draw.start.pt = FALSE, lwd = 2)
points(trj1, cex = .6, draw.start.pt = FALSE)
lines(trj2, col = "red", lty = 2)
points(trj2, col = "red", draw.start.pt = FALSE)
}

set.seed(1)
# Give it a constant step length of 1 so that it has a constant speed to simplify some tests
trj <- TrajGenerate(10, angularErrorSd = 1, stepLength = 1, linearErrorDist = function(n) rep(0, n), fps = 1)
trjL <- TrajLength(trj)

# These tests aren't strictly required to be true because resampled
# trajectories are also smoothed, so may be shorter than expected
ta <- TrajResampleTime(trj, .5)
expect_true(trjL - TrajLength(ta) < .5)
# plotTwoTrjs(trj, ta)
tb <- TrajResampleTime(trj, .7)
expect_true(trjL - TrajLength(tb) < .7)
# plotTwoTrjs(trj, tb)
tc <- TrajResampleTime(trj, 1)
expect_true(trjL - TrajLength(tc) == 0)
# plotTwoTrjs(trj, tc)
td <- TrajResampleTime(trj, 2)
expect_true(trjL - TrajLength(td) < 2)
# plotTwoTrjs(trj, td)
te <- TrajResampleTime(trj, 2.1)
# This isn't TRUE, since resampled te is straighter than trj
#expect_true(trjL - TrajLength(td) < 2)
# plotTwoTrjs(trj, te)
})
32 changes: 31 additions & 1 deletion vignettes/trajr-vignette.Rmd
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,8 @@ legend("topright", c("Original", "Smoothed"), lwd = c(1, 2), lty = c(1, 1), col

### Resampling trajectories

`trajr` provides functions to resample a trajectory to a fixed step length or a fixed step stime.

Some functions require a `Trajectory` with a fixed step length. The process of resampling a trajectory to a fixed step length is called _rediscretization_. The function `TrajRediscretize` implements trajectory resampling using the algorithm described by Bovet & Benhamou (1988). There are no clear guidelines for choosing a suitable step length for rediscretization: too small a step length leads to oversampling, leading to high autocorrelation between steps and high variability; too large a step length results in undersampling and loss of information. See Turchin (1998) section 5.2.2 for a discussion.
```{r rediscretize, fig.width=6, fig.height=4, fig.cap="_Rediscretization of trajectory with $\\mu_{L} = 2$ to step length $1$._", echo=-1:-2}
par(mar = c(4, 4, 0.5, 0.5) + 0.1)
Expand All @@ -146,7 +148,7 @@ trj <- TrajGenerate(10, stepLength = 2)
# Plot original trajectory with dots at trajectory coordinates
plot(trj, lwd = 2)
points(trj, draw.start.pt = FALSE, pch = 16, col = "black")
points(trj, draw.start.pt = FALSE, pch = 16, col = "black", cex = 1.2)
# Resample to step length 1
resampled <- TrajRediscretize(trj, 1)
Expand All @@ -158,6 +160,27 @@ legend("topright", c("Original", "Rediscretized"), col = c("black", "red"),
lwd = 2, inset = c(0.01, 0.02))
```

The function `TrajResampleTime` linearly interpolates points along a trajectory to create a new trajectory with fixed step time intervals.
```{r resample, fig.width=6, fig.height=4, fig.cap="_Resampling of trajectory with step duration $1$ hour._", echo=-1:-2}
par(mar = c(4, 4, 0.5, 0.5) + 0.1)
set.seed(5)
# Generate trajectory with a point every 2 hours and highly variable speed (which equates to step length)
trj <- TrajGenerate(10, stepLength = 1, fps = .5, timeUnits = "hours", linearErrorSd = .8)
# Plot original trajectory with dots at trajectory coordinates
plot(trj, lwd = 2)
points(trj, draw.start.pt = FALSE, pch = 16, col = "black", cex = 1.2)
# Resample to 1 hourly steps
resampled <- TrajResampleTime(trj, 1)
# Plot rediscretized trajectory in red
lines(resampled, col = "#FF0000A0", lwd = 2)
points(resampled, type = 'p', col = "#FF0000A0", pch = 16)
legend("topright", c("Original", "Resampled"), col = c("black", "red"),
lwd = 2, inset = c(0.01, 0.02))
```

### Other trajectory operations

Expand Down Expand Up @@ -509,6 +532,13 @@ The function `TrajsStepLengths` returns a vector containing all of the step leng
summary(TrajsStepLengths(trjs))
```

`TrajConvertTime` is a convenience function that converts a delimited time string to a numeric value. This may be useful if your data contains times which are formatted as strings with millisecond accuracy. In general, the base R function `strptime` should be used to convert strings to times, but it does not handle milliseconds.
```{r}
t <- data.frame(time = c("0:00:00:029", "0:01:00:216", "0:02:01:062", "1:00:02:195", "1:06:03:949", "1:42:04:087"), stringsAsFactors = FALSE)
t$seconds <- TrajConvertTime(t$time)
t
```

## Random trajectory generation

`Trajr` allows you to generate random trajectories with the `TrajGenerate` function. Random trajectories may be used in simulation studies, or simply for experimenting with trajectory analysis, and it has been used to generate the majority of trajectories in this document. By default, `TrajGenerate` returns a correlated random walk, however, by specifying different arguments, a variety of trajectory types can be created.
Expand Down

0 comments on commit 3142c00

Please sign in to comment.