Finding a trace point closest to a certain value on the x axis

Here is a little challenge: Suppose I want to find the point of a trace closest to a certain x value on a graph axis. What would be an effective way to find such a point? I would be fine to get the point value of the y wave closes to the provided axis value. A secondary goal would be to find the point closes to the edge of the displayed x range, which is however still within the displayed range. This could be achieve when solving the first task with the restriction that the point must be at larger / smaller x than the designated value.

The twist: The data is XY with non-monotonous x values, and there may be offsets in x involved. Also, the plotted range may be limited and the range may be not the same for x and y. Here is a start:

Make/d/o/n=100 y_Vals = gnoise(100)+300, x_Vals = gnoise(50)+150
Display y_Vals[11,90] vs x_Vals[16,95]
ModifyGraph offset={-20,400}

 

I may be confused by your question. Is this not an application that is resolved using either FindValue(s) or a FindLevels? Perhaps you are asking for a way to avoid having the search go from front to back and then from back to front, comparing the offset of the found value in each case and picking the smallest offset?

Have you tried this already? FindValue will not work at all unless I provide a precise value or set the tolerance rather high => the correct point is not found. FindLevel(s) will find the first position where the requested value is crossed, which is completely useless for non-monotonic x values. So yeah, I could try to write a loop calculating the distance to every x value in the whole x wave, then try to determine the minimum. I probably can come up with an awkward and cumbersome function to do this, but still wanted to hear if somebody has an intelligent option. :-)

Probably BinarySearch on the X wave is the way forward, but that requires monotonic X values. You could MakeIndex, IndexSort a copy and BinarySearch on that. Then invert the result to get to the Y value. Clearly, I've left out details on "invert", which isn't a trivial task.

I fear there is not efficient method for non-monotonic X values.

John, I was also just thinking that sort might work, hehe. I only want the point of the y-wave which is closest in x. For this I just need to find the best x-wave point and then account for a limited plot range if applicable. Here is what I got:

function getPfromX(wave xw, variable xVal)
    Duplicate/Free xw, sort_xw, pnt_xw; pnt_xw=p
    Sort xw, sort_xw, pnt_xw
    FindLevel/P/Q sort_xw, xVal
    if (!v_flag)
        return round(pnt_xw[v_levelX])
    else
        return NaN
    endif
end

 

BinarySearch, if you can use it, is faster (O(ln(N)) rather than O(N)). You could also use BinarySearchInterp so that you can round.

Thanks, good to know. I am not hitting any performance limits at the moment, but will keep this in mind for the future.

OK. I think that I understand. If so, why won't these steps do what you want?

* duplicate/FREE xwave dwave, pwave
* dwave = abs(xwave - value)
* pwave = p
* sort dwave and pwave using dwave as basis from lowest to highest
--> first point in dwave is desired smallest offset --> extract p value from first point in pwave

Otherwise, I am clearly not understanding the question (the need for a BinarySearch goes right over my head).

JJ, yes I think this will also give you the result. And BinarySearch is just a much more bare-bones alternative to FindValue. With the wave assignment your approach might get slow for big waves, so (without testing it) I guess the performance ranking will be: BinarySearch > FindValue > difference wave. BTW, I just looked into Tony's Marquee Peaks project, and he does exactly what I have posted above to find the range of points. I could have just looked there in the first place (but he does not account for trace offsets yet ;).

create a wave of distances, then wavestats/M=1 to give v_minloc?

MarqueePeaks in its current form uses mostly TraceFromPixel, which is not helpful for your problem.

At risk of seeming really naive, why does JJ's solution need to sort? I would just go through the wave once, keeping track of the closest x so far. 

Tony, yes that would work as well. Maybe getting the distances with MatrixOP is also efficient.

Kris, I am having a bit trouble to think of a way to keep track of the minimal value inside an implicit loop with wave assignment, but maybe I am forgetting something. With a for loop this would surely work as well, but it might be rather slow.

> Tony, yes that would work as well. Maybe getting the distances with MatrixOP is also efficient.

I had looked for an equivalent option in MatrixOp for minValLoc. With such a case, the entire process might be a one-liner

MatrixOP pv = minValLoc(abs(xwave - value))

(sidebar -- perhaps this is also a request for two new options in MatrixOP ... minValLoc and maxValLoc ... both able to handle all dimensional inputs ... e.g. finding the minimum location in a 2-D image returns pv as a (1x2) wave ... I can see these two new options being useful to obtain the (x,y) pixel location of a minimum or maximum intensity tracking through an image stack without writing a thread safe function to run the implicit loop)

> I am having a bit trouble to think of a way to keep track of the minimal value inside an implicit loop ...

Me too. But perhaps when dwave = xwave - value exists, something like this below, which needs no sorting and does the WaveStats operation as a function instead.

pv = x2pnt(dwave,wavemin(dwave))

JJ, indeed this option would be nice to have. While not looking too nice, this one-liner seems to work:

function getPfromX(wave xw, variable xVal)
    MatrixOP/Free pVal = equal(abs(xw-xVal),minCols(abs(xw-xVal))).indexRows(xw)
    return pVal[0]
end