0

Given the gravity dataframe:

df <- structure(list(user_id = c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 
1L), obs_id = c(1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L), scroll_id = c(1L, 
1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L, 1L), gra_x = c(-0.76694775, -0.7770001, 
-0.7770001, -0.7770001, -0.7864253, -0.7864253, -0.7864253, -0.7864253, 
-0.7947834, -0.80270606), gra_y = c(7.083949, 7.0901933, 7.0901933, 
7.0901933, 7.096873, 7.096873, 7.096873, 7.096873, 7.1042953, 
7.1121664), gra_z = c(6.737941, 6.730217, 6.730217, 6.730217, 
6.722077, 6.722077, 6.722077, 6.722077, 6.713248, 6.7039647)), .Names = c("user_id", 
"obs_id", "scroll_id", "gra_x", "gra_y", "gra_z"), row.names = c(NA, 
-10L), class = c("grouped_df", "tbl_df", "tbl", "data.frame"), vars = c("user_id", 
"obs_id", "scroll_id"), drop = TRUE, indices = list(0:9), group_sizes = 10L, biggest_group_size = 10L, labels = structure(list(
    user_id = 1L, obs_id = 1L, scroll_id = 1L), row.names = c(NA, 
-1L), class = "data.frame", vars = c("user_id", "obs_id", "scroll_id"
), drop = TRUE, .Names = c("user_id", "obs_id", "scroll_id")))

I want to calculate the angle between each 2 vectors (gra_x, gra_y, gra_z)

using:

angle <- function(x,y){
    dot.prod <- x%*%y 
    norm.x <- norm(x,type="2")
    norm.y <- norm(y,type="2")
    theta <- acos(dot.prod / (norm.x * norm.y))
    as.numeric(theta)
  }

I have tried to use:

df %>% 
    select(gra_x, gra_y, gra_z) %>%
    mutate(theta = angle(c(gra_x, gra_y, gra_z), c(lead(gra_x), lead(gra_y), lead(gra_z))))

But it returns an error. Please advise how to calculate an angle between each 2 rows?

SteveS
  • 3,789
  • 5
  • 30
  • 64
  • What error does it return? – G5W Oct 19 '18 at 23:21
  • You'll need to use `map` to apply multiple arguments to a function...but your function `angle` seems to take two only two arguments and you're feeding it four? None of this may matter, since the function will probably need to be redesigned in `tidyeval` syntax, the incomprehensible new addition to programming with `dplyr`. Only a handful of people understand it. – Nettle Oct 20 '18 at 14:47

1 Answers1

1

I think it is easier and probably more efficient to do it (without the dplyr syntax). See [R: row-wise dplyr::mutate using function that takes a data frame row and returns an integer and benchmarks.

Another thing to consider is that acos sometimes return NaN. A solution is provided in (see [acos(1) returns NaN for some values, not others) and used in the modified angle() function below.

angle <- function(x,y){
    dot.prod <- as.numeric(x%*%y )
    norm.x <- norm(x,type="2")
    norm.y <- norm(y,type="2")
    atheta<-dot.prod / (norm.x * norm.y)
    theta <- acos(pmin(pmax(atheta,-1.0),1.0))
    as.numeric(theta)
}

And we can simply calculate the angle between each 2 rows using a for loop:

df<-as.matrix(df)
df<-cbind(df, theta=NA)
for (i in 1:(nrow(df)-1)){
   df[i,"theta"]= angle(df[i,c("gra_x","gra_y","gra_z")] , df[i+1,c("gra_x","gra_y","gra_z")])
}

df
#      user_id obs_id scroll_id      gra_x    gra_y    gra_z       theta
# [1,]       1      1         1 -0.7669478 7.083949 6.737941 0.001441019
# [2,]       1      1         1 -0.7770001 7.090193 6.730217 0.000000000
# [3,]       1      1         1 -0.7770001 7.090193 6.730217 0.000000000
# [4,]       1      1         1 -0.7770001 7.090193 6.730217 0.001441059
# [5,]       1      1         1 -0.7864253 7.096873 6.722077 0.000000000
# [6,]       1      1         1 -0.7864253 7.096873 6.722077 0.000000000
# [7,]       1      1         1 -0.7864253 7.096873 6.722077 0.000000000
# [8,]       1      1         1 -0.7864253 7.096873 6.722077 0.001452513
# [9,]       1      1         1 -0.7947834 7.104295 6.713248 0.001480881
#[10,]       1      1         1 -0.8027061 7.112166 6.703965          NA
fishtank
  • 3,718
  • 1
  • 14
  • 16