savgol 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 74cb6ba4eb41d56918d8e0994ecab490234bae85
4
+ data.tar.gz: 60e5ce50c918ac33ccf028a9e26773d06d59e7af
5
+ SHA512:
6
+ metadata.gz: 9698cb0c54a48b474f27badfa100b65ca92857829f59ed38ed304c5e9ed4428e28dbf260c6829731c5dbef197279d4796212d0ce9ece1252cbaa809a1fb0730e
7
+ data.tar.gz: cabf344682dbe30bd2d1beb1209dbdbf7530fcadfd70079fcd5edb967fcbd41bce049235c7d2f9482e10d007bd435f3afe2f387b5390163940a97cfe748c8850
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.gitignore ADDED
@@ -0,0 +1,49 @@
1
+ # rcov generated
2
+ coverage
3
+ coverage.data
4
+
5
+ # rdoc generated
6
+ rdoc
7
+
8
+ # yard generated
9
+ doc
10
+ .yardoc
11
+
12
+ # bundler
13
+ .bundle
14
+
15
+ # jeweler generated
16
+ pkg
17
+
18
+ # Have editor/IDE/OS specific files you need to ignore? Consider using a global gitignore:
19
+ #
20
+ # * Create a file at ~/.gitignore
21
+ # * Include files you want ignored
22
+ # * Run: git config --global core.excludesfile ~/.gitignore
23
+ #
24
+ # After doing this, these files will be ignored in all your git projects,
25
+ # saving you from having to 'pollute' every project you touch with them
26
+ #
27
+ # Not sure what to needs to be ignored for particular editors/OSes? Here's some ideas to get you started. (Remember, remove the leading # of the line)
28
+ #
29
+ # For MacOS:
30
+ #
31
+ #.DS_Store
32
+
33
+ # For TextMate
34
+ #*.tmproj
35
+ #tmtags
36
+
37
+ # For emacs:
38
+ #*~
39
+ #\#*
40
+ #.\#*
41
+
42
+ # For vim:
43
+ #*.swp
44
+
45
+ # For redcar:
46
+ #.redcar
47
+
48
+ # For rubinius:
49
+ #*.rbc
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in binneroc.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2012 Brigham Young University
2
+ authored by: John T. Prince
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining
5
+ a copy of this software and associated documentation files (the
6
+ "Software"), to deal in the Software without restriction, including
7
+ without limitation the rights to use, copy, modify, merge, publish,
8
+ distribute, sublicense, and/or sell copies of the Software, and to
9
+ permit persons to whom the Software is furnished to do so, subject to
10
+ the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,28 @@
1
+ # savgol
2
+
3
+ Provides implementations of Savitzky-Golay smoothing (filtering).
4
+
5
+ The gem is based on the [scipy implementation](http://www.scipy.org/Cookbook/SavitzkyGolay). A good explanation of the process may be found [here on stackexchange](http://dsp.stackexchange.com/a/9494).
6
+
7
+ # Examples
8
+
9
+ ## Array implementation
10
+
11
+ require 'savgol/array'
12
+ data = [1, 2, 3, 4, 3.5, 5, 3, 2.2, 3, 0, -1, 2, 0, -2, -5, -8, -7, -2, 0, 1, 1]
13
+ # window size = 5, polynomial order = 3
14
+ data.savgol(5,3)
15
+
16
+ # Installation
17
+
18
+ gem install savgol
19
+
20
+ # TODO
21
+
22
+ * Implement for Scriruby's Nmatrix and NArray.
23
+ * Implement smoothing for unevenly sampled data.
24
+
25
+ # Copyright
26
+
27
+ MIT license. See LICENSE.txt.
28
+
data/Rakefile ADDED
@@ -0,0 +1,19 @@
1
+ require "bundler/gem_tasks"
2
+ require_relative "lib/savgol/version"
3
+
4
+ require 'rspec/core'
5
+ require 'rspec/core/rake_task'
6
+ RSpec::Core::RakeTask.new(:spec) do |spec|
7
+ spec.pattern = FileList['spec/**/*_spec.rb']
8
+ end
9
+
10
+ task :default => :spec
11
+
12
+ require 'rdoc/task'
13
+ Rake::RDocTask.new do |rdoc|
14
+ version = Savgol::VERSION
15
+ rdoc.rdoc_dir = 'rdoc'
16
+ rdoc.title = "savgol #{version}"
17
+ rdoc.rdoc_files.include('README*')
18
+ rdoc.rdoc_files.include('lib/**/*.rb')
19
+ end
data/lib/savgol.rb ADDED
@@ -0,0 +1,13 @@
1
+ require 'savgol/version'
2
+ require 'savgol/array'
3
+
4
+ module Savgol
5
+ end
6
+
7
+ # ar = [1, 2, 3, 4, 3.5, 5, 3, 2.2, 3, 0, -1, 2, 0, -2, -5, -8, -7, -2, 0, 1, 1]
8
+ # numpy_savgol_output = [1.0, 2.0, 3.12857143, 3.57142857, 4.27142857, 4.12571429, 3.36857143, 2.69714286, 2.04, 0.32571429, -0.05714286, 0.8, 0.51428571, -2.17142857, -5.25714286, -7.65714286, -6.4, -2.77142857, 0.17142857, 0.91428571, 1.0]
9
+ # sg = ar.savgol(5,3)
10
+ #
11
+ # sg.zip(numpy_savgol_output) do |sgv, numpy_sgv|
12
+ # p "#{sgv} vs #{numpy_sgv} diff #{(sgv - numpy_sgv).abs.round(8)}"
13
+ # end
@@ -0,0 +1,57 @@
1
+ require "matrix"
2
+
3
+ module Savgol
4
+
5
+ def savgol(window_size, order, deriv=0, check_args=false)
6
+
7
+ # check arguments
8
+ if !window_size.is_a?(Integer) || window_size.abs != window_size || window_size % 2 != 1 || window_size < 1
9
+ raise ArgumentError, "window_size size must be a positive odd integer"
10
+ end
11
+ if !order.is_a?(Integer) || order < 0
12
+ raise ArgumentError, "order must be an integer >= 0"
13
+ end
14
+ if window_size < order + 2
15
+ raise ArgumentError, "window_size is too small for the polynomials order"
16
+ end
17
+
18
+ half_window = (window_size -1) / 2
19
+ weights = sg_weights(half_window, order, deriv)
20
+ ar = sg_pad_ends(half_window)
21
+ sg_convolve(ar, weights)
22
+ end
23
+
24
+ def sg_convolve(data, weights, mode=:valid)
25
+ data.each_cons(weights.size).map do |ar|
26
+ ar.zip(weights).map {|pair| pair[0] * pair[1] }.reduce(:+)
27
+ end
28
+ end
29
+
30
+ # pads the ends with the reverse, geometric inverse sequence
31
+ def sg_pad_ends(half_window)
32
+ start = self[1..half_window]
33
+ start.reverse!
34
+ start.map! {|v| self[0] - (v - self[0]).abs }
35
+
36
+ fin = self[(-half_window-1)...-1]
37
+ fin.reverse!
38
+ fin.map! {|v| self[-1] + (v - self[-1]).abs }
39
+ start.push(*self, *fin)
40
+ end
41
+
42
+ # returns an object that will convolve with the padded array
43
+ def sg_weights(half_window, order, deriv=0)
44
+ # byebug
45
+ mat = Matrix[ *(-half_window..half_window).map {|k| (0..order).map {|i| k**i }} ]
46
+ # Moore-Penrose psuedo-inverse without SVD (not so precize)
47
+ # A' = (A.t * A)^-1 * A.t
48
+ pinv_matrix = Matrix[*(mat.transpose*mat).to_a].inverse * Matrix[*mat.to_a].transpose
49
+ pinv = Matrix[*pinv_matrix.to_a]
50
+ pinv.row(deriv).to_a
51
+ end
52
+
53
+ end
54
+
55
+ class Array
56
+ include Savgol
57
+ end
@@ -0,0 +1,3 @@
1
+ module Savgol
2
+ VERSION = "0.2.1"
3
+ end
data/savgol.gemspec ADDED
@@ -0,0 +1,37 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'savgol/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "savgol"
8
+ spec.version = Savgol::VERSION
9
+ spec.authors = ["John T. Prince", "Ondra Beneš"]
10
+ spec.email = ["jtprince@gmail.com"]
11
+ spec.summary = %q{performs Savitzky-Golay smoothing}
12
+ spec.description = %q{Extends Array class with method which calculates applies Savitzky-Golay filter used for smoothing the data}
13
+ spec.homepage = "http://github.com/princelab/savgol"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ [
22
+ #["trollop", "~> 2.0.0"],
23
+ ].each do |args|
24
+ spec.add_dependency(*args)
25
+ end
26
+
27
+ [
28
+ ["bundler", "~> 1.5.1"],
29
+ ["rake"],
30
+ ["rspec", "~> 2.14.1"],
31
+ ["rdoc", "~> 4.1.0"],
32
+ ["simplecov", "~> 0.8.2"],
33
+ ].each do |args|
34
+ spec.add_development_dependency(*args)
35
+ end
36
+
37
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ require 'savgol_shared_example'
4
+
5
+ require 'savgol/array'
6
+
7
+ describe Array do
8
+ it_behaves_like "a savgol smoother"
9
+ end
@@ -0,0 +1,89 @@
1
+ shared_examples "a savgol smoother" do
2
+ let(:smoother) do
3
+ object = described_class[1, 2, 3, 4, -7, -2, 0, 1, 1]
4
+ object.extend(Savgol)
5
+ object
6
+ end
7
+
8
+ it 'pads with the reverse geometrically inverted sequence' do
9
+ expect(smoother.sg_pad_ends(2)).to eq [-1, 0, 1, 2, 3, 4, -7, -2, 0, 1, 1, 1, 2]
10
+ expect(smoother.sg_pad_ends(3)).to eq [-2, -1, 0, 1, 2, 3, 4, -7, -2, 0, 1, 1, 1, 2, 4]
11
+ end
12
+
13
+ describe 'smoothing a signal' do
14
+ let(:smoother) do
15
+ object = described_class[1, 2, 3, 4, 3.5, 5, 3, 2.2, 3, 0, -1, 2, 0, -2, -5, -8, -7, -2, 0, 1, 1]
16
+ object.extend(Savgol)
17
+ object
18
+ end
19
+
20
+ it "works for the simple case" do
21
+ numpy_savgol_output = [1.0, 2.0, 3.12857143, 3.57142857, 4.27142857, 4.12571429, 3.36857143, 2.69714286, 2.04, 0.32571429, -0.05714286, 0.8, 0.51428571, -2.17142857, -5.25714286, -7.65714286, -6.4, -2.77142857, 0.17142857, 0.91428571, 1.0]
22
+ sg = smoother.savgol(5,3)
23
+ sg.size.should == numpy_savgol_output.size
24
+
25
+ numpy_savgol_output.each_with_index do |exp, i|
26
+ expect(sg[i]).to be_within(0.000001).of(exp)
27
+ end
28
+ end
29
+ end
30
+
31
+ end
32
+
33
+ =begin
34
+ b = matrix([[ 1, -2, 4, -8],
35
+ [ 1, -1, 1, -1],
36
+ [ 1, 0, 0, 0],
37
+ [ 1, 1, 1, 1],
38
+ [ 1, 2, 4, 8]])
39
+
40
+ # b = np.matrix([[ 1, -2, 4, -8],[ 1, -1, 1, -1],[ 1, 0, 0, 0],[ 1, 1, 1, 1],[ 1, 2, 4, 8]])
41
+
42
+ np.linalg.pinv(b)
43
+ matrix([[ -8.57142857e-02, 3.42857143e-01, 4.85714286e-01, 3.42857143e-01, -8.57142857e-02],
44
+ [ 8.33333333e-02, -6.66666667e-01, 6.17335241e-17, 6.66666667e-01, -8.33333333e-02],
45
+ [ 1.42857143e-01, -7.14285714e-02, -1.42857143e-01, -7.14285714e-02, 1.42857143e-01],
46
+ [ -8.33333333e-02, 1.66666667e-01, -8.12290576e-18, -1.66666667e-01, 8.33333333e-02]])
47
+
48
+ [[-0.08571422965620183, 0.34285713252351957, 0.4857142762115429, 0.3428571132587409, -0.08571425328426403],
49
+ [0.08333333127000826, -0.6666665420778334, -1.4769869399753408e-08, 0.6666665456225483, -0.08333341040935828],
50
+ [0.14285713578428869, -0.07142855323765136, -0.1428571582020276, -0.07142860273009285, 0.14285714667073207],
51
+ [-0.08333333356934626, 0.16666666384543039, 5.837836155482957e-11, -0.16666665040013168, 0.0833333492046153]]
52
+
53
+
54
+
55
+ np.linalg.pinv(b).A
56
+ array([[ -8.57142857e-02, 3.42857143e-01, 4.85714286e-01,
57
+ 3.42857143e-01, -8.57142857e-02],
58
+ [ 8.33333333e-02, -6.66666667e-01, 6.17335241e-17,
59
+ 6.66666667e-01, -8.33333333e-02],
60
+ [ 1.42857143e-01, -7.14285714e-02, -1.42857143e-01,
61
+ -7.14285714e-02, 1.42857143e-01],
62
+ [ -8.33333333e-02, 1.66666667e-01, -8.12290576e-18,
63
+ -1.66666667e-01, 8.33333333e-02]])
64
+
65
+ m = array([-0.08571429, 0.34285714, 0.48571429, 0.34285714, -0.08571429])
66
+
67
+ with
68
+
69
+ window_size = 5
70
+ half_window = 2
71
+ firstvals = array([-1., 0.])
72
+ lastvals = array([ 1., 2.])
73
+ concat array = array([-1. , 0. , 1. , 2. , 3. , 4. , 3.5, 5. , 3. , 2.2, 3. , 0. , -1. , 2. , 0. , -2. , -5. , -8. , -7. , -2. , 0. , 1. , 1. , 1. , 2. ])
74
+
75
+ with window_size = 7
76
+ half_window = 3
77
+ firstvals = array([-2., -1., 0.])
78
+ lastvals = array([ 1., 2., 4.])
79
+ concat array = array([-2. , -1. , 0. , 1. , 2. , 3. , 4. , 3.5, 5. , 3. , 2.2, 3. , 0. , -1. , 2. , 0. , -2. , -5. , -8. , -7. , -2. , 0. , 1. , 1. , 1. , 2. , 4. ])
80
+
81
+ The final output:
82
+ savgol
83
+ array([ 1. , 2. , 3.12857143, 3.57142857, 4.27142857,
84
+ 4.12571429, 3.36857143, 2.69714286, 2.04 , 0.32571429,
85
+ -0.05714286, 0.8 , 0.51428571, -2.17142857, -5.25714286,
86
+ -7.65714286, -6.4 , -2.77142857, 0.17142857, 0.91428571, 1. ])
87
+
88
+ =end
89
+
@@ -0,0 +1,9 @@
1
+ require 'simplecov'
2
+ SimpleCov.start
3
+
4
+ require 'rspec'
5
+
6
+ RSpec.configure do |config|
7
+ config.treat_symbols_as_metadata_keys_with_true_values = true
8
+ config.color = true
9
+ end
metadata ADDED
@@ -0,0 +1,133 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: savgol
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.1
5
+ platform: ruby
6
+ authors:
7
+ - John T. Prince
8
+ - Ondra Beneš
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-03-27 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ~>
19
+ - !ruby/object:Gem::Version
20
+ version: 1.5.1
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ~>
26
+ - !ruby/object:Gem::Version
27
+ version: 1.5.1
28
+ - !ruby/object:Gem::Dependency
29
+ name: rake
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - '>='
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - '>='
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: rspec
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ~>
47
+ - !ruby/object:Gem::Version
48
+ version: 2.14.1
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ~>
54
+ - !ruby/object:Gem::Version
55
+ version: 2.14.1
56
+ - !ruby/object:Gem::Dependency
57
+ name: rdoc
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ~>
61
+ - !ruby/object:Gem::Version
62
+ version: 4.1.0
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: 4.1.0
70
+ - !ruby/object:Gem::Dependency
71
+ name: simplecov
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ~>
75
+ - !ruby/object:Gem::Version
76
+ version: 0.8.2
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ~>
82
+ - !ruby/object:Gem::Version
83
+ version: 0.8.2
84
+ description: Extends Array class with method which calculates applies Savitzky-Golay
85
+ filter used for smoothing the data
86
+ email:
87
+ - jtprince@gmail.com
88
+ executables: []
89
+ extensions: []
90
+ extra_rdoc_files: []
91
+ files:
92
+ - .document
93
+ - .gitignore
94
+ - .rspec
95
+ - Gemfile
96
+ - LICENSE.txt
97
+ - README.md
98
+ - Rakefile
99
+ - lib/savgol.rb
100
+ - lib/savgol/array.rb
101
+ - lib/savgol/version.rb
102
+ - savgol.gemspec
103
+ - spec/savgol/array_spec.rb
104
+ - spec/savgol_shared_example.rb
105
+ - spec/spec_helper.rb
106
+ homepage: http://github.com/princelab/savgol
107
+ licenses:
108
+ - MIT
109
+ metadata: {}
110
+ post_install_message:
111
+ rdoc_options: []
112
+ require_paths:
113
+ - lib
114
+ required_ruby_version: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - '>='
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ required_rubygems_version: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - '>='
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ requirements: []
125
+ rubyforge_project:
126
+ rubygems_version: 2.0.14
127
+ signing_key:
128
+ specification_version: 4
129
+ summary: performs Savitzky-Golay smoothing
130
+ test_files:
131
+ - spec/savgol/array_spec.rb
132
+ - spec/savgol_shared_example.rb
133
+ - spec/spec_helper.rb