Andreas
Andreas

Reputation: 167

Iterate through a data.table while applying an operation to previos and next item

I have a number of GPS points.

points <- structure(list(counter = 1:6, lon = c(11.8300715, 11.8296697, 
11.8268708, 11.8267236, 11.8249612, 11.8251062), lat = c(48.1099048, 
48.10884, 48.1067431, 48.1066077, 48.1037673, 48.103318), dist = c(46.8463805878941, 
33.4921440879536, 10.6101735030534, 18.6085009578724, 6.97253109610173, 
9.8912817449265)), row.names = c(NA, -6L), class = c("data.table", 
"data.frame"))

I would like to smooth the track out. To do so i would like to apply the following calculation

points[n].latitude = points[n-1].latitude * 0.3 + points[n].latitude * .4 + points[n+1].latitude * .3
points[n].longitude = points[n-1].longitude * 0.3 + points[n].longitude * .4 + points[n+1].longitude * .3

So basically i need to iterate through the structure and apply the operation to previos and the next entry. What is the best way to do so? I would like to avoid a for loop. Thank you for advice.

Upvotes: 0

Views: 68

Answers (2)

r2evans
r2evans

Reputation: 160417

Try this hack:

library(data.table)
cols <- c("lon", "lat")
mysmooth <- function(z, wts = c(0.3, 0.4, 0.3)) { notna <- !is.na(z); sum(z[notna] * wts[notna]) / sum(wts[notna]); }
points[, (cols) := lapply(.SD, function(z) zoo::rollapply(c(NA,z,NA), 3, mysmooth)), .SDcols = cols]
points
#    counter      lon      lat      dist
# 1:       1 11.82990 48.10945 46.846381
# 2:       2 11.82895 48.10853 33.492144
# 3:       3 11.82767 48.10733 10.610174
# 4:       4 11.82624 48.10580 18.608501
# 5:       5 11.82553 48.10448  6.972531
# 6:       6 11.82504 48.10351  9.891282

The intent of c(NA,z,NA) is to somehow deal with partial vectors. By default, zoo::rollapply will either:

  • partial=FALSE will cause the return vector to be shorter than the source, because it only uses full windows. In this data with k=3, this results in losing one value on the left and one value on the right (assuming align="center"); or
  • partial=TRUE send vectors of length less-than-3 to the function. When this happens, I assume that your function would be (value[n]*0.4 * value[n+1]*0.3)/0.7 (and similar for the right side).

I should add that because I do a partial weighted-average, that the end points smoothing will be biased inwards.

Upvotes: 3

J. Doe.
J. Doe.

Reputation: 1305

All you need is to perform the calcs using the build-in shift function

setDT(points)[, (latitude.new) := shift(latitude, type='lag')*0.3 +
                                  latitude * 0.4 +     
                                  shift(latitude, type='lead')*3,]

See here for more info How to create a lag variable within each group?.

Upvotes: 1

Related Questions