avgjoe13
avgjoe13

Reputation: 115

How to insert a formula into a function so that it can be used to calculate values for consecutive rows

I'm aware that the title may be a little confusing but please allow me to clarify. I have a column with values

> df
    Direction
    139
    118
    180
    142
    185
    224
    137
    245

I have the following formula

((pt1 - pt2 + 180)%%360 - 180)

I would like to insert this formula to calculate the difference between each consecutive instance Example:

((139 - 118 + 180)%%360 - 180) = 21
((118 - 180 + 180)%%360 - 180) = -62

Some of the things I've tried

Creating the function

angle <- function(turn1, turn2) {
  coursediff <- ((turn1 - turn2 + 180)
                 %%360 - 180)
  coursediff
}
angle(118, 180)
## [1] -62

Trying to use it for each consecutive row for column Direction, creating a new column named Diff

df$Diff <- with(df, 
     ave(Direction, ID,  FUN=function(angle) c(NA, diff(angle))))

Or

df %>% group_by(ID) %>% 
  mutate(gap=angle(c(NA,diff(Direction)), 1))

Or

index <- function(angle) c(0, diff(angle))
DT <- data.table(df)
DT[, Diff := index(Direction), by = "ID"]

Or even

transform(df, 
          Diff=unlist(tapply(Direction, ID, 
                                function(angle) c(0, diff(Direction)))))

All the above attempts are giving me the same answers as if I were to simply take the difference per row, like so

df$Diff <- c(NA, diff(df$Direction))

This is what I get now

> df
    Direction  Diff  ID
    139        NA    1
    118       -21    1
    180        62    1
    142       -38    1
    185        NA    2
    224        39    2
    137       -87    2
    245        108   2

I hope someone can help me out, much appreciated!

Upvotes: 0

Views: 64

Answers (2)

GGamba
GGamba

Reputation: 13680

When you do

angle(118, 180)
## [1] -62

Means

angle(line1, line2)

You are comparing a direction with its consecutive, e.g. the next one.

In the other functions you wrote, the order is reversed, that is

angle(line2, line1)

Just fix that and you obtain the result you want:

library(dplyr)

df %>% 
  group_by(ID) %>% 
  mutate(diff = (Direction - lead(Direction) + 180) %% 360 -180)
#> Source: local data frame [8 x 4]
#> Groups: ID [2]
#> 
#>   Direction  Diff    ID  diff
#>       <int> <int> <int> <dbl>
#> 1       139    NA     1    21
#> 2       118   -21     1   -62
#> 3       180    62     1    38
#> 4       142   -38     1    NA
#> 5       185    NA     2   -39
#> 6       224    39     2    87
#> 7       137   -87     2  -108
#> 8       245   108     2    NA

Upvotes: 2

lmo
lmo

Reputation: 38520

You can use ave and diff in base R like this.

dat$diff <- ave(dat$Direction, dat$ID,
                FUN=function(x) c(NA, (rev((diff(rev(x))) + 180) %% 360) -180))

Here, we calculate in the desired order by feeding diff the reversed vector using rev and return the vector in the desired order using rev on the output of diff. Note that we need to use c(NA, ..) to add a missing value for each ID.

which returns

dat
  Direction Diff ID diff
1       139   NA  1   NA
2       118  -21  1   21
3       180   62  1  -62
4       142  -38  1   38
5       185   NA  2   NA
6       224   39  2  -39
7       137  -87  2   87
8       245  108  2 -108

data

dat <-
structure(list(Direction = c(139L, 118L, 180L, 142L, 185L, 224L, 
137L, 245L), Diff = c(NA, -21L, 62L, -38L, NA, 39L, -87L, 108L
), ID = c(1L, 1L, 1L, 1L, 2L, 2L, 2L, 2L)), .Names = c("Direction", 
"Diff", "ID"), row.names = c(NA, -8L), class = "data.frame")

Upvotes: 1

Related Questions