spliner 1.0.0 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
[](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
|