ntable 0.1.3 → 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- data/History.rdoc +5 -0
- data/Version +1 -1
- data/lib/ntable/axis.rb +28 -4
- data/lib/ntable/construction.rb +12 -3
- data/lib/ntable/index_wrapper.rb +11 -0
- data/lib/ntable/structure.rb +43 -5
- data/lib/ntable/table.rb +8 -1
- data/test/tc_nested_object.rb +12 -2
- data/test/tc_structure.rb +16 -0
- metadata +3 -3
data/History.rdoc
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
+
=== 0.1.4 / 2012-09-24
|
2
|
+
|
3
|
+
* NTable::Structure and NTable::AxisInfo are now Enumerable, letting you iterate over the axes and labels, respectively.
|
4
|
+
* You can now postprocess the labels generated by NTable.from_nested_object. Useful for filling in labels missing in the data.
|
5
|
+
|
1
6
|
=== 0.1.3 / 2012-09-10
|
2
7
|
|
3
8
|
* Creating a table from a nested object, using a label conversion procedure, tended to fail with NoSuchCellError. Fixed and added test cases.
|
data/Version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.1.
|
1
|
+
0.1.4
|
data/lib/ntable/axis.rb
CHANGED
@@ -131,40 +131,45 @@ module NTable
|
|
131
131
|
end
|
132
132
|
|
133
133
|
|
134
|
+
# See EmptyAxis#eql?
|
134
135
|
def eql?(obj_)
|
135
136
|
obj_.is_a?(LabeledAxis) && @a.eql?(obj_.instance_variable_get(:@a))
|
136
137
|
end
|
138
|
+
alias_method :==, :eql?
|
137
139
|
|
138
|
-
|
139
|
-
obj_.is_a?(LabeledAxis) && @a == obj_.instance_variable_get(:@a)
|
140
|
-
end
|
141
|
-
|
140
|
+
# See EmptyAxis#hash
|
142
141
|
def hash
|
143
142
|
@a.hash
|
144
143
|
end
|
145
144
|
|
145
|
+
# See EmptyAxis#inspect
|
146
146
|
def inspect
|
147
147
|
"#<#{self.class}:0x#{object_id.to_s(16)} #{@a.inspect}>"
|
148
148
|
end
|
149
149
|
alias_method :to_s, :inspect
|
150
150
|
|
151
151
|
|
152
|
+
# See EmptyAxis#size
|
152
153
|
attr_reader :size
|
153
154
|
|
154
155
|
|
156
|
+
# See EmptyAxis#index
|
155
157
|
def index(label_)
|
156
158
|
@h[label_.to_s]
|
157
159
|
end
|
158
160
|
|
161
|
+
# See EmptyAxis#label
|
159
162
|
def label(index_)
|
160
163
|
@a[index_]
|
161
164
|
end
|
162
165
|
|
163
166
|
|
167
|
+
# See EmptyAxis#to_json_object
|
164
168
|
def to_json_object(json_obj_)
|
165
169
|
json_obj_['labels'] = @a
|
166
170
|
end
|
167
171
|
|
172
|
+
# See EmptyAxis#from_json_object
|
168
173
|
def from_json_object(json_obj_)
|
169
174
|
initialize(json_obj_['labels'] || [])
|
170
175
|
end
|
@@ -188,39 +193,49 @@ module NTable
|
|
188
193
|
end
|
189
194
|
|
190
195
|
|
196
|
+
# See EmptyAxis#eql?
|
191
197
|
def eql?(obj_)
|
192
198
|
obj_.is_a?(IndexedAxis) && obj_.size.eql?(@size) && obj_.start.eql?(@start)
|
193
199
|
end
|
194
200
|
alias_method :==, :eql?
|
195
201
|
|
202
|
+
# See EmptyAxis#hash
|
196
203
|
def hash
|
197
204
|
@size.hash ^ @start.hash
|
198
205
|
end
|
199
206
|
|
207
|
+
# See EmptyAxis#inspect
|
200
208
|
def inspect
|
201
209
|
"#<#{self.class}:0x#{object_id.to_s(16)} size=#{@size} start=#{@start}>"
|
202
210
|
end
|
203
211
|
alias_method :to_s, :inspect
|
204
212
|
|
205
213
|
|
214
|
+
# See EmptyAxis#size
|
206
215
|
attr_reader :size
|
216
|
+
|
217
|
+
# Retrieve the number of the first row
|
207
218
|
attr_reader :start
|
208
219
|
|
209
220
|
|
221
|
+
# See EmptyAxis#index
|
210
222
|
def index(label_)
|
211
223
|
label_ >= @start && label_ < @size + @start ? label_ - @start : nil
|
212
224
|
end
|
213
225
|
|
226
|
+
# See EmptyAxis#label
|
214
227
|
def label(index_)
|
215
228
|
index_ >= 0 && index_ < @size ? index_ + @start : nil
|
216
229
|
end
|
217
230
|
|
218
231
|
|
232
|
+
# See EmptyAxis#to_json_object
|
219
233
|
def to_json_object(json_obj_)
|
220
234
|
json_obj_['size'] = @size
|
221
235
|
json_obj_['start'] = @start unless @start == 0
|
222
236
|
end
|
223
237
|
|
238
|
+
# See EmptyAxis#from_json_object
|
224
239
|
def from_json_object(json_obj_)
|
225
240
|
initialize(json_obj_['size'], json_obj_['start'].to_i)
|
226
241
|
end
|
@@ -245,40 +260,49 @@ module NTable
|
|
245
260
|
end
|
246
261
|
|
247
262
|
|
263
|
+
# See EmptyAxis#eql?
|
248
264
|
def eql?(obj_)
|
249
265
|
obj_.is_a?(ObjectAxis) && @a.eql?(obj_.instance_variable_get(:@a))
|
250
266
|
end
|
251
267
|
|
268
|
+
# See EmptyAxis#==
|
252
269
|
def ==(obj_)
|
253
270
|
obj_.is_a?(ObjectAxis) && @a == obj_.instance_variable_get(:@a)
|
254
271
|
end
|
255
272
|
|
273
|
+
# See EmptyAxis#hash
|
256
274
|
def hash
|
257
275
|
@a.hash
|
258
276
|
end
|
259
277
|
|
278
|
+
# See EmptyAxis#inspect
|
260
279
|
def inspect
|
261
280
|
"#<#{self.class}:0x#{object_id.to_s(16)} #{@a.inspect}>"
|
262
281
|
end
|
263
282
|
alias_method :to_s, :inspect
|
264
283
|
|
265
284
|
|
285
|
+
# See EmptyAxis#size
|
266
286
|
attr_reader :size
|
267
287
|
|
268
288
|
|
289
|
+
# See EmptyAxis#index
|
269
290
|
def index(label_)
|
270
291
|
@h[label_]
|
271
292
|
end
|
272
293
|
|
294
|
+
# See EmptyAxis#label
|
273
295
|
def label(index_)
|
274
296
|
@a[index_]
|
275
297
|
end
|
276
298
|
|
277
299
|
|
300
|
+
# See EmptyAxis#to_json_object
|
278
301
|
def to_json_object(json_obj_)
|
279
302
|
raise "Unable to JSON serialize an ObjectAxis"
|
280
303
|
end
|
281
304
|
|
305
|
+
# See EmptyAxis#from_json_object
|
282
306
|
def from_json_object(json_obj_)
|
283
307
|
raise "Unable to JSON serialize an ObjectAxis"
|
284
308
|
end
|
data/lib/ntable/construction.rb
CHANGED
@@ -41,9 +41,9 @@ require 'json'
|
|
41
41
|
module NTable
|
42
42
|
|
43
43
|
|
44
|
-
@numeric_sort =
|
45
|
-
@integer_sort =
|
46
|
-
@string_sort =
|
44
|
+
@numeric_sort = ->(a_, b_){ a_.to_f <=> b_.to_f }
|
45
|
+
@integer_sort = ->(a_, b_){ a_.to_i <=> b_.to_i }
|
46
|
+
@string_sort = ->(a_, b_){ a_.to_s <=> b_.to_s }
|
47
47
|
|
48
48
|
|
49
49
|
class << self
|
@@ -119,6 +119,13 @@ module NTable
|
|
119
119
|
# proc is provided, the resulting axis will be a LabeledAxis.
|
120
120
|
# You can also pass true instead of a Proc; this will create an
|
121
121
|
# LabeledAxis and make the conversion a simple to_s.
|
122
|
+
# [<tt>:postprocess</tt>]
|
123
|
+
# An optional Proc that postprocesses the final labels array.
|
124
|
+
# It should take an array of labels and return a modified array
|
125
|
+
# (which can be the original array modified in place). Called
|
126
|
+
# after any sort has been completed.
|
127
|
+
# You can use this, for example, to "fill in" labels that were
|
128
|
+
# not present in the original data.
|
122
129
|
#
|
123
130
|
# The third argument is an optional hash of miscellaneous options.
|
124
131
|
# The following keys are recognized:
|
@@ -188,6 +195,8 @@ module NTable
|
|
188
195
|
end
|
189
196
|
labels_.sort!(&func_)
|
190
197
|
end
|
198
|
+
postprocess_ = field_[:postprocess]
|
199
|
+
labels_ = postprocess_.call(labels_) if postprocess_.respond_to?(:call)
|
191
200
|
axis_ = klass_.new(labels_)
|
192
201
|
when ::Array
|
193
202
|
axis_ = IndexedAxis.new(ai_[1].to_i - ai_[0].to_i, ai_[0].to_i)
|
data/lib/ntable/index_wrapper.rb
CHANGED
@@ -42,24 +42,35 @@ module NTable
|
|
42
42
|
|
43
43
|
class IndexWrapper
|
44
44
|
|
45
|
+
|
45
46
|
# Create an IndexWrapper with the given integer index
|
46
47
|
|
47
48
|
def initialize(val_)
|
48
49
|
@value = val_.to_i
|
49
50
|
end
|
50
51
|
|
52
|
+
|
53
|
+
# Standard equality checker
|
54
|
+
|
51
55
|
def eql?(rhs_)
|
52
56
|
rhs_.is_a?(IndexWrapper) && @value == @rhs_.value
|
53
57
|
end
|
54
58
|
alias_method :==, :eql?
|
55
59
|
|
60
|
+
|
61
|
+
# Standard hash value
|
62
|
+
|
56
63
|
def hash
|
57
64
|
@value.hash
|
58
65
|
end
|
59
66
|
|
67
|
+
|
68
|
+
# Retrieve the actual index
|
69
|
+
|
60
70
|
attr_reader :value
|
61
71
|
alias_method :to_i, :value
|
62
72
|
|
73
|
+
|
63
74
|
end
|
64
75
|
|
65
76
|
|
data/lib/ntable/structure.rb
CHANGED
@@ -104,6 +104,7 @@ module NTable
|
|
104
104
|
def label(index_)
|
105
105
|
@axis_object.label(index_)
|
106
106
|
end
|
107
|
+
alias_method :[], :label
|
107
108
|
|
108
109
|
|
109
110
|
# Return the number of rows along this axis.
|
@@ -114,10 +115,27 @@ module NTable
|
|
114
115
|
end
|
115
116
|
|
116
117
|
|
117
|
-
|
118
|
+
# Iterate over the labels, in order.
|
119
|
+
|
120
|
+
def each
|
121
|
+
if block_given?
|
122
|
+
@axis_object.size.times do |i_|
|
123
|
+
yield @axis_object.label(i_)
|
124
|
+
end
|
125
|
+
else
|
126
|
+
to_enum
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
include ::Enumerable
|
131
|
+
|
132
|
+
|
133
|
+
# Standard equality check
|
134
|
+
|
135
|
+
def eql?(obj_)
|
118
136
|
obj_.is_a?(AxisInfo) && @axis_object.eql?(obj_.axis_object) && @axis_name.eql?(obj_.axis_name)
|
119
137
|
end
|
120
|
-
alias_method :==, :eql?
|
138
|
+
alias_method :==, :eql?
|
121
139
|
|
122
140
|
|
123
141
|
def _set_axis(axis_) # :nodoc:
|
@@ -133,7 +151,7 @@ module NTable
|
|
133
151
|
end
|
134
152
|
|
135
153
|
|
136
|
-
def _compute_offset(v_)
|
154
|
+
def _compute_offset(v_) # :nodoc:
|
137
155
|
if v_.is_a?(::NTable::IndexWrapper)
|
138
156
|
index_ = v_.to_i
|
139
157
|
index_ = nil if index_ < 0 || index_ >= @axis_object.size
|
@@ -160,11 +178,21 @@ module NTable
|
|
160
178
|
end
|
161
179
|
|
162
180
|
|
181
|
+
# Standard equality check
|
182
|
+
|
163
183
|
def eql?(obj_)
|
164
184
|
obj_.is_a?(Position) && obj_.structure.eql?(@structure) && obj_._offset.eql?(self._offset)
|
165
185
|
end
|
166
186
|
alias_method :==, :eql?
|
167
187
|
|
188
|
+
|
189
|
+
# Standard hash value
|
190
|
+
|
191
|
+
def hash
|
192
|
+
@structure.hash + @vector.hash
|
193
|
+
end
|
194
|
+
|
195
|
+
|
168
196
|
attr_reader :structure # :nodoc:
|
169
197
|
|
170
198
|
|
@@ -398,11 +426,21 @@ module NTable
|
|
398
426
|
@names[axis_.to_s]
|
399
427
|
end
|
400
428
|
end
|
429
|
+
alias_method :[], :axis
|
430
|
+
|
431
|
+
|
432
|
+
# Iterate over the axes in order, yielding AxisInfo objects.
|
433
|
+
|
434
|
+
def each(&block_)
|
435
|
+
@indexes.each(&block_)
|
436
|
+
end
|
437
|
+
|
438
|
+
include ::Enumerable
|
401
439
|
|
402
440
|
|
403
441
|
# Lock this structure, preventing further modification. Generally,
|
404
|
-
# this is done automatically when a structure is used by a table
|
405
|
-
#
|
442
|
+
# this is done automatically when a structure is used by a table;
|
443
|
+
# you normally do not need to call it yourself.
|
406
444
|
|
407
445
|
def lock!
|
408
446
|
unless @locked
|
data/lib/ntable/table.rb
CHANGED
@@ -128,6 +128,13 @@ module NTable
|
|
128
128
|
end
|
129
129
|
|
130
130
|
|
131
|
+
# Standard hash value
|
132
|
+
|
133
|
+
def hash
|
134
|
+
@structure.hash + @vals.hash + @offset.hash + @parent.hash
|
135
|
+
end
|
136
|
+
|
137
|
+
|
131
138
|
# The Structure of this table
|
132
139
|
attr_reader :structure
|
133
140
|
|
@@ -643,7 +650,7 @@ module NTable
|
|
643
650
|
end
|
644
651
|
|
645
652
|
|
646
|
-
def _offset_for_args(args_)
|
653
|
+
def _offset_for_args(args_) # :nodoc:
|
647
654
|
if args_.size == 1
|
648
655
|
first_ = args_.first
|
649
656
|
args_ = first_ if first_.is_a?(::Hash) || first_.is_a?(::Array)
|
data/test/tc_nested_object.rb
CHANGED
@@ -47,6 +47,7 @@ module NTable
|
|
47
47
|
def setup
|
48
48
|
@labeled_axis_2 = LabeledAxis.new([:one, :two])
|
49
49
|
@object_axis_2 = ObjectAxis.new([:one, :two])
|
50
|
+
@object_axis_3 = ObjectAxis.new([:one, :two, :three])
|
50
51
|
@labeled_axis_3 = LabeledAxis.new([:blue, :red, :white])
|
51
52
|
@indexed_axis_2 = IndexedAxis.new(2)
|
52
53
|
@indexed_axis_10 = IndexedAxis.new(10, 1)
|
@@ -202,7 +203,7 @@ module NTable
|
|
202
203
|
def test_from_level_1_labeled_with_objectify_conversion
|
203
204
|
obj_ = {'one' => 1, 'two' => 2}
|
204
205
|
t1_ = Table.from_nested_object(obj_,
|
205
|
-
[{:sort => true, :objectify =>
|
206
|
+
[{:sort => true, :objectify => ->(a_){ a_.to_sym }}])
|
206
207
|
assert_equal(Table.new(Structure.add(@object_axis_2), :load => [1,2]), t1_)
|
207
208
|
end
|
208
209
|
|
@@ -210,11 +211,20 @@ module NTable
|
|
210
211
|
def test_from_level_1_labeled_with_stringify_conversion
|
211
212
|
obj_ = {:one1 => 1, :two22 => 2}
|
212
213
|
t1_ = Table.from_nested_object(obj_,
|
213
|
-
[{:sort => true, :stringify =>
|
214
|
+
[{:sort => true, :stringify => ->(a_){ a_.to_s.gsub(/\d/, '') }}])
|
214
215
|
assert_equal(Table.new(Structure.add(@labeled_axis_2), :load => [1,2]), t1_)
|
215
216
|
end
|
216
217
|
|
217
218
|
|
219
|
+
def test_from_level_1_labeled_with_objectify_and_postprocess
|
220
|
+
obj_ = {:one => 1, :two => 2}
|
221
|
+
t1_ = Table.from_nested_object(obj_,
|
222
|
+
[{:sort => true, :objectify => true, :postprocess => ->(labels_){ labels_ << :three }}],
|
223
|
+
:fill => 0)
|
224
|
+
assert_equal(Table.new(Structure.add(@object_axis_3), :load => [1,2,0]), t1_)
|
225
|
+
end
|
226
|
+
|
227
|
+
|
218
228
|
end
|
219
229
|
|
220
230
|
end
|
data/test/tc_structure.rb
CHANGED
@@ -74,6 +74,13 @@ module NTable
|
|
74
74
|
end
|
75
75
|
|
76
76
|
|
77
|
+
def test_axis_info_enumerable
|
78
|
+
s_ = Structure.new
|
79
|
+
s_.add(@indexed1, :first)
|
80
|
+
assert_equal(45, s_.axis(0).inject(:+))
|
81
|
+
end
|
82
|
+
|
83
|
+
|
77
84
|
def test_add_multi_axis
|
78
85
|
s_ = Structure.new
|
79
86
|
s_.add(@labeled1, :first)
|
@@ -92,6 +99,15 @@ module NTable
|
|
92
99
|
end
|
93
100
|
|
94
101
|
|
102
|
+
def test_structure_enumerable
|
103
|
+
s_ = Structure.new
|
104
|
+
s_.add(@labeled1, :first)
|
105
|
+
s_.add(@indexed1, :second)
|
106
|
+
s_.add(@indexed1)
|
107
|
+
assert_equal([LabeledAxis, IndexedAxis, IndexedAxis], s_.map{ |ai_| ai_.axis_object.class })
|
108
|
+
end
|
109
|
+
|
110
|
+
|
95
111
|
def test_remove_axis
|
96
112
|
s_ = Structure.new
|
97
113
|
s_.add(@labeled1, :first)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ntable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-09-
|
12
|
+
date: 2012-09-24 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: NTable provides a convenient data structure for storing n-dimensional
|
15
15
|
tabular data. It works with zero-dimensional scalar values, arrays, tables, and
|
@@ -64,7 +64,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
64
64
|
version: 1.3.1
|
65
65
|
requirements: []
|
66
66
|
rubyforge_project: virtuoso
|
67
|
-
rubygems_version: 1.8.
|
67
|
+
rubygems_version: 1.8.24
|
68
68
|
signing_key:
|
69
69
|
specification_version: 3
|
70
70
|
summary: NTable is an n-dimensional table data structure for Ruby.
|