ntable 0.1.3 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -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.3
1
+ 0.1.4
@@ -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
- def ==(obj_)
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
@@ -41,9 +41,9 @@ require 'json'
41
41
  module NTable
42
42
 
43
43
 
44
- @numeric_sort = ::Proc.new{ |a_, b_| a_.to_f <=> b_.to_f }
45
- @integer_sort = ::Proc.new{ |a_, b_| a_.to_i <=> b_.to_i }
46
- @string_sort = ::Proc.new{ |a_, b_| a_.to_s <=> b_.to_s }
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)
@@ -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
 
@@ -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
- def eql?(obj_) # :nodoc:
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? # :nodoc:
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
- # and you do not need to call it yourself.
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
@@ -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)
@@ -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 => ::Proc.new{ |a_| a_.to_sym }}])
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 => ::Proc.new{ |a_| a_.to_s.gsub(/\d/, '') }}])
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
@@ -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.3
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-10 00:00:00.000000000 Z
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.21
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.