pest 0.0.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/.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
|