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 CHANGED
@@ -1,6 +1,6 @@
1
1
  = fuzzy-realty
2
2
 
3
- Description goes here.
3
+ A Fuzzy-search gem for a Real Estate website
4
4
 
5
5
  == Copyright
6
6
 
data/VERSION.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  ---
2
2
  :major: 0
3
- :minor: 5
4
- :patch: 1
3
+ :minor: 6
4
+ :patch: 2
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
- 100.times do |i|
36
- listings << FuzzyRealty::Listing.new({
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 20 Listings:"
53
- scores[(0..20)].each do |score|
54
- puts "%.2f" % score[:score] + "\t\t#{score[:listing].inspect}"
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.5.1
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-19 00:00:00 -07:00
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