Class: Spliner::Spliner

Inherits:
Object
  • Object
show all
Defined in:
lib/spliner/spliner.rb

Overview

Spliner::Spliner provides cubic spline interpolation based on provided key points on a X-Y curve.

Example

require 'spliner'

# Initialize a spline interpolation with x range 0.0..2.0
my_spline = Spliner::Spliner.new [0.0, 1.0, 2.0], [0.0, 1.0, 0.5]

# Interpolate for a single value
y1 = my_spline[0.5]

# Perform interpolation on 11 values ranging from 0..2.0
y_values = my_spline[(0.0..2.0).step(0.1)]

# You may prefer to use the shortcut class method
y2 = Spliner::Spliner[[0.0, 1.0, 2.0], [0.0, 1.0, 0.5], 0.5]

Algorithm based on en.wikipedia.org/wiki/Spline_interpolation

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(key_points, options) ⇒ Spliner #initialize(x, y, options) ⇒ Spliner

Creates a new Spliner::Spliner object to interpolate between the supplied key points.

The key points shoul be in increaing X order. When duplicate X values are encountered, the spline is split into two or more discontinuous sections.

The extrapolation method may be :linear by default, using a linear extrapolation at the curve ends using the curve derivative at the end points. The :hold method will use the Y value at the nearest end point of the curve.

Overloads:

  • #initialize(key_points, options) ⇒ Spliner

    Parameters:

    • key_points (Hash{Float => Float})

      keys are X values in increasing order, values Y

    • options (Hash)

    Options Hash (options):

    • :extrapolate (Range, String) — default: '0%'

      either a range or percentage, eg ‘10.0%’, or float 0.1

    • :emethod (Symbol) — default: :linear

      extrapolation method

    • :fix_invalid_x (Symbol) — default: false

      delete data points not in increasing order

  • #initialize(x, y, options) ⇒ Spliner

    Parameters:

    • x (Array(Float), Vector)

      the X values of the key points

    • y (Array(Float), Vector)

      the Y values of the key points

    • options (Hash)

    Options Hash (options):

    • :extrapolate (Range, String) — default: '0%'

      either a range or percentage, eg ‘10.0%’, or float 0.1

    • :emethod (Symbol) — default: :linear

      extrapolation method

    • :fix_invalid_x (Symbol) — default: false

      delete data points not in increasing order



57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/spliner/spliner.rb', line 57

def initialize(*param)
  # sort parameters from two alternative initializer signatures
  x, y = nil
  case param.first
  when Array, Vector
    xx,yy, options = param
    x = xx.to_a
    y = yy.to_a
  else
    points, options = param
    x = points.keys
    y = points.values
  end
  options ||= {}

  if options[:fix_invalid_x]
    begin
      size_at_start = x.size
      pp = Hash[x.zip y]
      to_delete = pp.keys.each_cons(2).select {|a,b| b < a}.map(&:last)
      to_delete.each {|k| pp.delete k }
      x = pp.keys
      y = pp.values
    end while x.size < size_at_start
  end

  @sections = split_at_duplicates(x).map {|slice| SplinerSection.new x[slice], y[slice] }

  # Handle extrapolation option parameter
  options[:extrapolate].tap do |ex|
    case ex
    when /^\d+(\.\d+)?\s?%$/
      percentage = ex[/\d+(\.\d+)?/].to_f
      span = x.last - x.first
      extra = span * percentage * 0.01
      @range = (x.first - extra)..(x.last + extra)
    when Range
      @range = ex
    when Float
      span = x.last - x.first
      extra = span * ex
      @range = (x.first - extra)..(x.last + extra)
    when nil
      @range = x.first..x.last
    else
      raise 'Unable to use extrapolation parameter'
    end
  end
  @extrapolation_method = options[:emethod] || :linear
end

Instance Attribute Details

#rangeObject (readonly)

Returns the value of attribute range.



28
29
30
# File 'lib/spliner/spliner.rb', line 28

def range
  @range
end

Class Method Details

.interpolate(points, x, options) ⇒ Object .interpolate(key_x, key_y, x, options) ⇒ Object Also known as: []

shortcut method to instanciate a Spliner::Spliner object and return a series of interpolated values. Options are like Spliner::Spliner#initialize

Overloads:

  • .interpolate(points, x, options) ⇒ Object

    Parameters:

    • points (Hash{Float => Float})

      keys are X values in increasing order, values Y

    • x (Float, Vector, Enumerable(Float))

      X value(s) to interpolate on

    • options (Hash)
  • .interpolate(key_x, key_y, x, options) ⇒ Object

    Parameters:

    • key_x (Array(Float), Vector)

      the X values of the key points

    • x (Float, Vector, Enumerable(Float))

      X value(s) to interpolate on

    • options (Hash)


123
124
125
126
127
128
129
130
131
132
133
# File 'lib/spliner/spliner.rb', line 123

def self.interpolate(*args)
  if (args.first.class == Hash)
    key_points, x, options = args
    s = Spliner.new key_points, (options || {})
    s[x]
  else
    key_x, key_y, x, options = args
    s = Spliner.new key_x, key_y, (options || {})
    s[x]
  end
end

Instance Method Details

#get(*x) ⇒ Object Also known as: []

returns the interpolated Y value(s) at the specified X

Example

my_spline = Spliner::Spliner.new [0.0, 1.0, 2.0], [0.0, 1.0, 0.5]
# get one value
y1 = my_spline.get 0.5
# get many values
y2 = my_spline.get [0.5, 1.5, 2.5]
y3 = my_spline.get 0.5, 1.5, 2.5
# get a range of values
y4 = my_spline.get 1..3
# generate an enumeration of x values
y5 = my_spline.get (1.5..2.5).step(0.5)

Parameters:

  • x (Float, Vector, Enumerable(Float))

    x



165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/spliner/spliner.rb', line 165

def get(*x)
  xx = if x.size == 1
         x.first
       else
         x
       end

  get_func = lambda do |v|
    i = @sections.find_index {|section| section.range.cover? v }
    if i
      @sections[i].get v
    elsif range.cover? v
      extrapolate(v)
    else
      nil
    end
  end

  case xx
  when Vector
     xx.collect {|x| get_func.call(x) }
  when Enumerable
    xx.map {|x| get_func.call(x) }
  else
    get_func.call(xx)
  end
end

#sectionsObject

The number of non-continuous sections used



196
197
198
# File 'lib/spliner/spliner.rb', line 196

def sections
  @sections.size
end