pest 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +6 -0
- data/Gemfile +13 -0
- data/README.md +90 -0
- data/Rakefile +53 -0
- data/VERSION +1 -0
- data/lib/pest.rb +23 -0
- data/lib/pest/data_set.rb +62 -0
- data/lib/pest/data_set/hash.rb +70 -0
- data/lib/pest/data_set/narray.rb +98 -0
- data/lib/pest/estimator.rb +72 -0
- data/lib/pest/estimator/frequency.rb +61 -0
- data/lib/pest/function.rb +28 -0
- data/lib/pest/function/entropy.rb +35 -0
- data/lib/pest/function/probability.rb +41 -0
- data/lib/pest/variable.rb +34 -0
- data/lib/pest/version.rb +7 -0
- data/pest.gemspec +112 -0
- data/spec/pest/data_set/hash_spec.rb +108 -0
- data/spec/pest/data_set/narray_spec.rb +141 -0
- data/spec/pest/data_set_spec.rb +95 -0
- data/spec/pest/estimator/bernoulli_spec.rb +21 -0
- data/spec/pest/estimator/frequency_spec.rb +85 -0
- data/spec/pest/estimator/gaussian_spec.rb +21 -0
- data/spec/pest/estimator/multinomial_spec.rb +21 -0
- data/spec/pest/estimator/parzen_spec.rb +21 -0
- data/spec/pest/estimator/svd_spec.rb +21 -0
- data/spec/pest/estimator_spec.rb +74 -0
- data/spec/pest/function/entropy_spec.rb +105 -0
- data/spec/pest/function/probability_spec.rb +118 -0
- data/spec/pest/variable_spec.rb +73 -0
- data/spec/pest_spec.rb +4 -0
- data/spec/spec_helper.rb +14 -0
- metadata +321 -0
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class TestClass
|
4
|
+
include Pest::DataSet
|
5
|
+
end
|
6
|
+
|
7
|
+
describe Pest::DataSet do
|
8
|
+
before(:each) { @instance = TestClass.new }
|
9
|
+
|
10
|
+
describe Pest::DataSet::ClassMethods do
|
11
|
+
before(:each) { @class = TestClass }
|
12
|
+
|
13
|
+
describe "::from" do
|
14
|
+
before(:each) do
|
15
|
+
@class.stub(:translators).and_return(Hash => :from_hash)
|
16
|
+
@class.stub(:from_hash).and_return(Hash.new)
|
17
|
+
@class.stub(:send).with(:from_hash, kind_of(Hash)).and_return(true)
|
18
|
+
|
19
|
+
@hash_source = {:foo => :bar}
|
20
|
+
@unknown_source = double("Unknown Instance")
|
21
|
+
@unknown_source.stub(:to_hash).and_return(@hash_source)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "checks for translator with passed class" do
|
25
|
+
@class.translators.should_receive(:[]).with(Hash).and_return(:from_hash)
|
26
|
+
@class.from({:foo => :bar})
|
27
|
+
end
|
28
|
+
|
29
|
+
it "passes to translator if found" do
|
30
|
+
@class.should_receive(:send).with(:from_hash, @hash_source)
|
31
|
+
@class.from(@hash_source)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "calls to_hash if unrecognized class" do
|
35
|
+
@unknown_source.should_receive(:to_hash)
|
36
|
+
@class.from(@unknown_source)
|
37
|
+
end
|
38
|
+
|
39
|
+
it "passes to translator after converting to hash" do
|
40
|
+
@class.should_receive(:send).with(:from_hash, @hash_source)
|
41
|
+
@class.from(@unknown_source)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "::translators" do
|
46
|
+
# Required
|
47
|
+
it "raises an error if called from module" do
|
48
|
+
lambda { @class.translators }.should raise_error(NotImplementedError)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
describe "::from_file" do
|
53
|
+
# Required
|
54
|
+
it "raises an error if called from module" do
|
55
|
+
lambda { @class.from_file }.should raise_error(NotImplementedError)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe "::from_hash" do
|
60
|
+
# Required
|
61
|
+
it "raises an error if called from module" do
|
62
|
+
lambda { @class.from_hash }.should raise_error(NotImplementedError)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe "#variables" do
|
68
|
+
it "defaults to an empty list" do
|
69
|
+
@instance.variables.should == {}
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe "#to_hash" do
|
74
|
+
# Required
|
75
|
+
it "raises an error if called from module" do
|
76
|
+
lambda { @instance.to_hash }.should raise_error(NotImplementedError)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe "#save" do
|
81
|
+
# Required
|
82
|
+
it "raises an error if called from module" do
|
83
|
+
lambda { @instance.save }.should raise_error(NotImplementedError)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
describe "#length" do
|
88
|
+
# Required
|
89
|
+
it "raises an error if called from module" do
|
90
|
+
lambda { @instance.length }.should raise_error(NotImplementedError)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Enumerable interface?
|
95
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# require 'spec_helper'
|
2
|
+
#
|
3
|
+
# describe Pest::Estimator::Bernoulli do
|
4
|
+
# it "inherits from set"
|
5
|
+
#
|
6
|
+
# describe "estimate_class" do
|
7
|
+
# before(:each) { @instance = Pest::Estimator::Bernoulli.new }
|
8
|
+
#
|
9
|
+
# it "returns class" do
|
10
|
+
# @instance.estimate_class.should == Pest::Estimate::Bernoulli
|
11
|
+
# end
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# describe Distribution do
|
15
|
+
# describe "cache" do
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# describe "evaluate" do
|
19
|
+
# end
|
20
|
+
# end
|
21
|
+
# end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Pest::Estimator::Frequency do
|
4
|
+
before(:each) do
|
5
|
+
@class = Pest::Estimator::Frequency
|
6
|
+
@v1 = Pest::Variable.new(:name => :foo)
|
7
|
+
@v2 = Pest::Variable.new(:name => :bar)
|
8
|
+
@data = Pest::DataSet::NArray.from_hash @v1 => [1,1,2,3], @v2 => [1,1,1,1]
|
9
|
+
@test = Pest::DataSet::NArray.from_hash @v1 => [1,2,4], @v2 => [1,1,1]
|
10
|
+
@instance = @class.new(@data)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "inherits from set" do
|
14
|
+
@instance.should be_a(Pest::Estimator)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "generates marginal probabilities" do
|
18
|
+
@instance.p(@v2).in(@test).should === NArray[[1.0, 1.0, 1.0]]
|
19
|
+
end
|
20
|
+
|
21
|
+
it "generates joint probability" do
|
22
|
+
@instance.p(@v1, @v2).in(@test).should == NArray[[0.5, 0.25, 0]]
|
23
|
+
end
|
24
|
+
|
25
|
+
it "generates conditional probability" do
|
26
|
+
@instance.p(@v1).given(@v2).in(@test).should == NArray[[0.5, 0.25, 0]]
|
27
|
+
end
|
28
|
+
|
29
|
+
describe Pest::Estimator::Frequency::Distribution do
|
30
|
+
before(:each) do
|
31
|
+
@dist = @instance.distributions[@data.variables.values.to_set]
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "#cache_model" do
|
35
|
+
context "with unrecognized checksum" do
|
36
|
+
it "determines vector frequency" do
|
37
|
+
@dist.cache_model
|
38
|
+
@dist.frequencies[[1,1]].should == 2
|
39
|
+
end
|
40
|
+
|
41
|
+
it "defaults to 0" do
|
42
|
+
@dist.cache_model
|
43
|
+
@dist.frequencies[[4,1]].should == 0
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
context "with recognized checksum but no file" do
|
48
|
+
it "determines vector frequency" do
|
49
|
+
@data.should_receive(:data_vectors).and_return @data
|
50
|
+
@dist.cache_model
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context "with recognized checksum and cache file" do
|
55
|
+
before(:each) do
|
56
|
+
@file = @dist.cache_model
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe "#probability" do
|
62
|
+
it "returns an NArray" do
|
63
|
+
@dist.probability(@test).should be_a(NArray)
|
64
|
+
end
|
65
|
+
|
66
|
+
it "calculates vector frequency / dataset length" do
|
67
|
+
@dist.probability(@test).should == NArray[[0.5,0.25,0]]
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "#entropy" do
|
72
|
+
it "returns a Float" do
|
73
|
+
@dist.entropy.should be_a(Float)
|
74
|
+
end
|
75
|
+
|
76
|
+
it "calculates -sum(PlogP)" do
|
77
|
+
# Outcomes = ([1,1]: 2, [2,1]: 1, [3,1]: 1)
|
78
|
+
# P = (0.5, 0.25, 0.25)
|
79
|
+
# logP = (-1, -2, -2) (log base 2 for bits)
|
80
|
+
# -sum(PlogP) = (0.5, 0.5, 0.5).sum
|
81
|
+
@dist.entropy.should == 1.5
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# require 'spec_helper'
|
2
|
+
#
|
3
|
+
# describe Pest::Estimator::Gaussian do
|
4
|
+
# it "inherits from discrete"
|
5
|
+
#
|
6
|
+
# describe "estimate_class" do
|
7
|
+
# before(:each) { @instance = Pest::Extimator::Gaussian.new }
|
8
|
+
#
|
9
|
+
# it "returns class" do
|
10
|
+
# @instance.estimate_class.should == Pest::Estimate::Gaussian
|
11
|
+
# end
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# describe Distribution do
|
15
|
+
# describe "cache" do
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# describe "evaluate" do
|
19
|
+
# end
|
20
|
+
# end
|
21
|
+
# end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# require 'spec_helper'
|
2
|
+
#
|
3
|
+
# describe Pest::Estimator::Multinomial do
|
4
|
+
# it "inherits from set"
|
5
|
+
#
|
6
|
+
# describe "estimate_class" do
|
7
|
+
# before(:each) { @instance = Pest::Estimator::Multinomial.new }
|
8
|
+
#
|
9
|
+
# it "returns class" do
|
10
|
+
# @instance.estimate_class.should == Pest::Estimate::Multinomial
|
11
|
+
# end
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# describe Distribution do
|
15
|
+
# describe "cache" do
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# describe "evaluate" do
|
19
|
+
# end
|
20
|
+
# end
|
21
|
+
# end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# require 'spec_helper'
|
2
|
+
#
|
3
|
+
# describe Pest::Estimator::Parzen do
|
4
|
+
# it "inherits from continuous"
|
5
|
+
#
|
6
|
+
# describe "estimate_class" do
|
7
|
+
# before(:each) { @instance = Pest::Estimator::Parzen.new }
|
8
|
+
#
|
9
|
+
# it "returns class" do
|
10
|
+
# @instance.estimate_class.should == Pest::Estimate::Parzen
|
11
|
+
# end
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# describe Distribution do
|
15
|
+
# describe "cache" do
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# describe "evaluate" do
|
19
|
+
# end
|
20
|
+
# end
|
21
|
+
# end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# require 'spec_helper'
|
2
|
+
#
|
3
|
+
# describe Pest::Estimator::SVD do
|
4
|
+
# it "inherits from continuous"
|
5
|
+
#
|
6
|
+
# describe "estimate_class" do
|
7
|
+
# before(:each) { @instance = Pest::Estimator::SVD.new }
|
8
|
+
#
|
9
|
+
# it "returns class" do
|
10
|
+
# @instance.estimate_class.should == Pest::Estimate::SVD
|
11
|
+
# end
|
12
|
+
# end
|
13
|
+
#
|
14
|
+
# describe Distribution do
|
15
|
+
# describe "cache" do
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# describe "evaluate" do
|
19
|
+
# end
|
20
|
+
# end
|
21
|
+
# end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class TestClass
|
4
|
+
include Pest::Estimator
|
5
|
+
def distribution_class; Distribution end
|
6
|
+
class Distribution
|
7
|
+
include Pest::Estimator::Distribution
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
describe Pest::Estimator do
|
12
|
+
before(:each) do
|
13
|
+
@data = Pest::DataSet::NArray.from_hash :foo => [1,1,2,3], :bar => [1,1,1,1]
|
14
|
+
@class = TestClass
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "::new" do
|
18
|
+
it "accepts a dataset" do
|
19
|
+
@class.new(@data).data.should == @data
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "#variables" do
|
24
|
+
it "proxies data set" do
|
25
|
+
@class.new(@data).variables.should == @data.variables
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "#estimates" do
|
30
|
+
before(:each) do
|
31
|
+
@v1 = Pest::Variable.new(:name => :foo)
|
32
|
+
@v2 = Pest::Variable.new(:name => :bar)
|
33
|
+
@v3 = Pest::Variable.new(:name => :baz)
|
34
|
+
|
35
|
+
@instance = TestClass.new
|
36
|
+
@instance.stub(:variables).and_return({:foo => @v1, :bar => @v2})
|
37
|
+
end
|
38
|
+
|
39
|
+
it "accepts a set of variables" do
|
40
|
+
@instance.distributions[@v1, @v2].should be_a(Pest::Estimator::Distribution)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "returns an estimator for the passed variables" do
|
44
|
+
@instance.distributions[@v1, @v2].variables.should == [@v1, @v2].to_set
|
45
|
+
end
|
46
|
+
|
47
|
+
it "returns an estimator for the passed strings" do
|
48
|
+
@instance.distributions[:foo, :bar].variables.should == [@v1, @v2].to_set
|
49
|
+
end
|
50
|
+
|
51
|
+
it "is variable order agnostic" do
|
52
|
+
@instance.distributions[@v1, @v2].should == @instance.distributions[@v2, @v1]
|
53
|
+
end
|
54
|
+
|
55
|
+
it "fails if a set variable isn't defined" do
|
56
|
+
lambda { @instance.distributions[@v1, @v3] }.should raise_error(ArgumentError)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe Pest::Estimator::Distribution do
|
61
|
+
before(:each) do
|
62
|
+
@class = TestClass::Distribution
|
63
|
+
@estimator = TestClass.new
|
64
|
+
@estimator.stub(:variables).and_return({:foo => @v1, :bar => @v2})
|
65
|
+
@instance = @class.new(@estimator, @estimator.variables)
|
66
|
+
end
|
67
|
+
|
68
|
+
describe "#probability" do
|
69
|
+
it "raises no implemented" do
|
70
|
+
expect { @instance.probability }.to raise_error(NotImplementedError)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class EntropyTestClass
|
4
|
+
include Pest::Estimator
|
5
|
+
include Pest::Function::Entropy
|
6
|
+
end
|
7
|
+
|
8
|
+
describe Pest::Function::Entropy do
|
9
|
+
before(:each) do
|
10
|
+
@v1 = Pest::Variable.new(:name => :foo)
|
11
|
+
@v2 = Pest::Variable.new(:name => :bar)
|
12
|
+
@v3 = Pest::Variable.new(:name => :baz)
|
13
|
+
@instance = EntropyTestClass.new
|
14
|
+
@instance.stub(:variables).and_return({:foo => @v1, :bar => @v2})
|
15
|
+
end
|
16
|
+
|
17
|
+
describe "#entropy" do
|
18
|
+
it "returns a Builder" do
|
19
|
+
@instance.entropy.should be_a(Pest::Function::Entropy::Builder)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "is aliased as h" do
|
23
|
+
@instance.h.should be_a(Pest::Function::Entropy::Builder)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe Pest::Function::Entropy::Builder do
|
28
|
+
describe "::new" do
|
29
|
+
before(:each) { @builder = EntropyTestClass::Builder.new(@instance, [@v1, :bar]) }
|
30
|
+
|
31
|
+
it "sets estimator" do
|
32
|
+
@builder.estimator.should == @instance
|
33
|
+
end
|
34
|
+
|
35
|
+
it "sets event" do
|
36
|
+
@builder.event.should == [@v1, @v2].to_set
|
37
|
+
end
|
38
|
+
|
39
|
+
it "fails if variable undefined for estimator" do
|
40
|
+
lambda { EntropyTestClass::Builder.new(@instance, [@v1, @v3]) }.should raise_error(ArgumentError)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "constructs dataset if passed hash"
|
44
|
+
end
|
45
|
+
|
46
|
+
describe "#given" do
|
47
|
+
before(:each) { @builder = EntropyTestClass::Builder.new(@instance, [:foo]) }
|
48
|
+
|
49
|
+
it "sets givens" do
|
50
|
+
@builder.given(:bar)
|
51
|
+
@builder.givens.should include(@v2)
|
52
|
+
end
|
53
|
+
|
54
|
+
it "returns self" do
|
55
|
+
@builder.given(:bar).should be_a(EntropyTestClass::Builder)
|
56
|
+
end
|
57
|
+
|
58
|
+
it "fails if variables aren't variables on the estimator" do
|
59
|
+
lambda { @builder.given(:baz) }.should raise_error(ArgumentError)
|
60
|
+
end
|
61
|
+
|
62
|
+
it "adds to dataset if passed hash"
|
63
|
+
|
64
|
+
it "raises error if passed hash with existing (non hash) dataset"
|
65
|
+
end
|
66
|
+
|
67
|
+
describe "#evaluate" do
|
68
|
+
it "generates dataset if not specified"
|
69
|
+
|
70
|
+
it "gets entropy of event" do
|
71
|
+
event = double('EntropyEventDist')
|
72
|
+
@instance.distributions.stub(:[]).with([@v1].to_set).and_return(event)
|
73
|
+
event.should_receive(:entropy).and_return 0.5
|
74
|
+
|
75
|
+
EntropyTestClass::Builder.new(@instance,[:foo]).evaluate
|
76
|
+
end
|
77
|
+
|
78
|
+
it "gets entropy of givens" do
|
79
|
+
event = double("EntropyEventDist", :entropy => 0.5)
|
80
|
+
given = double("EntropyGivenDist", :entropy => 0.25)
|
81
|
+
@instance.distributions.stub(:[]).with([@v1].to_set).and_return(event)
|
82
|
+
@instance.distributions.stub(:[]).with([@v2].to_set).and_return(given)
|
83
|
+
given.should_receive(:entropy).and_return 0.25
|
84
|
+
|
85
|
+
EntropyTestClass::Builder.new(@instance,[:foo]).given(:bar).evaluate
|
86
|
+
end
|
87
|
+
|
88
|
+
it "returns H event - givens (if givens)" do
|
89
|
+
event = double("EntropyEventDist", :entropy => 0.5)
|
90
|
+
given = double("EntropyGivenDist", :entropy => 0.1)
|
91
|
+
@instance.distributions.stub(:[]).with([@v1].to_set).and_return(event)
|
92
|
+
@instance.distributions.stub(:[]).with([@v2].to_set).and_return(given)
|
93
|
+
|
94
|
+
EntropyTestClass::Builder.new(@instance,[:foo]).given(:bar).evaluate.should == 0.4
|
95
|
+
end
|
96
|
+
|
97
|
+
it "returns H event (if no givens)" do
|
98
|
+
event = double("EntropyEventDist", :entropy => 0.5)
|
99
|
+
@instance.distributions.stub(:[]).with([@v1].to_set).and_return(event)
|
100
|
+
|
101
|
+
EntropyTestClass::Builder.new(@instance,[:foo]).evaluate.should == 0.5
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|