user3969377 - 3 months ago 12

R Question

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:

- 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. - 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`

. - 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. - 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='.')
```

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`

.

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:

- 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`

. `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`

.- 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`

. - 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: