savgol 0.2.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.
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