goldmine 2.1.0 → 3.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.
- checksums.yaml +4 -4
- data/Gemfile +0 -1
- data/Gemfile.lock +14 -29
- data/README.md +134 -136
- data/lib/goldmine.rb +7 -13
- data/lib/goldmine/miner.rb +20 -0
- data/lib/goldmine/pivot.rb +52 -0
- data/lib/goldmine/pivot_result.rb +21 -0
- data/lib/goldmine/rollup.rb +41 -0
- data/lib/goldmine/rollup_clean_room.rb +21 -0
- data/lib/goldmine/rollup_result.rb +45 -0
- data/lib/goldmine/version.rb +1 -1
- data/test/test_goldmine.rb +149 -277
- metadata +11 -13
- data/goldmine.gemspec +0 -20
- data/lib/goldmine/array_miner.rb +0 -67
- data/lib/goldmine/cache.rb +0 -28
- data/lib/goldmine/hash_miner.rb +0 -81
- data/lib/goldmine/hash_rollup.rb +0 -83
- data/lib/goldmine/rollup_context.rb +0 -23
- data/license.md +0 -8
data/lib/goldmine.rb
CHANGED
@@ -1,16 +1,10 @@
|
|
1
|
-
|
2
|
-
require "
|
3
|
-
require "
|
4
|
-
require "hash_rollup"
|
5
|
-
require "array_miner"
|
6
|
-
require "hash_miner"
|
1
|
+
$LOAD_PATH.unshift File.join(File.dirname(__FILE__), "goldmine")
|
2
|
+
require "version"
|
3
|
+
require "miner"
|
7
4
|
|
8
5
|
module Goldmine
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
nil
|
14
|
-
end
|
15
|
-
end
|
6
|
+
end
|
7
|
+
|
8
|
+
def Goldmine(list)
|
9
|
+
Goldmine::Miner.new(list)
|
16
10
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require "forwardable"
|
2
|
+
require "pivot"
|
3
|
+
|
4
|
+
module Goldmine
|
5
|
+
class Miner
|
6
|
+
extend Forwardable
|
7
|
+
include Enumerable
|
8
|
+
def_delegators :@array, :each, :to_a
|
9
|
+
attr_reader :pivots
|
10
|
+
|
11
|
+
def initialize(array=[])
|
12
|
+
@pivots = []
|
13
|
+
@array = array.to_a
|
14
|
+
end
|
15
|
+
|
16
|
+
def pivot(name, &block)
|
17
|
+
Pivot.new(name, self, block)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require "pivot_result"
|
2
|
+
|
3
|
+
module Goldmine
|
4
|
+
class Pivot
|
5
|
+
attr_reader :name, :proc
|
6
|
+
|
7
|
+
def initialize(name, array_miner, block)
|
8
|
+
@array_miner = array_miner
|
9
|
+
@name = name
|
10
|
+
@proc = block
|
11
|
+
array_miner.pivots << self
|
12
|
+
end
|
13
|
+
|
14
|
+
def pivot(name, &block)
|
15
|
+
self.class.new(name, array_miner, block)
|
16
|
+
end
|
17
|
+
|
18
|
+
def result
|
19
|
+
PivotResult.new.tap do |pivot_result|
|
20
|
+
array_miner.each do |item|
|
21
|
+
key_data = array_miner.pivots.each_with_object(key: [], keys: []) do |pivot, memo|
|
22
|
+
value = pivot.proc.call(item)
|
23
|
+
if value.is_a?(Array)
|
24
|
+
if value.empty?
|
25
|
+
memo[:key] << key_for(pivot.name, nil)
|
26
|
+
else
|
27
|
+
value.each { |v| memo[:keys] << key_for(pivot.name, v) }
|
28
|
+
end
|
29
|
+
else
|
30
|
+
memo[:key] << key_for(pivot.name, value)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
(pivot_result[key_data[:key]] ||= []) << item unless key_data[:key].empty?
|
34
|
+
key_data[:keys].each do |key|
|
35
|
+
(pivot_result[key] ||= []) << item
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
attr_reader :array_miner
|
44
|
+
|
45
|
+
def key_for(name, value)
|
46
|
+
Array.new(2).tap do |key|
|
47
|
+
key[0] = name
|
48
|
+
key[1] = value
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require "forwardable"
|
2
|
+
require "rollup"
|
3
|
+
|
4
|
+
module Goldmine
|
5
|
+
class PivotResult
|
6
|
+
extend Forwardable
|
7
|
+
include Enumerable
|
8
|
+
def_delegators :@hash, :[], :[]=, :each, :to_h
|
9
|
+
attr_reader :rollups, :cache
|
10
|
+
|
11
|
+
def initialize(hash={})
|
12
|
+
@rollups = []
|
13
|
+
@hash = hash
|
14
|
+
@cache = {}
|
15
|
+
end
|
16
|
+
|
17
|
+
def rollup(name, &block)
|
18
|
+
Rollup.new(name, self, block)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require "rollup_clean_room"
|
2
|
+
require "rollup_result"
|
3
|
+
|
4
|
+
module Goldmine
|
5
|
+
class Rollup
|
6
|
+
attr_reader :name, :proc
|
7
|
+
|
8
|
+
def initialize(name, pivot_result, block)
|
9
|
+
@name = name
|
10
|
+
@pivot_result = pivot_result
|
11
|
+
@proc = block
|
12
|
+
pivot_result.rollups << self
|
13
|
+
end
|
14
|
+
|
15
|
+
def rollup(name, &block)
|
16
|
+
self.class.new(name, pivot_result, block)
|
17
|
+
end
|
18
|
+
|
19
|
+
def result(cache: false)
|
20
|
+
RollupResult.new.tap do |rollup_result|
|
21
|
+
pivot_result.each do |pivot_key, pivoted_list|
|
22
|
+
stash = {} if cache
|
23
|
+
pivot_result.rollups.each do |rollup|
|
24
|
+
Array.new(2).tap do |computed_value|
|
25
|
+
key = rollup.name
|
26
|
+
value = RollupCleanRoom.new(key, stash).rollup(pivoted_list, &rollup.proc) if cache
|
27
|
+
value ||= rollup.proc.call(pivoted_list)
|
28
|
+
computed_value[0] = key
|
29
|
+
computed_value[1] = value
|
30
|
+
(rollup_result[pivot_key] ||= []) << computed_value
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
attr_reader :pivot_result
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Goldmine
|
2
|
+
class RollupCleanRoom
|
3
|
+
attr_reader :name, :cache
|
4
|
+
|
5
|
+
def initialize(name, cache={})
|
6
|
+
@name = name
|
7
|
+
@cache = cache
|
8
|
+
end
|
9
|
+
|
10
|
+
def eigen
|
11
|
+
class << self
|
12
|
+
self
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def rollup(pivoted_list, &block)
|
17
|
+
eigen.instance_eval { define_method(:do_rollup, &block) }
|
18
|
+
@cache[name] = do_rollup(pivoted_list)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require "forwardable"
|
2
|
+
require "csv"
|
3
|
+
|
4
|
+
module Goldmine
|
5
|
+
class RollupResult
|
6
|
+
extend Forwardable
|
7
|
+
include Enumerable
|
8
|
+
def_delegators :@pivot_result, :[], :[]=, :each, :to_h
|
9
|
+
|
10
|
+
def initialize(pivot_result={})
|
11
|
+
@pivot_result = pivot_result
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_rows
|
15
|
+
pivot_result.each_with_object([]) do |pair, memo|
|
16
|
+
memo << pair.first + pair.last
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_hash_rows
|
21
|
+
pivot_result.each_with_object([]) do |pair, memo|
|
22
|
+
memo << (pair.first + pair.last).to_h
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_tabular
|
27
|
+
rows = to_rows
|
28
|
+
values = rows.map { |r| r.map(&:last) }
|
29
|
+
sample = rows.first || []
|
30
|
+
values.unshift sample.map(&:first)
|
31
|
+
values
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_csv_table
|
35
|
+
rows = to_tabular
|
36
|
+
header = rows.shift
|
37
|
+
csv_rows = rows.map { |row| CSV::Row.new(header, row) }
|
38
|
+
CSV::Table.new csv_rows
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
attr_reader :pivot_result
|
44
|
+
end
|
45
|
+
end
|
data/lib/goldmine/version.rb
CHANGED
data/test/test_goldmine.rb
CHANGED
@@ -6,124 +6,58 @@ require File.expand_path("../../lib/goldmine", __FILE__)
|
|
6
6
|
|
7
7
|
class TestGoldmine < PryTest::Test
|
8
8
|
|
9
|
-
|
10
|
-
assert Goldmine.miner([]).is_a?(Goldmine::ArrayMiner)
|
11
|
-
end
|
12
|
-
|
13
|
-
test "miner from hash" do
|
14
|
-
assert Goldmine.miner({}).is_a?(Goldmine::HashMiner)
|
15
|
-
end
|
16
|
-
|
17
|
-
test "miner from usupported object" do
|
18
|
-
assert Goldmine.miner(Object.new).nil?
|
19
|
-
end
|
9
|
+
# pivots .....................................................................
|
20
10
|
|
21
11
|
test "simple pivot" do
|
22
12
|
list = [1,2,3,4,5,6,7,8,9]
|
23
|
-
|
24
|
-
data = list.pivot { |i| i < 5 }
|
25
|
-
|
26
|
-
expected = {
|
27
|
-
true => [1, 2, 3, 4],
|
28
|
-
false => [5, 6, 7, 8, 9]
|
29
|
-
}
|
30
|
-
|
31
|
-
assert data == expected
|
32
|
-
end
|
33
|
-
|
34
|
-
test "simple pivot rollup" do
|
35
|
-
list = [1,2,3,4,5,6,7,8,9]
|
36
|
-
list = Goldmine::ArrayMiner.new(list)
|
37
|
-
data = list.pivot { |i| i < 5 }
|
38
|
-
rolled = data.rollup(:count) { |items| items.size }
|
39
|
-
|
40
|
-
expected = {
|
41
|
-
true => { count: 4 },
|
42
|
-
false => { count: 5 }
|
43
|
-
}
|
44
|
-
|
45
|
-
assert rolled == expected
|
46
|
-
end
|
47
|
-
|
48
|
-
test "pivot with chained rollup" do
|
49
|
-
list = [1,2,3,4,5,6,7,8,9]
|
50
|
-
list = Goldmine::ArrayMiner.new(list)
|
51
|
-
data = list.pivot { |i| i < 5 }
|
52
|
-
rolled = data
|
53
|
-
.rollup(:count) { |items| items.size }
|
54
|
-
.rollup(:div_by_3) { |items| items.keep_if { |i| i % 3 == 0 }.size }
|
13
|
+
pivot = Goldmine(list).pivot("< 5") { |i| i < 5 }
|
55
14
|
|
56
15
|
expected = {
|
57
|
-
true =>
|
58
|
-
false =>
|
16
|
+
[["< 5", true]] => [1, 2, 3, 4],
|
17
|
+
[["< 5", false]] => [5, 6, 7, 8, 9]
|
59
18
|
}
|
60
19
|
|
61
|
-
assert
|
20
|
+
assert pivot.result.to_h == expected
|
62
21
|
end
|
63
22
|
|
64
|
-
test "
|
65
|
-
list = [1,2,3,4,5,6,7,8,9]
|
66
|
-
list = Goldmine::ArrayMiner.new(list)
|
67
|
-
rolled = list.pivot { |i| i < 5 }.rollup(:count, &:size)
|
68
|
-
|
69
|
-
expected = [
|
70
|
-
["column1", "count"],
|
71
|
-
[true, 4],
|
72
|
-
[false, 5]
|
73
|
-
]
|
74
|
-
|
75
|
-
assert rolled.to_tabular == expected
|
76
|
-
end
|
77
|
-
|
78
|
-
test "simple pivot rollup to_csv_table" do
|
79
|
-
list = [1,2,3,4,5,6,7,8,9]
|
80
|
-
list = Goldmine::ArrayMiner.new(list)
|
81
|
-
rolled = list.pivot { |i| i < 5 }.rollup(:count, &:size)
|
82
|
-
csv = rolled.to_csv_table
|
83
|
-
|
84
|
-
assert csv.headers == ["column1", "count"]
|
85
|
-
assert csv.to_a == [["column1", "count"], [true, 4], [false, 5]]
|
86
|
-
end
|
87
|
-
|
88
|
-
test "named pivot" do
|
23
|
+
test "chained pivots" do
|
89
24
|
list = [1,2,3,4,5,6,7,8,9]
|
90
|
-
|
91
|
-
|
25
|
+
pivot = Goldmine(list)
|
26
|
+
.pivot("< 5") { |i| i < 5 }
|
27
|
+
.pivot("even") { |i| i % 2 == 0 }
|
92
28
|
|
93
29
|
expected = {
|
94
|
-
|
95
|
-
|
30
|
+
[["< 5", true], ["even", false]] => [1, 3],
|
31
|
+
[["< 5", true], ["even", true]] => [2, 4],
|
32
|
+
[["< 5", false], ["even", false]] => [5, 7, 9],
|
33
|
+
[["< 5", false], ["even", true]] => [6, 8]
|
96
34
|
}
|
97
35
|
|
98
|
-
assert
|
36
|
+
assert pivot.result.to_h == expected
|
99
37
|
end
|
100
38
|
|
101
|
-
test "
|
39
|
+
test "deep chained pivots" do
|
102
40
|
list = [1,2,3,4,5,6,7,8,9]
|
103
|
-
|
104
|
-
|
105
|
-
|
41
|
+
pivot = Goldmine(list)
|
42
|
+
.pivot("< 3") { |i| i < 3 }
|
43
|
+
.pivot("< 6") { |i| i < 6 }
|
44
|
+
.pivot("< 9") { |i| i < 9 }
|
45
|
+
.pivot("even") { |i| i % 2 == 0 }
|
46
|
+
.pivot("odd") { |i| i % 3 == 0 || i == 1 }
|
106
47
|
|
107
48
|
expected = {
|
108
|
-
|
109
|
-
|
49
|
+
[["< 3", true], ["< 6", true], ["< 9", true], ["even", false], ["odd", true]] => [1],
|
50
|
+
[["< 3", true], ["< 6", true], ["< 9", true], ["even", true], ["odd", false]] => [2],
|
51
|
+
[["< 3", false], ["< 6", true], ["< 9", true], ["even", false], ["odd", true]] => [3],
|
52
|
+
[["< 3", false], ["< 6", true], ["< 9", true], ["even", true], ["odd", false]] => [4],
|
53
|
+
[["< 3", false], ["< 6", true], ["< 9", true], ["even", false], ["odd", false]] => [5],
|
54
|
+
[["< 3", false], ["< 6", false], ["< 9", true], ["even", true], ["odd", true]] => [6],
|
55
|
+
[["< 3", false], ["< 6", false], ["< 9", true], ["even", false], ["odd", false]] => [7],
|
56
|
+
[["< 3", false], ["< 6", false], ["< 9", true], ["even", true], ["odd", false]] => [8],
|
57
|
+
[["< 3", false], ["< 6", false], ["< 9", false], ["even", false], ["odd", true]] => [9]
|
110
58
|
}
|
111
59
|
|
112
|
-
assert
|
113
|
-
end
|
114
|
-
|
115
|
-
test "named pivot rollup to_tabular" do
|
116
|
-
list = [1,2,3,4,5,6,7,8,9]
|
117
|
-
list = Goldmine::ArrayMiner.new(list)
|
118
|
-
rolled = list.pivot("less than 5") { |i| i < 5 }.rollup(:count, &:size)
|
119
|
-
|
120
|
-
expected = [
|
121
|
-
["less than 5", "count"],
|
122
|
-
[true, 4],
|
123
|
-
[false, 5]
|
124
|
-
]
|
125
|
-
|
126
|
-
assert rolled.to_tabular == expected
|
60
|
+
assert pivot.result.to_h == expected
|
127
61
|
end
|
128
62
|
|
129
63
|
test "pivot of list values" do
|
@@ -133,23 +67,17 @@ class TestGoldmine < PryTest::Test
|
|
133
67
|
{ :name => "three", :list => [1, 2, 3] },
|
134
68
|
{ :name => "four", :list => [1, 2, 3, 4] },
|
135
69
|
]
|
136
|
-
|
137
|
-
|
70
|
+
pivot = Goldmine(list)
|
71
|
+
.pivot("list value") { |record| record[:list] }
|
138
72
|
|
139
73
|
expected = {
|
140
|
-
1 => [
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
2 => [ { :name => "two", :list => [1, 2] },
|
145
|
-
{ :name => "three", :list => [1, 2, 3] },
|
146
|
-
{ :name => "four", :list => [1, 2, 3, 4] } ],
|
147
|
-
3 => [ { :name => "three", :list => [1, 2, 3] },
|
148
|
-
{ :name => "four", :list => [1, 2, 3, 4] } ],
|
149
|
-
4 => [ { :name => "four", :list => [1, 2, 3, 4] } ]
|
74
|
+
["list value", 1] => [{:name=>"one", :list=>[1]}, {:name=>"two", :list=>[1, 2]}, {:name=>"three", :list=>[1, 2, 3]}, {:name=>"four", :list=>[1, 2, 3, 4]}],
|
75
|
+
["list value", 2] => [{:name=>"two", :list=>[1, 2]}, {:name=>"three", :list=>[1, 2, 3]}, {:name=>"four", :list=>[1, 2, 3, 4]}],
|
76
|
+
["list value", 3] => [{:name=>"three", :list=>[1, 2, 3]}, {:name=>"four", :list=>[1, 2, 3, 4]}],
|
77
|
+
["list value", 4] => [{:name=>"four", :list=>[1, 2, 3, 4]}]
|
150
78
|
}
|
151
79
|
|
152
|
-
assert
|
80
|
+
assert pivot.result.to_h == expected
|
153
81
|
end
|
154
82
|
|
155
83
|
test "pivot of list values with empty list" do
|
@@ -160,243 +88,187 @@ class TestGoldmine < PryTest::Test
|
|
160
88
|
{ :name => "three", :list => [1, 2, 3] },
|
161
89
|
{ :name => "four", :list => [1, 2, 3, 4] },
|
162
90
|
]
|
163
|
-
|
164
|
-
|
91
|
+
pivot = Goldmine(list)
|
92
|
+
.pivot("list value") { |record| record[:list] }
|
165
93
|
|
166
94
|
expected = {
|
167
|
-
nil => [
|
168
|
-
1 => [ {
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
2 => [ { :name => "two", :list => [1, 2] },
|
173
|
-
{ :name => "three", :list => [1, 2, 3] },
|
174
|
-
{ :name => "four", :list => [1, 2, 3, 4] } ],
|
175
|
-
3 => [ { :name => "three", :list => [1, 2, 3] },
|
176
|
-
{ :name => "four", :list => [1, 2, 3, 4] } ],
|
177
|
-
4 => [ { :name => "four", :list => [1, 2, 3, 4] } ]
|
95
|
+
[["list value", nil]] => [{:name=>"empty", :list=>[]}],
|
96
|
+
["list value", 1] => [{:name=>"one", :list=>[1]}, {:name=>"two", :list=>[1, 2]}, {:name=>"three", :list=>[1, 2, 3]}, {:name=>"four", :list=>[1, 2, 3, 4]}],
|
97
|
+
["list value", 2] => [{:name=>"two", :list=>[1, 2]}, {:name=>"three", :list=>[1, 2, 3]}, {:name=>"four", :list=>[1, 2, 3, 4]}],
|
98
|
+
["list value", 3] => [{:name=>"three", :list=>[1, 2, 3]}, {:name=>"four", :list=>[1, 2, 3, 4]}],
|
99
|
+
["list value", 4] => [{:name=>"four", :list=>[1, 2, 3, 4]}]
|
178
100
|
}
|
179
101
|
|
180
|
-
assert
|
102
|
+
assert pivot.result.to_h == expected
|
181
103
|
end
|
182
104
|
|
183
|
-
|
105
|
+
# rollups ...................................................................
|
106
|
+
|
107
|
+
test "simple pivot rollup" do
|
184
108
|
list = [1,2,3,4,5,6,7,8,9]
|
185
|
-
|
186
|
-
|
109
|
+
rollup = Goldmine(list)
|
110
|
+
.pivot("< 5") { |i| i < 5 }
|
111
|
+
.result
|
112
|
+
.rollup(:count) { |items| items.size }
|
187
113
|
|
188
114
|
expected = {
|
189
|
-
[
|
190
|
-
[
|
191
|
-
[false, false] => [5, 7, 9],
|
192
|
-
[false, true] => [6, 8]
|
115
|
+
[["< 5", true]] => [[:count, 4]],
|
116
|
+
[["< 5", false]] => [[:count, 5]]
|
193
117
|
}
|
194
118
|
|
195
|
-
assert
|
119
|
+
assert rollup.result.to_h == expected
|
196
120
|
end
|
197
121
|
|
198
122
|
test "chained pivots rollup" do
|
199
123
|
list = [1,2,3,4,5,6,7,8,9]
|
200
|
-
|
201
|
-
|
202
|
-
|
124
|
+
rollup = Goldmine(list)
|
125
|
+
.pivot("< 5") { |i| i < 5 }
|
126
|
+
.pivot("even") { |i| i % 2 == 0 }
|
127
|
+
.result
|
128
|
+
.rollup(:count) { |row| row.size }
|
203
129
|
|
204
130
|
expected = {
|
205
|
-
[true, false]
|
206
|
-
[true, true]
|
207
|
-
[false, false] =>
|
208
|
-
[false, true] =>
|
131
|
+
[["< 5", true], ["even", false]] => [[:count, 2]],
|
132
|
+
[["< 5", true], ["even", true]] => [[:count, 2]],
|
133
|
+
[["< 5", false], ["even", false]] => [[:count, 3]],
|
134
|
+
[["< 5", false], ["even", true]] => [[:count, 2]]
|
209
135
|
}
|
210
136
|
|
211
|
-
assert
|
212
|
-
end
|
213
|
-
|
214
|
-
test "chained pivots rollup to_tabular" do
|
215
|
-
list = [1,2,3,4,5,6,7,8,9]
|
216
|
-
list = Goldmine::ArrayMiner.new(list)
|
217
|
-
rolled = list.pivot { |i| i < 5 }.pivot { |i| i % 2 == 0 }.rollup(:count, &:size)
|
218
|
-
|
219
|
-
expected = [
|
220
|
-
["column1", "column2", "count"],
|
221
|
-
[true, false, 2],
|
222
|
-
[true, true, 2],
|
223
|
-
[false, false, 3],
|
224
|
-
[false, true, 2]
|
225
|
-
]
|
226
|
-
|
227
|
-
assert rolled.to_tabular == expected
|
137
|
+
assert rollup.result.to_h == expected
|
228
138
|
end
|
229
139
|
|
230
|
-
test "
|
140
|
+
test "pivot with chained rollup" do
|
231
141
|
list = [1,2,3,4,5,6,7,8,9]
|
232
|
-
list = Goldmine
|
233
|
-
|
234
|
-
.pivot { |i| i <
|
235
|
-
.
|
236
|
-
.
|
237
|
-
.
|
238
|
-
.pivot { |i| i % 3 == 0 }
|
142
|
+
list = Goldmine(list)
|
143
|
+
rollup = list
|
144
|
+
.pivot("< 5") { |i| i < 5 }
|
145
|
+
.result
|
146
|
+
.rollup(:count) { |items| items.size }
|
147
|
+
.rollup(:div_by_3) { |items| items.keep_if { |i| i % 3 == 0 }.size }
|
239
148
|
|
240
149
|
expected = {
|
241
|
-
[
|
242
|
-
[
|
243
|
-
[false, true, true, false, true] => [3],
|
244
|
-
[false, true, true, false, false] => [5],
|
245
|
-
[false, true, true, true, false] => [4],
|
246
|
-
[false, false, true, true, true] => [6],
|
247
|
-
[false, false, true, true, false] => [8],
|
248
|
-
[false, false, true, false, false] => [7],
|
249
|
-
[false, false, false, false, true] => [9]
|
150
|
+
[["< 5", true]] => [[:count, 4], [:div_by_3, 1]],
|
151
|
+
[["< 5", false]] => [[:count, 5], [:div_by_3, 2]]
|
250
152
|
}
|
251
153
|
|
252
|
-
assert
|
154
|
+
assert rollup.result.to_h == expected
|
253
155
|
end
|
254
156
|
|
255
|
-
|
256
|
-
list = [1,2,3,4,5,6,7,8,9]
|
257
|
-
list = Goldmine::ArrayMiner.new(list)
|
258
|
-
data = list.pivot("a") { |i| i < 3 }.pivot("b") { |i| i < 6 }.pivot("c") { |i| i < 9 }.pivot("d") { |i| i % 2 == 0 }.pivot("e") { |i| i % 3 == 0 }
|
259
|
-
|
260
|
-
expected = {
|
261
|
-
{"a"=>true, "b"=>true, "c"=>true, "d"=>false, "e"=>false} => [1],
|
262
|
-
{"a"=>true, "b"=>true, "c"=>true, "d"=>true, "e"=>false} => [2],
|
263
|
-
{"a"=>false, "b"=>true, "c"=>true, "d"=>false, "e"=>true} => [3],
|
264
|
-
{"a"=>false, "b"=>true, "c"=>true, "d"=>false, "e"=>false} => [5],
|
265
|
-
{"a"=>false, "b"=>true, "c"=>true, "d"=>true, "e"=>false} => [4],
|
266
|
-
{"a"=>false, "b"=>false, "c"=>true, "d"=>true, "e"=>true} => [6],
|
267
|
-
{"a"=>false, "b"=>false, "c"=>true, "d"=>true, "e"=>false} => [8],
|
268
|
-
{"a"=>false, "b"=>false, "c"=>true, "d"=>false, "e"=>false} => [7],
|
269
|
-
{"a"=>false, "b"=>false, "c"=>false, "d"=>false, "e"=>true} => [9]
|
270
|
-
}
|
157
|
+
# to_rows ...................................................................
|
271
158
|
|
272
|
-
|
273
|
-
end
|
274
|
-
|
275
|
-
test "named chained pivots" do
|
159
|
+
test "simple pivot rollup to_rows" do
|
276
160
|
list = [1,2,3,4,5,6,7,8,9]
|
277
|
-
|
278
|
-
|
161
|
+
rollup = Goldmine(list)
|
162
|
+
.pivot("< 5") { |i| i < 5 }
|
163
|
+
.result
|
164
|
+
.rollup(:count) { |items| items.size }
|
279
165
|
|
280
|
-
expected =
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
{ "less than 5" => false, "divisible by 2" => true} => [6, 8]
|
285
|
-
}
|
166
|
+
expected = [
|
167
|
+
[["< 5", true], [:count, 4]],
|
168
|
+
[["< 5", false], [:count, 5]]
|
169
|
+
]
|
286
170
|
|
287
|
-
assert
|
171
|
+
assert rollup.result.to_rows == expected
|
288
172
|
end
|
289
173
|
|
290
|
-
test "
|
174
|
+
test "chained pivots rollup to_rows" do
|
291
175
|
list = [1,2,3,4,5,6,7,8,9]
|
292
|
-
|
293
|
-
|
294
|
-
|
176
|
+
rollup = Goldmine(list)
|
177
|
+
.pivot("< 5") { |i| i < 5 }
|
178
|
+
.pivot("even") { |i| i % 2 == 0 }
|
179
|
+
.result
|
180
|
+
.rollup(:count) { |row| row.size }
|
295
181
|
|
296
|
-
expected =
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
182
|
+
expected = [
|
183
|
+
[["< 5", true], ["even", false], [:count, 2]],
|
184
|
+
[["< 5", true], ["even", true], [:count, 2]],
|
185
|
+
[["< 5", false], ["even", false], [:count, 3]],
|
186
|
+
[["< 5", false], ["even", true], [:count, 2]]
|
187
|
+
]
|
302
188
|
|
303
|
-
assert
|
189
|
+
assert rollup.result.to_rows == expected
|
304
190
|
end
|
305
191
|
|
306
|
-
test "
|
192
|
+
test "simple pivot rollup to_hash_rows" do
|
307
193
|
list = [1,2,3,4,5,6,7,8,9]
|
308
|
-
|
309
|
-
|
194
|
+
rollup = Goldmine(list)
|
195
|
+
.pivot("< 5") { |i| i < 5 }
|
196
|
+
.result
|
197
|
+
.rollup(:count) { |items| items.size }
|
310
198
|
|
311
199
|
expected = [
|
312
|
-
|
313
|
-
|
314
|
-
[true, true, 2],
|
315
|
-
[false, false, 3],
|
316
|
-
[false, true, 2]
|
200
|
+
{"< 5" => true, :count => 4},
|
201
|
+
{"< 5" => false, :count => 5}
|
317
202
|
]
|
318
203
|
|
319
|
-
assert
|
204
|
+
assert rollup.result.to_hash_rows == expected
|
320
205
|
end
|
321
206
|
|
322
|
-
|
323
|
-
list = [1,2,3,4,5,6,7,8,9]
|
324
|
-
list = Goldmine::ArrayMiner.new(list)
|
325
|
-
rolled = list.pivot("less than 5") { |i| i < 5 }.pivot("divisible by 2") { |i| i % 2 == 0 }.rollup(:count, &:size)
|
326
|
-
csv = rolled.to_csv_table
|
327
|
-
|
328
|
-
assert csv.to_a == rolled.to_tabular
|
329
|
-
|
330
|
-
expected = ["less than 5", "divisible by 2", "count"]
|
331
|
-
assert csv.headers == expected
|
332
|
-
|
333
|
-
row = csv.first
|
334
|
-
assert row["less than 5"] == true
|
335
|
-
assert row["divisible by 2"] == false
|
336
|
-
assert row ["count"] == 2
|
337
|
-
end
|
207
|
+
# to_tabular ................................................................
|
338
208
|
|
339
|
-
test "
|
209
|
+
test "simple pivot rollup to_tabular" do
|
340
210
|
list = [1,2,3,4,5,6,7,8,9]
|
341
|
-
|
342
|
-
|
343
|
-
.
|
211
|
+
rollup = Goldmine(list)
|
212
|
+
.pivot("< 5") { |i| i < 5 }
|
213
|
+
.result
|
344
214
|
.rollup(:count, &:size)
|
345
|
-
.rollup(:evens) { |l| l.select { |i| i % 2 == 0 }.size }
|
346
|
-
.rollup(:even_percentage) { |l| computed(:evens).for(l) / computed(:count).for(l).to_f }
|
347
215
|
|
348
216
|
expected = [
|
349
|
-
|
350
|
-
|
217
|
+
["< 5", :count],
|
218
|
+
[true, 4],
|
219
|
+
[false, 5]
|
351
220
|
]
|
352
221
|
|
353
|
-
assert
|
222
|
+
assert rollup.result.to_tabular == expected
|
354
223
|
end
|
355
224
|
|
356
|
-
test "
|
225
|
+
test "chained pivots rollup to_tabular" do
|
357
226
|
list = [1,2,3,4,5,6,7,8,9]
|
358
|
-
|
359
|
-
|
360
|
-
.pivot(:
|
227
|
+
rollup = Goldmine(list)
|
228
|
+
.pivot("< 5") { |i| i < 5 }
|
229
|
+
.pivot(:even) { |i| i % 2 == 0 }
|
230
|
+
.result
|
361
231
|
.rollup(:count, &:size)
|
362
|
-
.rollup(:evens) { |l| l.select { |i| i % 2 == 0 }.size }
|
363
|
-
.rollup(:even_percentage) { |l| computed(:evens).for(l) / computed(:count).for(l).to_f }
|
364
232
|
|
365
233
|
expected = [
|
366
|
-
|
367
|
-
|
234
|
+
["< 5", :even, :count],
|
235
|
+
[true, false, 2],
|
236
|
+
[true, true, 2],
|
237
|
+
[false, false, 3],
|
238
|
+
[false, true, 2]
|
368
239
|
]
|
369
240
|
|
370
|
-
assert
|
241
|
+
assert rollup.result.to_tabular == expected
|
371
242
|
end
|
372
243
|
|
373
|
-
|
244
|
+
# to_csv_table ..............................................................
|
245
|
+
|
246
|
+
test "simple pivot rollup to_csv_table" do
|
374
247
|
list = [1,2,3,4,5,6,7,8,9]
|
375
|
-
|
376
|
-
|
377
|
-
.
|
248
|
+
rollup = Goldmine(list)
|
249
|
+
.pivot("< 5") { |i| i < 5 }
|
250
|
+
.result
|
378
251
|
.rollup(:count, &:size)
|
379
|
-
.rollup(:evens) { |pivoted_list| pivoted_list.select { |i| i % 2 == 0 }.size }
|
380
|
-
.rollup(:even_percentage) { |pivoted_list| computed(:evens).for(pivoted_list) / computed(:count).for(pivoted_list).to_f }
|
381
252
|
|
382
|
-
|
383
|
-
|
384
|
-
[true, 4, 2, 0.5],
|
385
|
-
[false, 5, 2, 0.4]
|
386
|
-
]
|
387
|
-
assert rolled.to_tabular == expected
|
253
|
+
csv = rollup.result.to_csv_table
|
254
|
+
assert csv.to_s == "< 5,count\ntrue,4\nfalse,5\n"
|
388
255
|
end
|
389
256
|
|
390
|
-
|
257
|
+
# pivot_result cache ..........................................................
|
258
|
+
|
259
|
+
test "pivot_result cache is available to rollups" do
|
391
260
|
list = [1,2,3,4,5,6,7,8,9]
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
.
|
396
|
-
.rollup(:
|
261
|
+
cached_counts = []
|
262
|
+
Goldmine(list)
|
263
|
+
.pivot("< 5") { |i| i < 5 }
|
264
|
+
.result
|
265
|
+
.rollup(:count, &:size)
|
266
|
+
.rollup(:cached_count) { |_| cached_counts << cache[:count] }
|
267
|
+
.result(cache: true)
|
397
268
|
|
398
|
-
|
399
|
-
assert
|
269
|
+
assert cached_counts.size == 2
|
270
|
+
assert cached_counts.first == 4
|
271
|
+
assert cached_counts.last == 5
|
400
272
|
end
|
401
273
|
|
402
274
|
end
|