user3969377 user3969377 - 2 months ago 9
R Question

R time from nearest zero crossing

I would to add columns that are the distance in time to the closest zero crossing. The distance in time can be negative if the point is after the zero crossings.

Here is a calculation and plotting of the zero crossings

# Data generation
t=seq(0,10,0.05)
h = sin(t)+3*cos(3*t)
dh = cos(t)-9*sin(3*t)
plot(t,h,type='b')

# Find indices of zero crossings

df <- data.frame(h,dh)
zero_cross_down <- df[-1,] < 0 & df[-nrow(df),] >= 0
zero_cross_up <- df[-1,] >= 0 & df[-nrow(df),] < 0
indx_h_cross_down = which(zero_cross_down[,1])
indx_h_cross_up = which(zero_cross_up[,1])

# Find times of zero crossings and plot
hb<-df[,"h"][indx_h_cross_down]
ha<-df[,"h"][indx_h_cross_down+1]
tb<-indx_h_cross_down*0.05
ta<-(indx_h_cross_down+1)*0.05
tzcd_h <- tb + (ta-tb)/(ha-hb)*(0-hb)
points(tb,0*tzcd_h,col="red",pch='v')

hb<-df[,"h"][indx_h_cross_up]
ha<-df[,"h"][indx_h_cross_up+1]
tb<-indx_h_cross_up*0.05
ta<-(indx_h_cross_up+1)*0.05
tzcu_h <- tb + (ta-tb)/(ha-hb)*(0-hb)

points(tb,0*tzcu_h,col="green",pch='^')


But, now, I don't know how to calculate and a column to dh that is the time to the nearest crossing.

Answer

If I interpret what the OP wants correctly, we can compute the time to the next nearest crossing (either up or down) for each element in t using:

dt <- outer(-t,c(tzcd_h,tzcu_h),"+")
dt[dt < 0] <- Inf
dt_cross <- do.call(pmin.int, as.data.frame(dt))

Notes:

  1. Use outer to compute the difference in time between each crossing and each t. The result of this is a matrix where each row corresponds to an element in t, each column corresponds to a crossing, and each element of this matrix is the time difference.
  2. We are only interested in positive time differences (i.e., to the next crossing), so we set all negative values in the matrix to Inf.
  3. Finally, we compute the minimum time difference for each row (i.e., each element in t) using a fast method for performing row-wise min of a matrix as detailed in this SO answer.
  4. If the requirement is to compute the time difference to just the next up crossing (and not the next either up or down), then replace c(tzcd_h,tzcu_h) with tzcu_h in the outer computation. Similarly, if the requirement is to compute the time difference to just the next down crossing, then replace c(tzcd_h,tzcu_h) with tzcd_h in the outer computation. Obviously, we can also compute both of these separately.

The resulting dt_cross is a vector of the same length as t that contains the minimum time to the next crossing. This vector can be added to the df using df$dt_cross <- dt_cross.

head(dt_cross)
##[1] 0.6354627 0.5854627 0.5354627 0.4854627 0.4354627 0.3854627

We can plot this dt_cross as blue dots overlaid on the plot generated by the OP's code to see what is happening:

points(t,dt_cross,col="blue",pch='.')

time to next crossing

Note that the last 20 values of dt_cross are Inf. This is because the last crossing found is before these 20 values of t.


Update to new requirements

By definition, a distance is a metric and is non-negative. In the following we distinguish time distance from time difference, which can be either positive, zero, or negative. If we are interested in computing the time distance to the nearest down crossing, then the computation is:

dt_n <- dt_p <- outer(-t,tzcd_h,"+")
dt_p[dt_p < 0] <- Inf
dt_cross_p <- do.call(pmin.int, as.data.frame(dt_p))
dt_n[dt_n > 0] <- -Inf
dt_cross_n <- do.call(pmax.int, as.data.frame(dt_n))
dt_cross <- ifelse(dt_cross_p < -dt_cross_n, dt_cross_p, dt_cross_n)

Notes:

  1. Use outer as before to compute the time difference matrix between each down crossing and each element in t. We set this result to both dt_n and dt_p.
  2. dt_p will be use to find the minimum positive time difference to a down crossing point for each element in t. We set all its negative values to Inf and compute its row-wise minimum as before. The result is a vector dt_cross_p, which can be interpreted as being the minimum time distance to the next down crossing for each element in t.
  3. Conversely, dt_n will be use to find the maximum negative time difference to a down crossing point for each element in t. We set all its positive values to -Inf and call pmax likewise. The result is a vector dt_cross_n, which can be interpreted as being the minimum time distance (maximum negative time difference) to the previous down crossing for each element in t.
  4. The time difference to the nearest down crossing for each element in t is then its corresponding element in dt_cross_p if dt_cross_p < -dt_cross_n and dt_cross_n, otherwise. We make this computation in vectorized fashion using ifelse.

We now have the plot:

delta time to nearest down crossing