ml 0.1.0

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/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ source :gemcutter
2
+
3
+ # for plotting svg
4
+ gem 'rubyvis'
5
+ gem 'nokogiri'
6
+
7
+ group :development do
8
+ gem "bacon", ">= 0"
9
+ gem "yard", "~> 0.6.0"
10
+ gem "bundler", "~> 1.0.0"
11
+ gem "jeweler", "~> 1.6.4"
12
+ gem "rcov", ">= 0"
13
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,26 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ bacon (1.1.0)
5
+ git (1.2.5)
6
+ jeweler (1.6.4)
7
+ bundler (~> 1.0)
8
+ git (>= 1.2.5)
9
+ rake
10
+ nokogiri (1.5.0)
11
+ rake (0.9.2)
12
+ rcov (0.9.10)
13
+ rubyvis (0.5.0)
14
+ yard (0.6.8)
15
+
16
+ PLATFORMS
17
+ ruby
18
+
19
+ DEPENDENCIES
20
+ bacon
21
+ bundler (~> 1.0.0)
22
+ jeweler (~> 1.6.4)
23
+ nokogiri
24
+ rcov
25
+ rubyvis
26
+ yard (~> 0.6.0)
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Andrew Liu
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,18 @@
1
+ # ml
2
+
3
+ Machine learning library for ruby
4
+
5
+ # Algorithm Implemented
6
+
7
+ * Perceptron Learning Algorithm
8
+
9
+ # Tools
10
+
11
+ * Data generator
12
+ * Data plotter
13
+
14
+ # Copyright
15
+
16
+ Copyright (c) 2011 Andrew Liu. See LICENSE.txt for
17
+ further details.
18
+
data/Rakefile ADDED
@@ -0,0 +1,46 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "ml"
18
+ gem.homepage = "http://github.com/eggegg/ml"
19
+ gem.license = "MIT"
20
+ gem.summary = %Q{Machine learning library for Ruby}
21
+ gem.description = %Q{Machine learning library in Ruby}
22
+ gem.email = "andrewliu33@gmail.com"
23
+ gem.authors = ["Andrew Liu"]
24
+ # dependencies defined in Gemfile
25
+ end
26
+ Jeweler::RubygemsDotOrgTasks.new
27
+
28
+ require 'rake/testtask'
29
+ Rake::TestTask.new(:spec) do |spec|
30
+ spec.libs << 'lib' << 'spec'
31
+ spec.pattern = 'spec/**/*_spec.rb'
32
+ spec.verbose = true
33
+ end
34
+
35
+ require 'rcov/rcovtask'
36
+ Rcov::RcovTask.new do |spec|
37
+ spec.libs << 'spec'
38
+ spec.pattern = 'spec/**/*_spec.rb'
39
+ spec.verbose = true
40
+ spec.rcov_opts << '--exclude "gems/*"'
41
+ end
42
+
43
+ task :default => :spec
44
+
45
+ #require 'yard'
46
+ #YARD::Rake::YardocTask.new
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,98 @@
1
+ require 'matrix'
2
+
3
+ module ML
4
+ # Generating sample points on 2D plane
5
+ class Generator2D
6
+ # Generate point from line
7
+ #
8
+ # @param [Array] [a,b,c] for ax+by+c=0
9
+ # @param [Number] x value
10
+ # @return [Array]
11
+ def self.point_from_line coef, x
12
+ [x, (-coef[2]-(coef[0] * x))/coef[1]]
13
+ end
14
+
15
+ # Initialize a generator
16
+ #
17
+ # @param [Integer] x range
18
+ # @param [Integer] y range
19
+ def initialize x_range = 100, y_range = 100
20
+ @x_range = x_range
21
+ @y_range = y_range
22
+ end
23
+
24
+ # Generate two groups of points on 2d plain
25
+ #
26
+ # @param [Integer] the number of points of each set
27
+ # @param [Array] [a,b,c] for ax+by+c=0
28
+ # @return [Array] two sets of points
29
+ def points_2d points, coef = [-1.0, 1.0, 0.0]
30
+ result = []
31
+ # for each group
32
+ [1, -1].each do |grp|
33
+ result << []
34
+
35
+ points.times do
36
+ while true
37
+ point = generate_point
38
+ prod = Matrix.column_vector(point).transpose * Matrix.column_vector(coef)
39
+ if (prod[0,0] <=> 0) == grp
40
+ result[-1] << point
41
+ break
42
+ end
43
+ end
44
+ end
45
+ end
46
+ result
47
+ end
48
+
49
+ private
50
+ def generate_point
51
+ [@x_range * rand, @y_range * rand, 1.0]
52
+ end
53
+ end
54
+
55
+ # General generator for n-dimentional space
56
+ class Generator
57
+ # Initial generator
58
+ #
59
+ # @param [Integer] dimension
60
+ def initialize dim
61
+ @dim = dim
62
+ end
63
+
64
+ # Generate two groups of points
65
+ #
66
+ # @param [Integer] the number of points of each set
67
+ # @param [Array] array of the size of dimension to specify the hyper plane
68
+ # @return [Array] two sets of points
69
+ def points points, coef
70
+ result = []
71
+ # for each group
72
+ [1, -1].each do |grp|
73
+ result << []
74
+
75
+ points.times do
76
+ while true
77
+ point = Generator.generate_vector(@dim)
78
+ prod = Matrix.column_vector(point).transpose * Matrix.column_vector(coef)
79
+ if (prod[0,0] <=> 0) == grp
80
+ result[-1] << point
81
+ break
82
+ end
83
+ end
84
+ end
85
+ end
86
+ result
87
+ end
88
+
89
+ # Generating a random vector
90
+ #
91
+ # @param [Integer] the dimension of the vector
92
+ # @return [Array] random vector
93
+ def self.generate_vector dim
94
+ result = Array.new(dim) { rand - 0.5 }
95
+ result << 1.0
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,27 @@
1
+ module ML
2
+ # Parser for traing/testing data
3
+ class Parser
4
+ # Parse the vector file with supervised result
5
+ #
6
+ # @return [Hash] map from data to supervised result
7
+ def parse_supervised filename
8
+ result = {}
9
+ lines = IO.readlines(filename)
10
+
11
+ lines.each do |line|
12
+ splitted = line.split.map(&:to_f)
13
+ result[splitted[1..-1] + [1.0]] = splitted[0]
14
+ end
15
+
16
+ result
17
+ end
18
+
19
+ # Parse the vector file
20
+ #
21
+ # @return [Array] array of vectors
22
+ def parse_unsupervised filename
23
+ lines = IO.readlines(filename)
24
+ lines.map {|line| line.split.map(&:to_f) }
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,77 @@
1
+ require 'rubyvis'
2
+
3
+ module ML
4
+ # Plotting the data to svg
5
+ class Plotter
6
+ # Initializer of plotter
7
+ #
8
+ # @param [Integer] x value range
9
+ # @param [Integer] y value range
10
+ # @param [Integer] x plot size
11
+ # @param [Integer] y plot size
12
+ def initialize x_range = 100, y_range = 100, x_size = 100, y_size = 100
13
+ @x_range = x_range
14
+ @y_range = y_range
15
+ @x_size = x_size
16
+ @y_size = y_size
17
+
18
+ @x = pv.Scale.linear(0, @x_range).range(0, @x_size)
19
+ @y = pv.Scale.linear(0, @y_range).range(0, @y_size)
20
+
21
+ @vis = pv.Panel.new.width(@x_size).height(@y_size)
22
+ .bottom(20).left(20).right(10).top(5)
23
+
24
+ @vis.add(pv.Rule).data(@y.ticks()).bottom(@y)
25
+ .stroke_style(lambda {|d| d!=0 ? "#eee" : "#000"})
26
+ .anchor("left").add(pv.Label)
27
+ .visible(lambda {|d| d > 0 and d < x_range})
28
+ .text(@y.tick_format)
29
+
30
+ @vis.add(pv.Rule).data(@x.ticks()).left(@x)
31
+ .stroke_style(lambda {|d| d!=0 ? "#eee" : "#000"})
32
+ .anchor("bottom").add(pv.Label)
33
+ .visible(lambda {|d| d > 0 and d < y_range})
34
+ .text(@x.tick_format)
35
+
36
+ yield(self) if block_given?
37
+ end
38
+
39
+ # Plotting points with shape and color
40
+ #
41
+ # @param [Array] points to plot
42
+ # @param [String] shape of the points
43
+ # @param [String] color of the points
44
+ def dot points, shape = "circle", color = "#000"
45
+ # FIXME: dirty hack for instance_exec
46
+ x = @x
47
+ y = @y
48
+
49
+ @vis.add(pv.Dot).data(points)
50
+ .left(lambda {|d| x.scale(d[0])})
51
+ .bottom(lambda {|d| y.scale(d[1])})
52
+ .shape(shape)
53
+ .stroke_style(color)
54
+ end
55
+
56
+ # Plotting line with color
57
+ #
58
+ # @param [Array] 2 points which represents line
59
+ def line points, color = "#000"
60
+ x = @x
61
+ y = @y
62
+
63
+ @vis.add(pv.Line).data(points)
64
+ .left(lambda {|d| x.scale(d[0])})
65
+ .bottom(lambda {|d| y.scale(d[1])})
66
+ .stroke_style(color)
67
+ end
68
+
69
+ # Convert to svg
70
+ #
71
+ # @return [String] svg plot
72
+ def to_svg
73
+ @vis.render
74
+ @vis.to_svg
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,66 @@
1
+ require 'matrix'
2
+
3
+ module ML
4
+ # Implementation of Perceptron Learning Algorithm
5
+ class PerceptronLearner
6
+ # Initialize a perceptron learner
7
+ #
8
+ # @param [Integer] the number of dimension
9
+ def initialize dim
10
+ @dim = dim
11
+ @w = Matrix.column_vector(Array.new(dim + 1, 0))
12
+ end
13
+
14
+ # Train with supervised data
15
+ #
16
+ # @param [Hash] supervised input data (mapping from array to integer)
17
+ # @return [PerceptronLearner] self object
18
+ def train! data
19
+ @update = 0
20
+ while true
21
+ misclassified = false
22
+
23
+ for dat, result in data
24
+ aug_data = Matrix.column_vector(dat)
25
+
26
+ if classify(aug_data) != result
27
+ misclassified = true
28
+
29
+ @w = @w + result * aug_data
30
+ @update += 1
31
+ break
32
+ end
33
+ end
34
+
35
+ break unless misclassified
36
+ end
37
+ end
38
+
39
+ # The final coefficient of the line
40
+ #
41
+ # @return [line] [a,b,c] for ax+by+c=0
42
+ def line
43
+ @w.column(0).to_a
44
+ end
45
+
46
+ # The number for updates
47
+ #
48
+ # @return [Integer] update count
49
+ def update_count
50
+ @update
51
+ end
52
+
53
+ # Predict certain data
54
+ #
55
+ # @param [Array] data in question
56
+ # @param [Integer] prediction
57
+ def predict data
58
+ classify Matrix.column_vector(data + [1.0])
59
+ end
60
+
61
+ private
62
+ def classify data
63
+ (@w.transpose * data)[0,0] <=> 0
64
+ end
65
+ end
66
+ end
data/lib/ml.rb ADDED
@@ -0,0 +1,13 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ require 'data/plotter'
5
+ require 'data/generator'
6
+ require 'data/parser'
7
+
8
+ require 'method/perceptron'
9
+
10
+ module ML
11
+ end
12
+
13
+ MachingLearning = ML
data/ml.gemspec ADDED
@@ -0,0 +1,71 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "ml"
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Andrew Liu"]
12
+ s.date = "2011-10-03"
13
+ s.description = "Machine learning library in Ruby"
14
+ s.email = "andrewliu33@gmail.com"
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.md"
18
+ ]
19
+ s.files = [
20
+ "Gemfile",
21
+ "Gemfile.lock",
22
+ "LICENSE.txt",
23
+ "README.md",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "lib/data/generator.rb",
27
+ "lib/data/parser.rb",
28
+ "lib/data/plotter.rb",
29
+ "lib/method/perceptron.rb",
30
+ "lib/ml.rb",
31
+ "ml.gemspec",
32
+ "spec/learning_spec.rb",
33
+ "spec/spec_helper.rb"
34
+ ]
35
+ s.homepage = "http://github.com/eggegg/ml"
36
+ s.licenses = ["MIT"]
37
+ s.require_paths = ["lib"]
38
+ s.rubygems_version = "1.8.10"
39
+ s.summary = "Machine learning library for Ruby"
40
+
41
+ if s.respond_to? :specification_version then
42
+ s.specification_version = 3
43
+
44
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
45
+ s.add_runtime_dependency(%q<rubyvis>, [">= 0"])
46
+ s.add_runtime_dependency(%q<nokogiri>, [">= 0"])
47
+ s.add_development_dependency(%q<bacon>, [">= 0"])
48
+ s.add_development_dependency(%q<yard>, ["~> 0.6.0"])
49
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
50
+ s.add_development_dependency(%q<jeweler>, ["~> 1.6.4"])
51
+ s.add_development_dependency(%q<rcov>, [">= 0"])
52
+ else
53
+ s.add_dependency(%q<rubyvis>, [">= 0"])
54
+ s.add_dependency(%q<nokogiri>, [">= 0"])
55
+ s.add_dependency(%q<bacon>, [">= 0"])
56
+ s.add_dependency(%q<yard>, ["~> 0.6.0"])
57
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
58
+ s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
59
+ s.add_dependency(%q<rcov>, [">= 0"])
60
+ end
61
+ else
62
+ s.add_dependency(%q<rubyvis>, [">= 0"])
63
+ s.add_dependency(%q<nokogiri>, [">= 0"])
64
+ s.add_dependency(%q<bacon>, [">= 0"])
65
+ s.add_dependency(%q<yard>, ["~> 0.6.0"])
66
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
67
+ s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
68
+ s.add_dependency(%q<rcov>, [">= 0"])
69
+ end
70
+ end
71
+
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Learning" do
4
+ it "fails" do
5
+ should.flunk "hey buddy, you should probably rename this file and start specing for real"
6
+ end
7
+ end
@@ -0,0 +1,16 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'bacon'
11
+
12
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
13
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
14
+ require 'learning'
15
+
16
+ Bacon.summary_on_exit
metadata ADDED
@@ -0,0 +1,141 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ml
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Andrew Liu
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2011-10-03 00:00:00.000000000Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rubyvis
16
+ requirement: &2154897000 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *2154897000
25
+ - !ruby/object:Gem::Dependency
26
+ name: nokogiri
27
+ requirement: &2154896440 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *2154896440
36
+ - !ruby/object:Gem::Dependency
37
+ name: bacon
38
+ requirement: &2154895820 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *2154895820
47
+ - !ruby/object:Gem::Dependency
48
+ name: yard
49
+ requirement: &2154895300 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 0.6.0
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *2154895300
58
+ - !ruby/object:Gem::Dependency
59
+ name: bundler
60
+ requirement: &2154894780 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ~>
64
+ - !ruby/object:Gem::Version
65
+ version: 1.0.0
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *2154894780
69
+ - !ruby/object:Gem::Dependency
70
+ name: jeweler
71
+ requirement: &2154894220 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ~>
75
+ - !ruby/object:Gem::Version
76
+ version: 1.6.4
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: *2154894220
80
+ - !ruby/object:Gem::Dependency
81
+ name: rcov
82
+ requirement: &2154893720 !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: *2154893720
91
+ description: Machine learning library in Ruby
92
+ email: andrewliu33@gmail.com
93
+ executables: []
94
+ extensions: []
95
+ extra_rdoc_files:
96
+ - LICENSE.txt
97
+ - README.md
98
+ files:
99
+ - Gemfile
100
+ - Gemfile.lock
101
+ - LICENSE.txt
102
+ - README.md
103
+ - Rakefile
104
+ - VERSION
105
+ - lib/data/generator.rb
106
+ - lib/data/parser.rb
107
+ - lib/data/plotter.rb
108
+ - lib/method/perceptron.rb
109
+ - lib/ml.rb
110
+ - ml.gemspec
111
+ - spec/learning_spec.rb
112
+ - spec/spec_helper.rb
113
+ homepage: http://github.com/eggegg/ml
114
+ licenses:
115
+ - MIT
116
+ post_install_message:
117
+ rdoc_options: []
118
+ require_paths:
119
+ - lib
120
+ required_ruby_version: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ segments:
127
+ - 0
128
+ hash: 3039846434950023552
129
+ required_rubygems_version: !ruby/object:Gem::Requirement
130
+ none: false
131
+ requirements:
132
+ - - ! '>='
133
+ - !ruby/object:Gem::Version
134
+ version: '0'
135
+ requirements: []
136
+ rubyforge_project:
137
+ rubygems_version: 1.8.10
138
+ signing_key:
139
+ specification_version: 3
140
+ summary: Machine learning library for Ruby
141
+ test_files: []