Interpolations.jl: different fill values left and right for FillExtrapolation

Is it possible to select different fill values for left and right for extrapolation?
For example, imagine I want the interpolation object to return 0 when to left of the minimum existing value in the grid and 1 when it’s to the right of the maximum existing value in the grid.

My naive attempts:

julia> using Interpolations

julia> testvector = rand(10)
10-element Vector{Float64}:
 0.6057931199864178
 0.43856861325879903
 0.9548698226248755
 0.8378373282482391
 0.8473327381106962
 0.21294308592509092
 0.45082522330659225
 0.8614753487397658
 0.22748963029677682
 0.6711081947725003

julia> testitp1 = linear_interpolation(1:1:10, testvector, extrapolation_bc=(0.0,1.0))
10-element extrapolate(scale(interpolate(::Vector{Float64}, BSpline(Linear())), (1:1:10,)), (0.0, 1.0)) with element type Any:
 0.6057931199864178
 0.43856861325879903
 0.9548698226248755
 0.8378373282482391
 0.8473327381106962
 0.21294308592509092
 0.45082522330659225
 0.8614753487397658
 0.22748963029677682
 0.6711081947725003

julia> testitp1(0.5)
(0.0, 1.0)

So I get back the actual tuple. I tried it like this because we can specify, for example, (Flat(),Linear()) to get different behaviours left and right.

I had a look at the code in Interpolations.jl/filled.jl at master · JuliaMath/Interpolations.jl · GitHub but I couldn’t figure out how to achieve this.

The reason for this is to provide approximations for probability density functions and cumulative density functions. For pdf’s, the single value of 0 is sufficient, for cdf’s I need to put 0 to the left and 1 to the right.

EDIT: As a last resort I know I can define my interpolated function by branches and have it use the left branch with one value and the right branch with another, but I would like to know if there’s something more elegant.

If the goal is to make a CDF, then the testvector should be sorted (as a CDF is monotonic).

Additionally, for a 0.0 value to the left and 1.0 to the right of testvector domain, 0.0 can be added before, and 1.0 after testvector and extrapolation_bc set to Flat().

In code:

julia> testitp1 = linear_interpolation(0:1:11, 
  vcat(0.0, sort(testvector), 1.0), extrapolation_bc=Flat())
12-element extrapolate(...)

julia> using UnicodePlots
julia> lineplot(x->testitp1[x], -4.0, 14.0; xlim=(-4.0,14.0))
          ┌────────────────────────────────────────┐       
        1 │⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡰⠋⠉⠉⠉⠉⠉⠉│ #11(x)
          │⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⠜⠁⠀⠀⠀⠀⠀⠀⠀│       
          │⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⡎⠀⠀⠀⠀⠀⠀⠀⠀⠀│       
          │⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣀⡎⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│       
          │⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│       
          │⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣀⡤⠴⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│       
          │⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⢀⡔⠒⠋⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│       
   f(x)   │⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⠀⣀⠤⠃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│       
          │⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⠀⡏⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│       
          │⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│       
          │⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⠀⡜⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│       
          │⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⢀⠇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│       
          │⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⢸⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│       
          │⠀⠀⠀⠀⠀⠀⠀⠀⢸⠀⠀⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│       
        0 │⣀⣀⣀⣀⣀⣀⣀⣀⣸⡠⠎⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀│       
          └────────────────────────────────────────┘       
          ⠀-4⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀14⠀       
          ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀x⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀       

Hope this helps. There might be better methods to generate CDFs from a sample.

Thanks @Dan . My example is obviously not a cdf, it was just a MWE. The cdf bit was to contextualize.

I was currently defining my cdf by branches, although your solution could be used, but it requires me to also add 2 extra points to the x axis. In this MWE we go from 1:1:10 to 0:1:11, but in generalized context I would have to choose some small spacing, which could be tricky if the grid isn’t constant.

My example shows how to fix the interpolation function on both sides. There really needs to be some knots (in interpolation parlance) on the left and right with the values the extrapolation needs to take.
If the example is not sufficient, maybe the MWE does not capture the whole problem.

Exactly, this is the only thing I was pointing out, as your solution is evidently clean and does the job. It’s just that we might have knots that are not equidistant and we are just performing linear interpolation. In that case, the distance between knots x_{-1} and x_0 (for the left flat extrapolation) and the distance between knots x_{N} and x_{N+1} (for the right flat extrapolation) have to be decided by some kind of rule. If they the distance is too great, we might be inducing a fake slope that doesn’t exist in the original function on the end points whereas a smaller distance would result more like a truncation. The default Flat() methodology is a truncation, if I understand it correctly.

Now that I think about it, your solution never has distance = 0 so it is not a truncation, even with a constant grid, with equidistant knots.