spliner 1.0.0 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -0
- data/.travis.yml +1 -1
- data/Gemfile +4 -0
- data/README.markdown +22 -6
- data/lib/spliner.rb +60 -5
- data/spec/spliner_spec.rb +29 -0
- metadata +3 -1
data/.gitignore
ADDED
data/.travis.yml
CHANGED
data/Gemfile
ADDED
data/README.markdown
CHANGED
@@ -1,27 +1,41 @@
|
|
1
|
+
[![Build Status](https://secure.travis-ci.org/tallakt/spliner.png?branch=master)](http://travis-ci.org/tallakt/spliner)
|
2
|
+
|
1
3
|
Spliner
|
2
4
|
=======
|
3
5
|
|
4
6
|
Spliner is a Ruby library to perform cubic spline interpolation
|
5
7
|
based on provided key points (X1, Y1), (X2, Y2), ... , (Xn,Yn)
|
6
8
|
|
9
|
+
It also supports extrapolation outside the provided range of X
|
10
|
+
values.
|
11
|
+
|
7
12
|
Installation
|
8
13
|
------------
|
9
14
|
|
10
15
|
Spliner requires Ruby 1.9 or later. Install with rubygems:
|
11
16
|
|
12
|
-
|
17
|
+
gem install spliner
|
13
18
|
|
14
19
|
Quick Start
|
15
20
|
-----------
|
16
21
|
|
17
22
|
require 'spliner'
|
18
23
|
|
19
|
-
|
20
|
-
|
24
|
+
# Initialize a spline interpolation with x range 0.0..2.0
|
25
|
+
my_spline = Spliner::Spliner.new({0.0 => 0.0, 1.0 => 1.0, 2.0 => 0.5})
|
26
|
+
|
27
|
+
# Perform interpolation on 31 values ranging from 0..2.0
|
28
|
+
x_values = (0..30).map {|x| x / 30.0 * 2.0 }
|
29
|
+
y_values = x_values.map {|x| my_spline[x] }
|
30
|
+
|
21
31
|
|
22
|
-
|
23
|
-
|
24
|
-
|
32
|
+
# perform extrapolation outside key points using linear Y = aX + b
|
33
|
+
ex_spline = Spliner::Spliner.new({0.0 => 0.0, 1.0 => 1.0, 2.0 => 0.5}, :extrapolate => '10%')
|
34
|
+
xx = ex_spline[2.1] # returns 0.4124999999999999
|
35
|
+
|
36
|
+
# perform extrapolation outside key points using linear Y = aX + b
|
37
|
+
ex_spline = Spliner::Spliner.new({0.0 => 0.0, 1.0 => 1.0, 2.0 => 0.5}, :extrapolate => '10%', :emethod => :hold)
|
38
|
+
xx = ex_spline[2.1] # returns 0.5
|
25
39
|
|
26
40
|
Spliner is based on the interpolation described on this page
|
27
41
|
http://en.wikipedia.org/wiki/Spline_interpolation
|
@@ -59,3 +73,5 @@ License
|
|
59
73
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
60
74
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
61
75
|
|
76
|
+
|
77
|
+
|
data/lib/spliner.rb
CHANGED
@@ -4,7 +4,7 @@
|
|
4
4
|
require 'matrix'
|
5
5
|
|
6
6
|
module Spliner
|
7
|
-
VERSION = '1.0.
|
7
|
+
VERSION = '1.0.1'
|
8
8
|
|
9
9
|
# Spliner::Spliner provides cubic spline interpolation based on provided
|
10
10
|
# key points on a X-Y curve.
|
@@ -20,12 +20,27 @@ module Spliner
|
|
20
20
|
# http://en.wikipedia.org/wiki/Spline_interpolation
|
21
21
|
#
|
22
22
|
class Spliner
|
23
|
+
attr_reader :range
|
24
|
+
|
23
25
|
# Creates a new Spliner::Spliner object to interpolate between
|
24
26
|
# the supplied key points. The key points are provided in a hash where
|
25
27
|
# the key is the X value, and the value is the Y value. The X values
|
26
28
|
# mush be increasing and not duplicate. You must provide at least
|
27
29
|
# two values.
|
28
|
-
|
30
|
+
#
|
31
|
+
# options may take the following keys:
|
32
|
+
#
|
33
|
+
# :extrapolate
|
34
|
+
# Specify an area outside the given X values provided that should return
|
35
|
+
# a valid number. The value may be either a range (eg. -10..110) or a
|
36
|
+
# percentage value written as a string (eg '10%'). Default is no
|
37
|
+
# extrapolation.
|
38
|
+
#
|
39
|
+
# :emethod
|
40
|
+
# Specify a method of extrapolation, one of :linear (continue curve as
|
41
|
+
# a straigt line, default), or :hold (use Y values at the curve endpoints)
|
42
|
+
#
|
43
|
+
def initialize(key_points, options = {})
|
29
44
|
|
30
45
|
@points = key_points
|
31
46
|
@x = @points.keys
|
@@ -58,18 +73,38 @@ module Spliner
|
|
58
73
|
b = vector_helper(tmp)
|
59
74
|
|
60
75
|
@k = a.inv * b
|
76
|
+
|
77
|
+
options[:extrapolate].tap do |ex|
|
78
|
+
case ex
|
79
|
+
when /^\d+(\.\d+)?\s?%$/
|
80
|
+
percentage = ex[/\d+(\.\d+)?/].to_f
|
81
|
+
span = @x.last - @x.first
|
82
|
+
extra = span * percentage * 0.01
|
83
|
+
@range = (@x.first - extra)..(@x.last + extra)
|
84
|
+
when Range
|
85
|
+
@range = ex
|
86
|
+
when nil
|
87
|
+
@range = @x.first..@x.last
|
88
|
+
else
|
89
|
+
raise 'Unable to use extrapolation parameter'
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
@extrapolation_method = options[:emethod] || :linear
|
61
94
|
end
|
62
95
|
|
63
96
|
# returns an interpolated value
|
64
|
-
def get(
|
65
|
-
i = @x_pairs.find_index {|pair| pair.member?
|
97
|
+
def get(v)
|
98
|
+
i = @x_pairs.find_index {|pair| pair.member? v }
|
66
99
|
if i
|
67
100
|
dx = @x[i + 1] - @x[i]
|
68
101
|
dy = @y[i + 1] - @y[i]
|
69
|
-
t = (
|
102
|
+
t = (v - @x[i]) / dx
|
70
103
|
a = @k[i] * dx - dy
|
71
104
|
b = -(@k[i + 1] * dx - dy)
|
72
105
|
(1 - t) * @y[i] + t * @y[i + 1] + t * (1 - t) * (a * (1 - t) + b * t)
|
106
|
+
elsif range.member? v
|
107
|
+
extrapolate(v)
|
73
108
|
else
|
74
109
|
nil
|
75
110
|
end
|
@@ -95,5 +130,25 @@ module Spliner
|
|
95
130
|
end
|
96
131
|
private :check_points_increasing
|
97
132
|
|
133
|
+
# :nodoc:
|
134
|
+
def extrapolate(v)
|
135
|
+
case @extrapolation_method
|
136
|
+
when :hold
|
137
|
+
if v < @x.first
|
138
|
+
@y.first
|
139
|
+
else
|
140
|
+
@y.last
|
141
|
+
end
|
142
|
+
else
|
143
|
+
x, y, k = if v < @x.first
|
144
|
+
[@x.first, @y.first, @k.first]
|
145
|
+
else
|
146
|
+
[@x.last, @y.last, @k[-1]]
|
147
|
+
end
|
148
|
+
y + k * (v - x)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
private :extrapolate
|
152
|
+
|
98
153
|
end
|
99
154
|
end
|
data/spec/spliner_spec.rb
CHANGED
@@ -2,6 +2,8 @@ require 'spliner'
|
|
2
2
|
|
3
3
|
describe Spliner::Spliner do
|
4
4
|
DATASET = {0.0 => 0.0, 1.0 => 1.0, 2.0 => 0.5}
|
5
|
+
KEYS_0_100 = {0.0 => 0.0, 100.0 => 100.0}
|
6
|
+
|
5
7
|
|
6
8
|
it 'should not accept x values that are not increasing' do
|
7
9
|
expect(lambda { Spliner::Spliner.new({0 => 0, 0 => 10})}).to raise_exception
|
@@ -42,4 +44,31 @@ describe Spliner::Spliner do
|
|
42
44
|
expect(s[-1]).to be_nil
|
43
45
|
expect(s[0]).to be_within(0.0001).of(0.0)
|
44
46
|
end
|
47
|
+
|
48
|
+
it 'performs :linear extrapolation outside the data range when such is given' do
|
49
|
+
s = Spliner::Spliner.new KEYS_0_100, :extrapolate => -200..200
|
50
|
+
expect(s.get -110).not_to be_nil
|
51
|
+
expect(s.get -150).to be_within(0.0001).of(-150)
|
52
|
+
expect(s.get 150).to be_within(0.0001).of(150)
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'performs :hold extrapolation' do
|
56
|
+
s = Spliner::Spliner.new KEYS_0_100, :extrapolate => -200..200, :emethod => :hold
|
57
|
+
expect(s.get -150).to be_within(0.0001).of(0)
|
58
|
+
expect(s.get 150).to be_within(0.0001).of(100)
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'supports data ranges given as a string like "10%"' do
|
62
|
+
s1 = Spliner::Spliner.new KEYS_0_100, :extrapolate => '10%'
|
63
|
+
expect(s1.range.first).to be_within(0.0001).of(-10.0)
|
64
|
+
expect(s1.range.last).to be_within(0.0001).of(110.0)
|
65
|
+
|
66
|
+
s2 = Spliner::Spliner.new KEYS_0_100, :extrapolate => '10.0%'
|
67
|
+
expect(s2.range.first).to be_within(0.0001).of(-10.0)
|
68
|
+
expect(s2.range.last).to be_within(0.0001).of(110.0)
|
69
|
+
|
70
|
+
s3 = Spliner::Spliner.new KEYS_0_100, :extrapolate => '10 %'
|
71
|
+
expect(s3.range.first).to be_within(0.0001).of(-10.0)
|
72
|
+
expect(s3.range.last).to be_within(0.0001).of(110.0)
|
73
|
+
end
|
45
74
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: spliner
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -51,7 +51,9 @@ executables: []
|
|
51
51
|
extensions: []
|
52
52
|
extra_rdoc_files: []
|
53
53
|
files:
|
54
|
+
- .gitignore
|
54
55
|
- .travis.yml
|
56
|
+
- Gemfile
|
55
57
|
- README.markdown
|
56
58
|
- Rakefile
|
57
59
|
- lib/spliner.rb
|