Given a list of {x,y} datapoints, return a pure function f (from the reals to the reals) such that f[x]==y for every {x,y} in the data. If x is not one of the x-values then return the y-value for the previous point (the one with x-value less than x). If the function gets a value less than that of the first x-value in the data — i.e., there is no previous point — then return 0.
For example, given data {{1,20}, {2,10}}, return a pure function that looks like this:
Graph of the function given {{1,20},{2,10}} http://yootles.com/outbox/so/piecewise.png
I wrote something using Function and Piecewise that I’ll include as an answer but it seems like it might be inefficient, especially for a large list of points.
[UPDATE: My answer may actually be decent now. I’ll probably go with it if no one has better ideas.]
To be clear, we’re looking for function that takes a single argument — a list of pairs of numbers — and returns a pure function.
That pure function should take a number and return a number.
Hand-Coded Binary Search
If one is willing to sacrifice conciseness for performance, then an imperative binary search approach performs well:
Equipped with some test scaffolding…
…we can test and time various solutions:
On my machine, the following timings are obtained:
I expect that further performance gains could be obtained by compiling the various functions, but I’ll leave that as an exercise for the reader.
Better Still: Precomputed Interpolation
(response To dreeves’ comment)
It is baffling that a hand-coded, uncompiled binary search would beat a Mathematica built-in function. It is perhaps not so surprising for
Piecewisesince, barring optimizations, it is really just a glorified IF-THEN-ELSEIF chain testing expressions of arbitrary complexity. However, one would expectInterpolationto fare much better since it is essentially purpose-built for this task.The good news is that
Interpolationdoes provide a very fast solution, provided one arranges to compute the interpolation only once:This is blindingly fast, requiring only 0.016 seconds on my machine to execute
test[stepifyWithInterpolation, 100000].