rkneufeld-fuzzy-realty 0.5.1 → 0.6.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +1 -1
- data/VERSION.yml +2 -2
- data/lib/classes.rb +36 -7
- data/lib/fuzzy_realty.rb +28 -10
- data/lib/profiler.rb +48 -0
- data/lib/weights.rb +4 -0
- metadata +3 -2
data/README.rdoc
CHANGED
data/VERSION.yml
CHANGED
data/lib/classes.rb
CHANGED
@@ -1,22 +1,31 @@
|
|
1
1
|
module FuzzyRealty
|
2
|
-
# Stub for testing without Rails project
|
3
|
-
class Listing
|
4
|
-
attr_accessor :price, :sqft, :location, :style
|
5
|
-
def initialize(values={})
|
6
|
-
values.each_key {|k| instance_variable_set(:"@#{k}", values[k])}
|
7
|
-
end
|
8
|
-
end
|
9
2
|
class Query
|
10
3
|
attr_reader :params
|
11
4
|
def initialize(params=[])
|
5
|
+
if params.any? {|param| !param.is_a?(Parameter)}
|
6
|
+
raise "Attempting to add non-Parameter to Query"
|
7
|
+
end
|
12
8
|
@params = params
|
13
9
|
end
|
10
|
+
# Allows formation of Query by 'popping' parameters onto it
|
14
11
|
def << (param)
|
15
12
|
if !param.is_a?(Parameter)
|
16
13
|
raise "Attempting to add non-Parameter to Query"
|
17
14
|
end
|
18
15
|
@params << param
|
16
|
+
end
|
19
17
|
|
18
|
+
# Create a random query for testing
|
19
|
+
def self.random
|
20
|
+
query = Query.new
|
21
|
+
query << Parameter.new(:price, 20_000 + rand(250_000), [true,false][rand(1)])
|
22
|
+
query << Parameter.new(:sqft, 300 + rand(2000), [true,false][rand(1)])
|
23
|
+
query << Parameter.new(:location, %W{A B C D}[rand(4)], [true,false][rand(1)])
|
24
|
+
query << Parameter.new(
|
25
|
+
:style,
|
26
|
+
%W{Bungalow Bi-level Split-level Two-story Condominium}[rand(5)],
|
27
|
+
[true,false][rand(1)])
|
28
|
+
query
|
20
29
|
end
|
21
30
|
end
|
22
31
|
|
@@ -30,12 +39,32 @@ module FuzzyRealty
|
|
30
39
|
@desired = desired
|
31
40
|
@required = required
|
32
41
|
end
|
42
|
+
|
43
|
+
#A valid type is any of the ones there is a weight for.
|
33
44
|
def self.valid_types
|
34
45
|
FuzzyRealty::WEIGHTS.each_key.to_a
|
35
46
|
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
# Stub for testing without Rails project
|
51
|
+
class Listing
|
52
|
+
attr_accessor :price, :sqft, :location, :style
|
53
|
+
def initialize(values={})
|
54
|
+
values.each_key {|k| instance_variable_set(:"@#{k}", values[k])}
|
55
|
+
end
|
56
|
+
def self.random
|
57
|
+
FuzzyRealty::Listing.new({
|
58
|
+
:price => 20_000 + rand(250_000),
|
59
|
+
:sqft => 300 + rand(2000),
|
60
|
+
:location => %W{A B C D}[rand(4)],
|
61
|
+
:style => %W{Bungalow Bi-level Split-level Two-story Condominium}[rand(5)]
|
62
|
+
})
|
63
|
+
end
|
36
64
|
end
|
37
65
|
end
|
38
66
|
|
67
|
+
#Small hack to the Array class to allow for easier type checking
|
39
68
|
class Array
|
40
69
|
def contains(value)
|
41
70
|
self.any? {|array_value| array_value == value }
|
data/lib/fuzzy_realty.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'benchmark'
|
2
|
+
|
1
3
|
require 'weights.rb'
|
2
4
|
require 'classes.rb'
|
3
5
|
require 'scores_table.rb'
|
@@ -26,19 +28,34 @@ module FuzzyRealty
|
|
26
28
|
end
|
27
29
|
return scores.sort {|a,b| b[:score] <=> a[:score]}
|
28
30
|
end
|
31
|
+
def self.performance
|
32
|
+
puts "Benchmarking search through 100,000-1 (powers of ten) random listings"
|
33
|
+
puts "======================================================================="
|
34
|
+
listings = []
|
35
|
+
100_000.times { listings << Listing.random }
|
36
|
+
puts "Generated #{listings.count} random listings"
|
37
|
+
Benchmark.bm do |x|
|
38
|
+
x.report("100,000 listings:") { ExpertSystem.scores(listings,Query.random) }
|
39
|
+
listings = listings[(0...10_000)]
|
40
|
+
x.report("10,000 listings:") { ExpertSystem.scores(listings,Query.random) }
|
41
|
+
listings = listings[(0...1_000)]
|
42
|
+
x.report("1,000 listings:") { ExpertSystem.scores(listings,Query.random) }
|
43
|
+
listings = listings[(0...100)]
|
44
|
+
x.report("100 listings:") { ExpertSystem.scores(listings,Query.random) }
|
45
|
+
listings = listings[(0...10)]
|
46
|
+
x.report("10 listings:") { ExpertSystem.scores(listings,Query.random) }
|
47
|
+
listings = listings[(0...1)]
|
48
|
+
x.report("1 listing:") { ExpertSystem.scores(listings,Query.random) }
|
49
|
+
end
|
50
|
+
end
|
29
51
|
end
|
30
52
|
end
|
31
53
|
|
32
54
|
#When running the library directly calculate an example
|
33
55
|
if __FILE__ == $0
|
34
56
|
listings = []
|
35
|
-
|
36
|
-
listings << FuzzyRealty::Listing.
|
37
|
-
:price => 20_000 + rand(250_000),
|
38
|
-
:sqft => 300 + rand(2000),
|
39
|
-
:location => %W{A B C D}[rand(4)],
|
40
|
-
:style => %W{Bungalow Bi-level Split-level Two-story Condominium}[rand(5)]
|
41
|
-
})
|
57
|
+
1000.times do |i|
|
58
|
+
listings << FuzzyRealty::Listing.random
|
42
59
|
end
|
43
60
|
|
44
61
|
query = FuzzyRealty::Query.new
|
@@ -49,12 +66,13 @@ if __FILE__ == $0
|
|
49
66
|
|
50
67
|
scores = FuzzyRealty::ExpertSystem.scores(listings,query)
|
51
68
|
puts "Query 1, $250k Condominium in the prestiguous 'A' suburbs. 1575 sq. ft."
|
52
|
-
puts "Top
|
53
|
-
scores[(0
|
54
|
-
puts "%.2f" % score[:score] + "\t
|
69
|
+
puts "Top 10 Listings:"
|
70
|
+
scores[(0...10)].each_with_index do |score,i|
|
71
|
+
puts "(#{i+1})\t%.2f" % score[:score] + "\t#{score[:listing].inspect}"
|
55
72
|
end
|
56
73
|
|
57
74
|
puts "\n"
|
75
|
+
|
58
76
|
query = FuzzyRealty::Query.new
|
59
77
|
query << FuzzyRealty::Parameter.new(:price,99_000,true)
|
60
78
|
query << FuzzyRealty::Parameter.new(:location,'C')
|
data/lib/profiler.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'ruby-prof'
|
3
|
+
require 'fuzzy_realty.rb'
|
4
|
+
|
5
|
+
RubyProf.start
|
6
|
+
|
7
|
+
puts "Generating 10000 Listings"
|
8
|
+
listings = []
|
9
|
+
10000.times do |i|
|
10
|
+
listings << FuzzyRealty::Listing.random
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
query = FuzzyRealty::Query.new
|
15
|
+
query << FuzzyRealty::Parameter.new(:price,250000)
|
16
|
+
query << FuzzyRealty::Parameter.new(:location, 'A',true)
|
17
|
+
query << FuzzyRealty::Parameter.new(:style,"Condominium",true)
|
18
|
+
query << FuzzyRealty::Parameter.new(:sqft,1575,true)
|
19
|
+
|
20
|
+
puts "Running score calculation 10 times..."
|
21
|
+
scores = []
|
22
|
+
10.times { |i| puts i; scores = FuzzyRealty::ExpertSystem.scores(listings,query) }
|
23
|
+
puts "Query 1, $250k Condominium in the prestiguous 'A' suburbs. 1575 sq. ft."
|
24
|
+
puts "Top 20 Listings:"
|
25
|
+
scores[(0..20)].each do |score|
|
26
|
+
puts "%.2f" % score[:score] + "\t\t#{score[:listing].inspect}"
|
27
|
+
end
|
28
|
+
|
29
|
+
puts "\n"
|
30
|
+
|
31
|
+
query = FuzzyRealty::Query.new
|
32
|
+
query << FuzzyRealty::Parameter.new(:price,99_000,true)
|
33
|
+
query << FuzzyRealty::Parameter.new(:location,'C')
|
34
|
+
query << FuzzyRealty::Parameter.new(:style, "Bungalow")
|
35
|
+
query << FuzzyRealty::Parameter.new(:sqft,1200)
|
36
|
+
|
37
|
+
puts "Running score calculation 10 times..."
|
38
|
+
10.times { |i| puts i; scores = FuzzyRealty::ExpertSystem.scores(listings,query) }
|
39
|
+
puts "Query 2, $99k Bungalow in the ever so average 'C' block. 1200 sq. ft."
|
40
|
+
puts "Top 20 Listings:"
|
41
|
+
scores[(0..20)].each do |score|
|
42
|
+
puts "%.2f" % score[:score] + "\t\t#{score[:listing].inspect}"
|
43
|
+
end
|
44
|
+
|
45
|
+
result = RubyProf.stop
|
46
|
+
|
47
|
+
printer = RubyProf::GraphHtmlPrinter.new(result)
|
48
|
+
printer.print(STDOUT, :min_percent=>0)
|
data/lib/weights.rb
CHANGED
@@ -3,6 +3,9 @@ module FuzzyRealty
|
|
3
3
|
# but as the knowledge engineer I was forced to pick the crisp values.
|
4
4
|
# Experimentation with larger user groups would likely show specific values to do
|
5
5
|
# better
|
6
|
+
|
7
|
+
# class Parameter uses this hash to determine what is a valid type.
|
8
|
+
# - This must be matched by a rule in rulebase.rb
|
6
9
|
WEIGHTS = {
|
7
10
|
:sqft => 15,
|
8
11
|
:price => 10,
|
@@ -10,6 +13,7 @@ module FuzzyRealty
|
|
10
13
|
:style => 18
|
11
14
|
}
|
12
15
|
|
16
|
+
# Easy way to sum the max score (for testing)
|
13
17
|
def self.max_score
|
14
18
|
WEIGHTS.inject(0) {|sum,n| sum + n[1]}
|
15
19
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rkneufeld-fuzzy-realty
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryan Neufeld
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-04-
|
12
|
+
date: 2009-04-20 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -30,6 +30,7 @@ files:
|
|
30
30
|
- lib/TODO
|
31
31
|
- lib/classes.rb
|
32
32
|
- lib/fuzzy_realty.rb
|
33
|
+
- lib/profiler.rb
|
33
34
|
- lib/rulebase.rb
|
34
35
|
- lib/scores_table.rb
|
35
36
|
- lib/weights.rb
|