object_table 0.2.4 → 0.2.8

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,15 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: fcf93afcb7a81c3a9fb40c8ffc7d8b1bd506c845
4
- data.tar.gz: 9d6a155f502215a3f57639108bfe908ea4e853f8
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ YzA3YzQ3OTc1OGZiMzYwN2NlNGM0Y2UxN2ExOTJjNjQxODdiZTdkNQ==
5
+ data.tar.gz: !binary |-
6
+ MzdhYWU4NmI2MWNhOGM2YTk4MzAwNGRiYTAyZDg1OWE4YWM2ZWZmNg==
5
7
  SHA512:
6
- metadata.gz: 0a4a23f3e527ee524e2d528637ed939caaa0e48bde230df277cfaccbb50a4f62824081be6990fd3307cc5108f485066ecb05f2d79563999b6365154ab04151e7
7
- data.tar.gz: b23605bf82b78fb04fce0a4f0f4a8f6e049a2f3b15fbf3c9ee2029f71370f9b0fc2e064269edbe7bbb110cfa6a79ca8d3acfb2450292efe36df34a0c4676a23e
8
+ metadata.gz: !binary |-
9
+ MmU2MTA0NGY3NzA1ZGM3NDVlOWJiOTBmM2NiYWM3OTNkZjdiM2NhZTQzMDUz
10
+ Zjg5NzYwOWZmMzUyMzE3MWIyZjE2ZGEwNTRhZTI2ZjlhMjhkZDNhNzgyNWNj
11
+ N2Y0YWExNDQ5NzU2MGFjNzdhMmY1NWY1ZTYxOWVhNGU5YjliODc=
12
+ data.tar.gz: !binary |-
13
+ YTZjYTFmMjljNGQzYjkzY2U1OTU5MTQ3ZjllMDkyMDBiYjgxYzE2NDk5NmE3
14
+ MDU2YWNhNTkzOGUzZTc2ZTk2MGIyYjUwOTIzZjI0NmI0M2RkNmRlNDZjMjAz
15
+ M2VkZWMzYWE4MDI3MmVkOWE5ZmRhZTg0ZWFlODEwMDllMGE5YjQ=
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - 2.1.0
data/lib/object_table.rb CHANGED
@@ -43,17 +43,16 @@ class ObjectTable
43
43
 
44
44
  raise "Don't know how to append a #{x.class}" unless x.is_a?(ObjectTable::BasicGrid)
45
45
  next if x.empty?
46
- raise 'Mismatch in column names' unless (colnames | x.keys) == (colnames & x.keys)
46
+ raise 'Mismatch in column names' unless (colnames - x.keys).empty?
47
47
 
48
48
  x.each do |k, v|
49
- v = v.to_a if v.is_a?(Range)
50
- new_values[k].push NArray.to_na(v)
49
+ unless new_values.include?(k) and v.empty?
50
+ new_values[k].push(NArray.to_na(v))
51
+ end
51
52
  end
52
53
  end
53
54
 
54
- return self if new_values.empty?
55
- new_rows = new_values.values.first.map{|x| x.shape[-1]}.reduce(:+)
56
- return self unless (new_rows and new_rows != 0)
55
+ return self if new_values.values.first.empty?
57
56
 
58
57
  new_values.each do |k, v|
59
58
  @columns[k] = @columns[k].stack(*v)
@@ -1,30 +1,32 @@
1
1
  require 'narray'
2
2
 
3
3
  class ObjectTable::BasicGrid < Hash
4
- ARRAY_LIKE = [Array, Range]
5
-
6
4
  # def self.[](*args)
7
5
  # grid = super
8
6
  # grid._ensure_uniform_columns!
9
7
  # end
10
8
 
11
- def _ensure_uniform_columns!(rows = nil)
12
- arrays, scalars = partition{|k, v| ARRAY_LIKE.any?{|cls| v.is_a?(cls)} }
13
- narrays, scalars = scalars.partition{|k, v| v.is_a?(NArray) }
9
+ def _get_number_rows!
10
+ each{|k, v| self[k] = v.to_a if v.is_a?(Range)}
11
+ rows = map do |k, v|
12
+ case v
13
+ when Array
14
+ v.length
15
+ when NArray
16
+ v.shape.last or 0
17
+ end
18
+ end.compact.uniq
19
+ end
14
20
 
15
- unique_rows = arrays.map{|k, v| v.count}
16
- unique_rows += narrays.map{|k, v| v.shape.last or 0}
17
- unique_rows = unique_rows.uniq
21
+ def _ensure_uniform_columns!(rows = nil)
22
+ unique_rows = _get_number_rows!
23
+ unique_rows |= [rows] if rows
18
24
 
19
- if rows
20
- raise "Differing number of rows: #{unique_rows}" unless unique_rows.empty? or unique_rows == [rows]
21
- else
22
- raise "Differing number of rows: #{unique_rows}" if unique_rows.length > 1
23
- rows = (unique_rows[0] or 1)
24
- end
25
+ raise "Differing number of rows: #{unique_rows}" if unique_rows.length > 1
26
+ rows = (unique_rows[0] or 1)
25
27
 
26
- scalars.each do |k, v|
27
- self[k] = [v] * rows
28
+ each do |k, v|
29
+ self[k] = [v] * rows unless (v.is_a?(Array) || v.is_a?(NArray))
28
30
  end
29
31
 
30
32
  self
@@ -11,12 +11,11 @@ class ObjectTable::Grouped
11
11
  @names = names
12
12
  end
13
13
 
14
-
15
14
  def _groups
16
15
  names, keys = _keys()
17
16
  groups = keys.length.times.group_by{|i| keys[i]}
18
17
  groups.each do |k, v|
19
- groups[k] = NArray.to_na(v)
18
+ groups[k] = v
20
19
  end
21
20
  [names, groups]
22
21
  end
@@ -26,11 +25,11 @@ class ObjectTable::Grouped
26
25
  keys = @parent.apply(&@grouper)
27
26
  raise 'Group keys must be hashes' unless keys.is_a?(Hash)
28
27
  keys = ObjectTable::BasicGrid.new.replace keys
28
+ keys._ensure_uniform_columns!(@parent.nrows)
29
29
  else
30
30
  keys = ObjectTable::BasicGrid[@names.map{|n| [n, @parent.get_column(n)]}]
31
31
  end
32
32
 
33
- keys._ensure_uniform_columns!(@parent.nrows)
34
33
  names = keys.keys
35
34
  keys = keys.values.map(&:to_a).transpose
36
35
  [names, keys]
@@ -38,15 +37,8 @@ class ObjectTable::Grouped
38
37
 
39
38
  def each(&block)
40
39
  names, groups = _groups()
41
- if block
42
- groups.each do |k, v|
43
- keys = names.zip(k)
44
- __group_cls__.new(@parent, Hash[keys], v).apply &block
45
- end
46
- return @parent
47
- end
48
40
 
49
- Enumerator.new(groups.length) do |y|
41
+ enumerator = Enumerator.new do |y|
50
42
  groups.each do |k, v|
51
43
  keys = names.zip(k)
52
44
  y.yield __group_cls__.new(@parent, Hash[keys], v)
@@ -54,6 +46,8 @@ class ObjectTable::Grouped
54
46
  @parent
55
47
  end
56
48
 
49
+ return enumerator unless block
50
+ enumerator.each{|grp| grp._apply_block(&block)}
57
51
  end
58
52
 
59
53
  def apply(&block)
@@ -62,19 +56,31 @@ class ObjectTable::Grouped
62
56
 
63
57
  data = groups.map do |k, v|
64
58
  keys = names.zip(k)
65
- value = __group_cls__.new(@parent, Hash[keys], v).apply &block
59
+ value = __group_cls__.new(@parent, Hash[keys], v)._apply_block &block
66
60
 
67
- if value.is_a?(ObjectTable::TableMethods)
61
+ case value
62
+ when ObjectTable::TableMethods
63
+ nrows = value.nrows
68
64
  value = value.columns
65
+ when ObjectTable::BasicGrid
66
+ value._ensure_uniform_columns!
67
+ nrows = NArray.to_na(value.values.first).shape[-1]
68
+ when Array
69
+ nrows = value.length
70
+ when NArray
71
+ nrows = value.shape.last
72
+ else
73
+ nrows = 1
69
74
  end
70
75
 
76
+ keys = names.zip(k.map{|x| [x] * nrows})
77
+
71
78
  grid = case value
72
79
  when ObjectTable::BasicGrid
73
80
  ObjectTable::BasicGrid[keys].merge!(value)
74
81
  else
75
82
  ObjectTable::BasicGrid[keys + [[value_key, value]]]
76
83
  end
77
- grid._ensure_uniform_columns!
78
84
  end
79
85
 
80
86
  __table_cls__.stack(*data)
@@ -10,11 +10,23 @@ class ObjectTable::StaticView
10
10
  super()
11
11
  @parent = parent
12
12
  @indices = indices
13
- columns
13
+ @columns = ObjectTable::BasicGrid.new
14
+ @fully_cached = false
14
15
  end
15
16
 
16
17
  def columns
17
- @columns ||= super
18
+ unless @fully_cached
19
+ @parent.columns.map{|k, v| get_column(k)}
20
+ @fully_cached = true
21
+ end
22
+ @columns
23
+ end
24
+
25
+ def get_column(name)
26
+ @columns.fetch(name) do
27
+ col = super
28
+ @columns[name] = col if col
29
+ end
18
30
  end
19
31
 
20
32
  def add_column(name, *args)
@@ -67,16 +67,18 @@ module ObjectTable::TableMethods
67
67
  end
68
68
 
69
69
  def apply(&block)
70
+ result = _apply_block(&block)
71
+
72
+ return result unless result.is_a? ObjectTable::BasicGrid
73
+ __table_cls__.new(result)
74
+ end
75
+
76
+ def _apply_block(&block)
70
77
  if block.arity == 0
71
78
  result = instance_eval &block
72
79
  else
73
80
  result = block.call(self)
74
81
  end
75
-
76
- if result.is_a? ObjectTable::BasicGrid
77
- result = __table_cls__.new(result)
78
- end
79
- result
80
82
  end
81
83
 
82
84
  def where(&block)
@@ -1,3 +1,3 @@
1
1
  class ObjectTable
2
- VERSION = "0.2.4"
2
+ VERSION = "0.2.8"
3
3
  end
@@ -16,12 +16,6 @@ class ObjectTable::View
16
16
 
17
17
  def_delegators :@parent, :has_column?
18
18
 
19
- def get_column(name)
20
- col = @parent.get_column(name)
21
- ObjectTable::MaskedColumn.mask(col, indices) if col
22
- end
23
- alias_method :[], :get_column
24
-
25
19
  def make_view
26
20
  __static_view_cls__.new @parent, indices
27
21
  end
@@ -37,7 +31,7 @@ class ObjectTable::View
37
31
  end
38
32
 
39
33
  def indices
40
- @indices or NArray.int(@parent.nrows).indgen![@parent.apply &@filter]
34
+ @indices or NArray.int(@parent.nrows).indgen![@parent._apply_block &@filter]
41
35
  end
42
36
 
43
37
  def cache_indices(&block)
@@ -7,10 +7,20 @@ module ObjectTable::ViewMethods
7
7
  include ObjectTable::TableMethods
8
8
  include ObjectTable::TableChild
9
9
 
10
+ def nrows
11
+ indices.length
12
+ end
13
+
10
14
  def columns
11
15
  ObjectTable::BasicGrid[@parent.columns.map{|k, v| [k, ObjectTable::MaskedColumn.mask(v, indices)]}]
12
16
  end
13
17
 
18
+ def get_column(name)
19
+ col = @parent.get_column(name)
20
+ ObjectTable::MaskedColumn.mask(col, indices) if col
21
+ end
22
+ alias_method :[], :get_column
23
+
14
24
  def add_column(name, *args)
15
25
  col = @parent.add_column(name, *args)
16
26
  ObjectTable::MaskedColumn.mask(col, indices)
@@ -10,12 +10,85 @@ describe ObjectTable::BasicGrid do
10
10
  # end
11
11
  # end
12
12
 
13
+ describe '#_get_number_rows!' do
14
+ let(:grid){ ObjectTable::BasicGrid[columns] }
15
+
16
+ subject{ grid._get_number_rows! }
17
+
18
+ context 'with columns of the same length' do
19
+ let(:columns){ {col1: [1, 2, 3], col2: [1, 2, 3]} }
20
+ it 'should give unique column lengths' do
21
+ expect(subject).to match_array [3]
22
+ end
23
+ end
24
+
25
+ context 'with columns of differing length' do
26
+ let(:columns){ {col1: [1, 2, 3], col2: [1, 2, 3, 4]} }
27
+ it 'should return the column lengths' do
28
+ expect(subject).to match_array [3, 4]
29
+ end
30
+ end
31
+
32
+ context 'with a mix of scalars and columns' do
33
+ let(:columns){ {col1: [1, 2, 3], col2: [1, 2, 3], col3: 6} }
34
+ it 'should ignore the scalars' do
35
+ expect(subject).to match_array [3]
36
+ end
37
+ end
38
+
39
+ context 'with scalars only' do
40
+ let(:columns){ {col1: 1, col2: 2} }
41
+ it 'should be empty' do
42
+ expect(subject).to be_empty
43
+ end
44
+ end
45
+
46
+ context 'with ranges' do
47
+ let(:columns){ {col1: 0...3} }
48
+ it 'should convert them to arrays' do
49
+ subject
50
+ expect(grid[:col1]).to eql columns[:col1].to_a
51
+ end
52
+
53
+ it 'should use the length of the range' do
54
+ expect(subject).to match_array [3]
55
+ end
56
+ end
57
+
58
+ context 'with multi dimensional narrays' do
59
+ context 'with the same last dimension' do
60
+ let(:columns) { {col1: NArray[[1, 1], [2, 2], [3, 3]], col2: [1, 2, 3]} }
61
+
62
+ it 'should include the last dimension' do
63
+ expect(subject).to match_array [3]
64
+ end
65
+ end
66
+
67
+ context 'with a different last dimension' do
68
+ let(:columns) { {col1: NArray[[1, 2, 3]], col2: [1, 2, 3]} }
69
+
70
+ it 'should include the last dimension' do
71
+ expect(subject).to match_array [1, 3]
72
+ end
73
+ end
74
+ end
75
+
76
+ context 'with empty narrays' do
77
+ let(:columns) { {col1: [1, 2, 3], col2: NArray[]} }
78
+
79
+ it 'should treat them as having zero length' do
80
+ expect(subject).to match_array [3, 0]
81
+ end
82
+ end
83
+
84
+ end
85
+
13
86
  describe '#_ensure_uniform_columns!' do
14
87
  let(:grid){ ObjectTable::BasicGrid[columns] }
15
88
 
16
89
  subject{ grid._ensure_uniform_columns! }
17
90
 
18
- context 'with rows of the same length' do
91
+ context 'with columns of the same length' do
19
92
  let(:columns){ {col1: [1, 2, 3], col2: [1, 2, 3]} }
20
93
  it 'should succeed' do
21
94
  subject
@@ -24,14 +97,14 @@ describe ObjectTable::BasicGrid do
24
97
  end
25
98
  end
26
99
 
27
- context 'with rows of differing length' do
100
+ context 'with columns of differing length' do
28
101
  let(:columns){ {col1: [1, 2, 3], col2: [1, 2, 3, 4]} }
29
102
  it 'should fail' do
30
103
  expect{subject}.to raise_error
31
104
  end
32
105
  end
33
106
 
34
- context 'with a mix of scalars and rows' do
107
+ context 'with a mix of scalars and columns' do
35
108
  let(:columns){ {col1: [1, 2, 3], col2: [1, 2, 3], col3: 6} }
36
109
  it 'should recycle the scalar into a full column' do
37
110
  subject
@@ -41,7 +114,7 @@ describe ObjectTable::BasicGrid do
41
114
 
42
115
  context 'with scalars only' do
43
116
  let(:columns){ {col1: 1, col2: 2} }
44
- it 'should assume there is one row' do
117
+ it 'should assume there is one column' do
45
118
  subject
46
119
  expect(grid[:col1]).to eql [columns[:col1]]
47
120
  expect(grid[:col2]).to eql [columns[:col2]]
@@ -51,12 +124,11 @@ describe ObjectTable::BasicGrid do
51
124
  context 'with ranges' do
52
125
  let(:columns){ {col1: 0...3} }
53
126
  it 'should succeed' do
54
- subject
55
- expect(grid[:col1]).to eql columns[:col1]
127
+ expect{subject}.to_not raise_error
56
128
  end
57
129
  end
58
130
 
59
- context 'with rank>2 narrays' do
131
+ context 'with multi dimensional narrays' do
60
132
  context 'with the correct last dimension' do
61
133
  let(:columns) { {col1: NArray[[1, 1], [2, 2], [3, 3]], col2: [1, 2, 3]} }
62
134
 
@@ -87,10 +159,10 @@ describe ObjectTable::BasicGrid do
87
159
 
88
160
 
89
161
  context 'with other non-empty columns' do
90
- let(:columns) { {col1: [], col2: NArray[]} }
162
+ let(:columns) { {col1: [], col2: NArray[], col3: [1, 2, 3]} }
91
163
 
92
164
  it 'should fail' do
93
- expect{subject}.to_not raise_error
165
+ expect{subject}.to raise_error
94
166
  end
95
167
  end
96
168
  end
@@ -71,9 +71,9 @@ describe ObjectTable::Grouped do
71
71
  subject{ grouped }
72
72
 
73
73
  it 'should mirror changes to the parent' do
74
- expect(subject._groups[1]).to eql ({[0] => NArray[1, 3], [1] => NArray[0, 2]})
74
+ expect(subject._groups[1]).to eql ({[0] => [1, 3], [1] => [0, 2]})
75
75
  table[:col1] = [2, 3, 4, 5]
76
- expect(subject._groups[1]).to eql ({[0] => NArray[0, 2], [1] => NArray[1, 3]})
76
+ expect(subject._groups[1]).to eql ({[0] => [0, 2], [1] => [1, 3]})
77
77
  end
78
78
  end
79
79
 
@@ -86,8 +86,8 @@ describe ObjectTable::Grouped do
86
86
 
87
87
  it 'should return the group key => row mapping' do
88
88
  groups = subject[1]
89
- expect(groups[[0]]).to eql even
90
- expect(groups[[1]]).to eql odd
89
+ expect(groups[[0]]).to eql even.to_a
90
+ expect(groups[[1]]).to eql odd.to_a
91
91
  end
92
92
 
93
93
  context 'when grouping by columns' do
@@ -100,10 +100,10 @@ describe ObjectTable::Grouped do
100
100
 
101
101
  it 'should use the columns as groups' do
102
102
  groups = subject[1]
103
- expect(groups[[0, 0]]).to eql (table.key1.eq(0) & table.key2.eq(0)).where
104
- expect(groups[[0, 1]]).to eql (table.key1.eq(0) & table.key2.eq(1)).where
105
- expect(groups[[1, 0]]).to eql (table.key1.eq(1) & table.key2.eq(0)).where
106
- expect(groups[[1, 1]]).to eql (table.key1.eq(1) & table.key2.eq(1)).where
103
+ expect(groups[[0, 0]]).to eql (table.key1.eq(0) & table.key2.eq(0)).where.to_a
104
+ expect(groups[[0, 1]]).to eql (table.key1.eq(0) & table.key2.eq(1)).where.to_a
105
+ expect(groups[[1, 0]]).to eql (table.key1.eq(1) & table.key2.eq(0)).where.to_a
106
+ expect(groups[[1, 1]]).to eql (table.key1.eq(1) & table.key2.eq(1)).where.to_a
107
107
  end
108
108
  end
109
109
  end
@@ -249,6 +249,40 @@ describe ObjectTable::Grouped do
249
249
  end
250
250
  end
251
251
  end
252
+
253
+ context 'with a matrix key' do
254
+ let(:ngroups) { 10 }
255
+ let(:table) do
256
+ ObjectTable.new(
257
+ key1: 10.times.map{[rand, 'abc']} * ngroups,
258
+ key2: 10.times.map{[rand, 'def', 'ghi']} * ngroups,
259
+ value: (ngroups*10).times.map{rand},
260
+ )
261
+ end
262
+
263
+ let(:grouped) { ObjectTable::Grouped.new(table, :key1, :key2) }
264
+ subject{ grouped.apply{|group| group.value.sum} }
265
+
266
+ it 'should return a table with the group keys' do
267
+ expect(subject).to be_a ObjectTable
268
+ expect(subject.colnames).to include :key1
269
+ expect(subject.colnames).to include :key2
270
+ end
271
+
272
+ it 'should preserve the dimensions of the keys' do
273
+ expect(subject.key1.shape[0...-1]).to eql table.key1.shape[0...-1]
274
+ expect(subject.key2.shape[0...-1]).to eql table.key2.shape[0...-1]
275
+ end
276
+
277
+ context 'with vector values' do
278
+ subject{ grouped.apply{|group| group.value[0...10]} }
279
+
280
+ it 'should work' do
281
+ expect{subject}.to_not raise_error
282
+ end
283
+ end
284
+ end
285
+
252
286
  end
253
287
 
254
288
  end
@@ -246,8 +246,16 @@ describe ObjectTable do
246
246
  end
247
247
  end
248
248
 
249
- context 'with differing column names' do
250
- let(:others){ [ObjectTable.new(col1: 10, col2: 50), ObjectTable.new(col1: 10, col3: 50)] }
249
+ context 'with extra column names' do
250
+ let(:others){ [ObjectTable.new(col1: 10, col2: 50), ObjectTable.new(col1: 10, col2: 30, col3: 50)] }
251
+
252
+ it 'should fail' do
253
+ expect{subject}.to raise_error
254
+ end
255
+ end
256
+
257
+ context 'with missing column names' do
258
+ let(:others){ [ObjectTable.new(col1: 10, col2: 50), ObjectTable.new(col1: 10)] }
251
259
 
252
260
  it 'should fail' do
253
261
  expect{subject}.to raise_error
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: object_table
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.4
4
+ version: 0.2.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Cheney Lin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-01-20 00:00:00.000000000 Z
11
+ date: 2015-02-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: narray
@@ -42,14 +42,14 @@ dependencies:
42
42
  name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - '>='
45
+ - - ! '>='
46
46
  - !ruby/object:Gem::Version
47
47
  version: '0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - '>='
52
+ - - ! '>='
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
@@ -74,6 +74,7 @@ extensions: []
74
74
  extra_rdoc_files: []
75
75
  files:
76
76
  - .gitignore
77
+ - .travis.yml
77
78
  - Gemfile
78
79
  - LICENSE
79
80
  - README.md
@@ -112,12 +113,12 @@ require_paths:
112
113
  - lib
113
114
  required_ruby_version: !ruby/object:Gem::Requirement
114
115
  requirements:
115
- - - '>='
116
+ - - ! '>='
116
117
  - !ruby/object:Gem::Version
117
118
  version: '0'
118
119
  required_rubygems_version: !ruby/object:Gem::Requirement
119
120
  requirements:
120
- - - '>='
121
+ - - ! '>='
121
122
  - !ruby/object:Gem::Version
122
123
  version: '0'
123
124
  requirements: []