bmg 0.18.10 → 0.18.14

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8b2c8128cd4f8a2dad6d605f518cc9c0c8d40060
4
- data.tar.gz: 9560e2261e1c0abb410ef9311b003c26fa347f16
3
+ metadata.gz: 8541ab1e17726eb751772cc29a3d198f8bc021d7
4
+ data.tar.gz: a4e07beb7b994d3ac6dda214e3a75822a1a3eb07
5
5
  SHA512:
6
- metadata.gz: 4dbdd828629da347e11df110e15d0970e1f0862825ac0182a670cd0ff5fd8fb5a9e9e4eea11d3a761e264513227eacd5aa7cb9f2f3a609557c93a0ba3fb8b5cc
7
- data.tar.gz: c5662d96afd0666cbd17d86fb35bf9f21fe1d14e04d63c043e7c7c069fd01bb31f47653a2a1d553fc157fbcbf3a145a865d4552afdfae9248ec32539f8cfcd18
6
+ metadata.gz: '063179c6a79505b6baaaf800f56a35dc7084b95ac4fa2c73acc92e809bb1d164d99bb3ce6120863d3b87886f8e21b3bc3579a1043e01dc2938ff6c278a4c08c3'
7
+ data.tar.gz: 3fe57b85503565d547522f4a41ad608ca3839add2305e5108b212f269a4d76adb4d681da58cca01bea564c1ce24b70922215923f3df332d5a7d51dadc09a2ee8
data/lib/bmg/algebra.rb CHANGED
@@ -12,6 +12,7 @@ module Bmg
12
12
  protected :_allbut
13
13
 
14
14
  def autowrap(options = {})
15
+ return self if self.type.identity_autowrap?(options)
15
16
  _autowrap self.type.autowrap(options), options
16
17
  end
17
18
 
@@ -20,12 +21,12 @@ module Bmg
20
21
  end
21
22
  protected :_autowrap
22
23
 
23
- def autosummarize(by = [], summarization = {})
24
- _autosummarize type = self.type.autosummarize(by, summarization), by, summarization
24
+ def autosummarize(by = [], summarization = {}, options = {})
25
+ _autosummarize self.type.autosummarize(by, summarization, options), by, summarization, options
25
26
  end
26
27
 
27
- def _autosummarize(type, by, summarization)
28
- Operator::Autosummarize.new(type, self, by, summarization)
28
+ def _autosummarize(type, by, summarization, options)
29
+ Operator::Autosummarize.new(type, self, by, summarization, options)
29
30
  end
30
31
  protected :_autosummarize
31
32
 
@@ -11,16 +11,22 @@ module Bmg
11
11
  class Autosummarize
12
12
  include Operator::Unary
13
13
 
14
- def initialize(type, operand, by, sums)
14
+ DEFAULT_OPTIONS = {
15
+ default: :same
16
+ }
17
+
18
+ def initialize(type, operand, by, sums, options = {})
15
19
  @type = type
16
20
  @operand = operand
17
21
  @by = by
18
22
  @sums = sums.each_with_object({}){|(k,v),h| h[k] = to_summarizer(v) }
23
+ @options = DEFAULT_OPTIONS.merge(options)
24
+ @algo = build_algo
19
25
  end
20
26
 
21
27
  protected
22
28
 
23
- attr_reader :by, :sums
29
+ attr_reader :by, :sums, :options
24
30
 
25
31
  public
26
32
 
@@ -45,17 +51,17 @@ module Bmg
45
51
  h = {}
46
52
  @operand.each do |tuple|
47
53
  key = key(tuple)
48
- h[key] ||= init(key, tuple)
49
- h[key] = sum(h[key], tuple)
54
+ h[key] ||= @algo.init(tuple)
55
+ h[key] = @algo.sum(h[key], tuple)
50
56
  end
51
57
  h.each_pair do |k,v|
52
- h[k] = term(v)
58
+ h[k] = @algo.term(v)
53
59
  end
54
60
  h.values.each(&bl)
55
61
  end
56
62
 
57
63
  def to_ast
58
- [:autosummarize, operand.to_ast, by.dup, sums.dup]
64
+ [:autosummarize, operand.to_ast, by.dup, sums.dup, options.dup]
59
65
  end
60
66
 
61
67
  public ### for internal reasons
@@ -73,7 +79,7 @@ module Bmg
73
79
  else
74
80
  op = operand
75
81
  op = op.restrict(bottom)
76
- op = op.autosummarize(by, sums)
82
+ op = op.autosummarize(by, sums, options)
77
83
  op = op.restrict(top)
78
84
  op
79
85
  end
@@ -87,46 +93,86 @@ module Bmg
87
93
 
88
94
  private
89
95
 
96
+ def build_algo
97
+ case default = @options[:default]
98
+ when :same then Check.new(sums)
99
+ when :first then Trust.new(sums)
100
+ else
101
+ raise ArgumentError, "Unknown default summarizer: `#{default}`"
102
+ end
103
+ end
104
+
90
105
  # Returns the tuple determinant.
91
106
  def key(tuple)
92
107
  @by.map{|by| tuple[by] }
93
108
  end
94
109
 
95
- # Returns the initial tuple to use for a given determinant.
96
- def init(key, tuple)
97
- tuple.each_with_object({}){|(k,v),h|
98
- h.merge!(k => summarizer(k).init(v))
99
- }
100
- end
101
-
102
- # Returns the summarizer to use for a given key.
103
- def summarizer(k)
104
- @sums[k] || Same.new
105
- end
106
-
107
- # Sums `tuple` on `memo`, returning the new tuple to use as memo.
108
- def sum(memo, tuple)
109
- tuple.each_with_object(memo.dup){|(k,v),h|
110
- h.merge!(k => summarizer(k).sum(h[k], v))
111
- }
112
- end
113
-
114
- # Terminates the summarization of a given tuple.
115
- def term(tuple)
116
- tuple.each_with_object({}){|(k,v),h|
117
- h.merge!(k => summarizer(k).term(v))
118
- }
119
- end
120
-
121
110
  def to_summarizer(x)
122
111
  case x
123
- when :same then Same.new
124
- when :group then DistinctList.new
112
+ when :same then Same::INSTANCE
113
+ when :group then DistinctList::INSTANCE
125
114
  else
126
115
  x
127
116
  end
128
117
  end
129
118
 
119
+ class Check
120
+ def initialize(sums)
121
+ @sums = sums
122
+ end
123
+ attr_reader :sums
124
+
125
+ def summarizer(k)
126
+ @sums[k] ||= Same::INSTANCE
127
+ end
128
+
129
+ def init(tuple)
130
+ tuple.each_with_object({}){|(k,v),h|
131
+ h.merge!(k => summarizer(k).init(v))
132
+ }
133
+ end
134
+
135
+ def sum(memo, tuple)
136
+ tuple.each_with_object(memo.dup){|(k,v),h|
137
+ h.merge!(k => summarizer(k).sum(h[k], v))
138
+ }
139
+ end
140
+
141
+ def term(tuple)
142
+ tuple.each_with_object({}){|(k,v),h|
143
+ h.merge!(k => summarizer(k).term(v))
144
+ }
145
+ end
146
+ end # class Check
147
+
148
+ class Trust
149
+ def initialize(sums)
150
+ @sums = sums
151
+ end
152
+ attr_reader :sums
153
+
154
+ # Returns the initial tuple to use for a given determinant.
155
+ def init(tuple)
156
+ sums.each_with_object(tuple.dup){|(attribute,summarizer),new_tuple|
157
+ new_tuple[attribute] = summarizer.init(tuple[attribute])
158
+ }
159
+ end
160
+
161
+ # Sums `tuple` on `memo`, returning the new tuple to use as memo.
162
+ def sum(memo, tuple)
163
+ sums.each_with_object(memo.dup){|(attribute,summarizer),new_tuple|
164
+ new_tuple[attribute] = summarizer.sum(memo[attribute], tuple[attribute])
165
+ }
166
+ end
167
+
168
+ # Terminates the summarization of a given tuple.
169
+ def term(tuple)
170
+ sums.each_with_object(tuple.dup){|(attribute,summarizer),new_tuple|
171
+ new_tuple[attribute] = summarizer.term(tuple[attribute])
172
+ }
173
+ end
174
+ end # class Trust
175
+
130
176
  #
131
177
  # Summarizes by enforcing that the same dependent is observed for a given
132
178
  # determinant, returning the dependent as summarization.
@@ -138,7 +184,7 @@ module Bmg
138
184
  end
139
185
 
140
186
  def sum(v1, v2)
141
- raise "Same values expected, got `#{v1}` vs. `#{v2}`" unless v1 == v2
187
+ raise TypeError, "Same values expected, got `#{v1}` vs. `#{v2}`" unless v1 == v2
142
188
  v1
143
189
  end
144
190
 
@@ -151,6 +197,7 @@ module Bmg
151
197
  end
152
198
  alias :to_s :inspect
153
199
 
200
+ INSTANCE = new
154
201
  end # class Same
155
202
 
156
203
  #
@@ -183,6 +230,7 @@ module Bmg
183
230
  end
184
231
  alias :to_s :inspect
185
232
 
233
+ INSTANCE = new
186
234
  end # class DistinctList
187
235
 
188
236
  #
@@ -26,6 +26,12 @@ module Bmg
26
26
  :split => "_"
27
27
  }
28
28
 
29
+ class << self
30
+ def separator(options)
31
+ options[:split] || DEFAULT_OPTIONS[:split]
32
+ end
33
+ end
34
+
29
35
  def initialize(type, operand, options = {})
30
36
  @type = type
31
37
  @operand = operand
@@ -32,7 +32,8 @@ module Bmg
32
32
  def each(&bl)
33
33
  return to_enum unless block_given?
34
34
  page_size = options[:page_size]
35
- @operand.to_a
35
+ @operand
36
+ .to_a
36
37
  .sort(&comparator)
37
38
  .drop(page_size * (page_index-1))
38
39
  .take(page_size)
@@ -46,7 +47,7 @@ module Bmg
46
47
  protected ### inspect
47
48
 
48
49
  def comparator
49
- Ordering.new(@ordering).comparator
50
+ @comparator ||= Ordering.new(@ordering).comparator
50
51
  end
51
52
 
52
53
  def args
@@ -10,32 +10,27 @@ module Bmg
10
10
  }
11
11
 
12
12
  def initialize(type, path, options = {})
13
- @type = type
13
+ require 'roo'
14
14
  @path = path
15
15
  @options = DEFAULT_OPTIONS.merge(options)
16
+ @type = type.knows_attrlist? ? type : type.with_attrlist(infer_attrlist)
16
17
  end
17
18
 
18
19
  def each
19
20
  return to_enum unless block_given?
20
- require 'roo'
21
- xlsx = Roo::Spreadsheet.open(@path, @options)
22
- headers = nil
23
- xlsx.sheet(@options[:sheet])
24
- .each
25
- .drop(@options[:skip])
26
- .each_with_index
27
- .each do |row, i|
28
- if i==0
29
- headers = row.map{|c| c.to_s.strip.to_sym }
30
- else
31
- init = init_tuple(i)
32
- tuple = (0...headers.size)
33
- .each_with_object(init){|i,t|
34
- t[headers[i]] = row[i]
35
- }
36
- yield(tuple)
37
- end
38
- end
21
+
22
+ headers = type.attrlist
23
+ headers = headers[1..-1] if generate_row_num?
24
+ start_at = @options[:skip] + 2
25
+ end_at = spreadsheet.last_row
26
+ (start_at..end_at).each do |i|
27
+ row = spreadsheet.row(i)
28
+ init = init_tuple(i - start_at + 1)
29
+ tuple = (0...headers.size).each_with_object(init){|i,t|
30
+ t[headers[i]] = row[i]
31
+ }
32
+ yield(tuple)
33
+ end
39
34
  end
40
35
 
41
36
  def to_ast
@@ -49,17 +44,37 @@ module Bmg
49
44
 
50
45
  private
51
46
 
52
- def init_tuple(i)
47
+ def spreadsheet
48
+ @spreadsheet ||= Roo::Spreadsheet
49
+ .open(@path, @options)
50
+ .sheet(@options[:sheet])
51
+ end
52
+
53
+ def infer_attrlist
54
+ row = spreadsheet.row(1+@options[:skip])
55
+ attrlist = row.map{|c| c.to_s.strip.to_sym }
56
+ attrlist.unshift(row_num_name) if generate_row_num?
57
+ attrlist
58
+ end
59
+
60
+ def generate_row_num?
61
+ !!@options[:row_num]
62
+ end
63
+
64
+ def row_num_name
53
65
  case as = @options[:row_num]
54
- when TrueClass
55
- { :row_num => i }
56
- when FalseClass
57
- {}
58
- when Symbol
59
- { :"#{as}" => i }
66
+ when TrueClass then :row_num
67
+ when Symbol then as
68
+ else nil
60
69
  end
61
70
  end
62
71
 
72
+ def init_tuple(i)
73
+ return {} unless generate_row_num?
74
+
75
+ { row_num_name => i }
76
+ end
77
+
63
78
  end # class Excel
64
79
  end # module Reader
65
- end # module Bmg
80
+ end # module Bmg
@@ -5,6 +5,7 @@ module Bmg
5
5
 
6
6
  def initialize(operand)
7
7
  @operand = operand
8
+ @materialized = nil
8
9
  end
9
10
 
10
11
  def type
@@ -19,14 +20,14 @@ module Bmg
19
20
  public
20
21
 
21
22
  def _count
22
- operand._count
23
+ _materialize._count
23
24
  end
24
25
 
25
26
  public
26
27
 
27
28
  def each(&bl)
28
- @operand = Relation::InMemory.new(operand.type, operand.to_a) unless @operand.is_a?(Relation::InMemory)
29
- @operand.each(&bl)
29
+ return to_enum unless block_given?
30
+ _materialize.each(&bl)
30
31
  end
31
32
 
32
33
  def to_ast
@@ -37,6 +38,14 @@ module Bmg
37
38
  []
38
39
  end
39
40
 
41
+ private
42
+
43
+ def _materialize
44
+ return @materialized if @materialized
45
+
46
+ @materialized = Relation::InMemory.new(operand.type, operand.to_a)
47
+ end
48
+
40
49
  end # class Materialized
41
50
  end # module Relation
42
51
  end # module Bmg
@@ -24,13 +24,27 @@ module Bmg
24
24
  protected :type=
25
25
 
26
26
  def each(&bl)
27
- spy.call(self) if bl
28
- operand.each(&bl)
27
+ return enum_for(:each) unless bl
28
+
29
+ if spy.respond_to?(:measure)
30
+ spy.measure(self) do
31
+ operand.each(&bl)
32
+ end
33
+ else
34
+ spy.call(self)
35
+ operand.each(&bl)
36
+ end
29
37
  end
30
38
 
31
39
  def count
32
- spy.call(self) if bl
33
- operand.count
40
+ if spy.respond_to?(:measure)
41
+ spy.measure(self) do
42
+ operand.count
43
+ end
44
+ else
45
+ spy.call(self)
46
+ operand.count
47
+ end
34
48
  end
35
49
 
36
50
  def to_ast
@@ -11,13 +11,26 @@ module Bmg
11
11
  end
12
12
 
13
13
  def comparator
14
- @comparator ||= ->(t1, t2) {
15
- attrs.each do |(attr,direction)|
16
- c = t1[attr] <=> t2[attr]
17
- return (direction == :desc ? -c : c) unless c==0
14
+ @comparator ||= ->(t1, t2) { compare_attrs(t1, t2) }
15
+ end
16
+
17
+ def compare_attrs(t1, t2)
18
+ attrs.each do |(attr,direction)|
19
+ a1, a2 = t1[attr], t2[attr]
20
+ if a1.nil? && a2.nil?
21
+ 0
22
+ elsif a1.nil?
23
+ return direction == :desc ? -1 : 1
24
+ elsif a2.nil?
25
+ return direction == :desc ? 1 : -1
26
+ elsif a1.respond_to?(:<=>)
27
+ c = a1 <=> a2
28
+ unless c.nil? || c==0
29
+ return direction == :desc ? -c : c
30
+ end
18
31
  end
19
- 0
20
- }
32
+ end
33
+ 0
21
34
  end
22
35
 
23
36
  end # class Ordering
data/lib/bmg/type.rb CHANGED
@@ -103,8 +103,15 @@ module Bmg
103
103
  }
104
104
  end
105
105
 
106
+ def identity_autowrap?(options)
107
+ return false unless knows_attrlist?
108
+
109
+ sep = Operator::Autowrap.separator(options)
110
+ self.attrlist.all?{|a| a.to_s.index(sep).nil? }
111
+ end
112
+
106
113
  def autowrap(options)
107
- sep = options[:split] || Operator::Autowrap::DEFAULT_OPTIONS[:split]
114
+ sep = Operator::Autowrap.separator(options)
108
115
  splitter = ->(a){ a.to_s.split(sep).first }
109
116
  is_split = ->(a){ a.to_s.split(sep).size > 1 }
110
117
  dup.tap{|x|
@@ -115,7 +122,7 @@ module Bmg
115
122
  }
116
123
  end
117
124
 
118
- def autosummarize(by, summarization)
125
+ def autosummarize(by, summarization, options)
119
126
  known_attributes!(by + summarization.keys) if typechecked? && knows_attrlist?
120
127
  dup.tap{|x|
121
128
  x.attrlist = nil
data/lib/bmg/version.rb CHANGED
@@ -2,7 +2,7 @@ module Bmg
2
2
  module Version
3
3
  MAJOR = 0
4
4
  MINOR = 18
5
- TINY = 10
5
+ TINY = 14
6
6
  end
7
7
  VERSION = "#{Version::MAJOR}.#{Version::MINOR}.#{Version::TINY}"
8
8
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bmg
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.18.10
4
+ version: 0.18.14
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bernard Lambeau
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-11-26 00:00:00.000000000 Z
11
+ date: 2022-02-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: predicate
@@ -16,20 +16,20 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '2.5'
19
+ version: '2.6'
20
20
  - - ">="
21
21
  - !ruby/object:Gem::Version
22
- version: 2.5.0
22
+ version: 2.6.0
23
23
  type: :runtime
24
24
  prerelease: false
25
25
  version_requirements: !ruby/object:Gem::Requirement
26
26
  requirements:
27
27
  - - "~>"
28
28
  - !ruby/object:Gem::Version
29
- version: '2.5'
29
+ version: '2.6'
30
30
  - - ">="
31
31
  - !ruby/object:Gem::Version
32
- version: 2.5.0
32
+ version: 2.6.0
33
33
  - !ruby/object:Gem::Dependency
34
34
  name: path
35
35
  requirement: !ruby/object:Gem::Requirement