carray 3.0.3 → 3.0.4

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.
Files changed (212) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +264 -0
  3. data/README.md +3 -2
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_descriptor.h +27 -24
  6. data/ext/ca_axis_dispatch.c +1 -6
  7. data/ext/ca_axis_group.c +27 -27
  8. data/ext/ca_binop_dispatch.c +1 -29
  9. data/ext/ca_binop_dispatch.h +0 -1
  10. data/ext/ca_categorical_iterator.c +205 -134
  11. data/ext/ca_compare.c +7 -18
  12. data/ext/ca_group_iter.c +16 -17
  13. data/ext/ca_kernel_iterator.c +1234 -1994
  14. data/ext/ca_kernel_iterator.h +391 -437
  15. data/ext/ca_monop_dispatch.c +82 -59
  16. data/ext/ca_monop_dispatch.h +1 -0
  17. data/ext/ca_obj_array.c +96 -39
  18. data/ext/ca_obj_bincmp.c +24 -21
  19. data/ext/ca_obj_binop.c +15 -26
  20. data/ext/ca_obj_bitarray.c +3 -2
  21. data/ext/ca_obj_bitfield.c +5 -6
  22. data/ext/ca_obj_block.c +18 -9
  23. data/ext/ca_obj_byte_swap.c +5 -6
  24. data/ext/ca_obj_const_string.c +173 -19
  25. data/ext/ca_obj_face.c +238 -57
  26. data/ext/ca_obj_face.h +19 -0
  27. data/ext/ca_obj_fake.c +15 -29
  28. data/ext/ca_obj_farray.c +6 -3
  29. data/ext/ca_obj_field.c +17 -5
  30. data/ext/ca_obj_fixlen_string.c +2 -3
  31. data/ext/ca_obj_grid.c +406 -132
  32. data/ext/ca_obj_meld.c +172 -34
  33. data/ext/ca_obj_moncmp.c +23 -8
  34. data/ext/ca_obj_monop.c +67 -57
  35. data/ext/ca_obj_object.c +157 -51
  36. data/ext/ca_obj_record.c +12 -7
  37. data/ext/ca_obj_reduce.c +13 -8
  38. data/ext/ca_obj_refer.c +25 -27
  39. data/ext/ca_obj_remap.c +30 -9
  40. data/ext/ca_obj_repeat.c +9 -10
  41. data/ext/ca_obj_roll.c +6 -5
  42. data/ext/ca_obj_select.c +14 -9
  43. data/ext/ca_obj_select_axis.c +15 -47
  44. data/ext/ca_obj_shift.c +11 -1
  45. data/ext/ca_obj_stack.c +179 -34
  46. data/ext/ca_obj_stride.c +162 -115
  47. data/ext/ca_obj_string.c +71 -4
  48. data/ext/ca_obj_tile.c +6 -5
  49. data/ext/ca_obj_time.c +22 -6
  50. data/ext/ca_obj_timedelta.c +22 -6
  51. data/ext/ca_obj_transpose.c +5 -3
  52. data/ext/ca_obj_triop.c +23 -32
  53. data/ext/ca_obj_window.c +190 -26
  54. data/ext/ca_sweep_engine.c +2 -2
  55. data/ext/caframe_csv_reader.c +80 -1
  56. data/ext/carray.h +181 -75
  57. data/ext/carray_access.c +184 -96
  58. data/ext/carray_address_basis.c +343 -87
  59. data/ext/carray_attribute.c +89 -6
  60. data/ext/carray_bincount.c +10 -8
  61. data/ext/carray_broadcast.c +13 -8
  62. data/ext/carray_build_flags.h +3 -0
  63. data/ext/carray_call_cfunc.c +108 -0
  64. data/ext/carray_cast.c +142 -24
  65. data/ext/carray_cast_func.rb +46 -6
  66. data/ext/carray_class.c +4 -3
  67. data/ext/carray_config.h +28 -0
  68. data/ext/carray_conversion.c +16 -7
  69. data/ext/carray_copy.c +51 -14
  70. data/ext/carray_core.c +147 -55
  71. data/ext/carray_count.c +50 -20
  72. data/ext/carray_element.c +76 -33
  73. data/ext/carray_factorize.c +118 -64
  74. data/ext/carray_generate.c +191 -50
  75. data/ext/carray_histogram.c +62 -17
  76. data/ext/carray_hold.c +12 -15
  77. data/ext/carray_index_classifier.c +388 -176
  78. data/ext/carray_internal.h +336 -29
  79. data/ext/carray_kernels_bincmp.c +4453 -0
  80. data/ext/carray_kernels_binop.c +11368 -0
  81. data/ext/carray_kernels_init.c +1734 -0
  82. data/ext/carray_kernels_moncmp.c +2104 -0
  83. data/ext/carray_kernels_monop.c +18369 -0
  84. data/ext/carray_kernels_reduce_aggregate.c +23818 -0
  85. data/ext/carray_kernels_reduce_boolean.c +406 -0
  86. data/ext/carray_kernels_reduce_cumulative.c +10086 -0
  87. data/ext/carray_kernels_reduce_extreme.c +17634 -0
  88. data/ext/carray_kernels_reduce_variance.c +3849 -0
  89. data/ext/carray_kernels_scan.c +4302 -0
  90. data/ext/carray_kernels_search.c +33829 -0
  91. data/ext/carray_kernels_sort.c +10906 -0
  92. data/ext/carray_kernels_triop.c +1864 -0
  93. data/ext/carray_key_windows.c +216 -0
  94. data/ext/carray_lazy.c +170 -111
  95. data/ext/carray_mask.c +292 -72
  96. data/ext/carray_median_percentile.c +82 -28
  97. data/ext/carray_memory_view.c +669 -561
  98. data/ext/carray_operator.c +124 -47
  99. data/ext/carray_order.c +384 -76
  100. data/ext/carray_partition.c +28 -27
  101. data/ext/carray_random.c +67 -13
  102. data/ext/carray_round.c +273 -0
  103. data/ext/carray_scatter.c +63 -38
  104. data/ext/carray_segment.c +3 -3
  105. data/ext/carray_slab.c +91 -28
  106. data/ext/carray_sort.c +35 -35
  107. data/ext/carray_struct.c +19 -18
  108. data/ext/carray_test.c +26 -5
  109. data/ext/carray_undef.c +4 -0
  110. data/ext/carray_utils.c +246 -45
  111. data/ext/mk_call_cfunc.rb +19 -2
  112. data/ext/mkkernel.rb +198 -240
  113. data/ext/ruby_carray.c +10 -4
  114. data/ext/version.h +4 -4
  115. data/lib/carray/attribute.rb +126 -22
  116. data/lib/carray/autoload_carray.rb +8 -31
  117. data/lib/carray/autoload_method_extension.rb +14 -5
  118. data/lib/carray/axis_group.rb +255 -106
  119. data/lib/carray/basics.rb +78 -10
  120. data/lib/carray/bincount_nd.rb +12 -2
  121. data/lib/carray/block_iterator.rb +126 -23
  122. data/lib/carray/boolean_reduce.rb +17 -17
  123. data/lib/carray/categorical.rb +165 -43
  124. data/lib/carray/categorical_iterator.rb +130 -27
  125. data/lib/carray/complex.rb +5 -5
  126. data/lib/carray/conditional.rb +67 -18
  127. data/lib/carray/const_string.rb +14 -5
  128. data/lib/carray/construct.rb +15 -11
  129. data/lib/carray/data_type_extension.rb +1 -0
  130. data/lib/carray/distance_limit.rb +77 -0
  131. data/lib/carray/fixlen_string.rb +7 -1
  132. data/lib/carray/frame/column_verbs.rb +70 -0
  133. data/lib/carray/frame/convert.rb +69 -0
  134. data/lib/carray/frame/csv_parser.rb +111 -17
  135. data/lib/carray/frame/describe.rb +3 -2
  136. data/lib/carray/frame/frame.rb +246 -21
  137. data/lib/carray/frame/group.rb +510 -95
  138. data/lib/carray/frame/io.rb +115 -28
  139. data/lib/carray/frame/join.rb +312 -51
  140. data/lib/carray/frame/records.rb +83 -7
  141. data/lib/carray/frame/reshape.rb +42 -35
  142. data/lib/carray/frame/sort.rb +7 -19
  143. data/lib/carray/frame/verbs.rb +188 -48
  144. data/lib/carray/frame/windows.rb +117 -0
  145. data/lib/carray/frame.rb +2 -0
  146. data/lib/carray/fuse_source.rb +11 -4
  147. data/lib/carray/fusion.rb +40 -16
  148. data/lib/carray/histogram.rb +6 -1
  149. data/lib/carray/inspect.rb +31 -8
  150. data/lib/carray/iterator.rb +85 -7
  151. data/lib/carray/key_windows.rb +620 -0
  152. data/lib/carray/lazy.rb +182 -183
  153. data/lib/carray/mask_gap_fill.rb +63 -57
  154. data/lib/carray/meld_reduce.rb +12 -14
  155. data/lib/carray/methods/bin.rb +26 -18
  156. data/lib/carray/methods/bincount.rb +16 -0
  157. data/lib/carray/methods/bit_string.rb +1 -1
  158. data/lib/carray/methods/choose.rb +16 -3
  159. data/lib/carray/methods/composition.rb +3 -1
  160. data/lib/carray/methods/gather_nd.rb +14 -5
  161. data/lib/carray/methods/insert_block.rb +2 -2
  162. data/lib/carray/methods/is_between.rb +59 -0
  163. data/lib/carray/methods/is_in.rb +53 -36
  164. data/lib/carray/methods/join.rb +1 -6
  165. data/lib/carray/methods/lag.rb +49 -0
  166. data/lib/carray/methods/locate_addr.rb +47 -51
  167. data/lib/carray/methods/mode.rb +1 -1
  168. data/lib/carray/methods/nunique.rb +11 -6
  169. data/lib/carray/methods/resize.rb +3 -3
  170. data/lib/carray/methods/value_counts.rb +4 -11
  171. data/lib/carray/record_order.rb +3 -3
  172. data/lib/carray/rng.rb +3 -0
  173. data/lib/carray/runtime.rb +53 -8
  174. data/lib/carray/segment_iterator.rb +313 -82
  175. data/lib/carray/serialize.rb +24 -10
  176. data/lib/carray/slab_iterator.rb +50 -0
  177. data/lib/carray/string.rb +105 -5
  178. data/lib/carray/string_operation_extension.rb +8 -1
  179. data/lib/carray/struct.rb +9 -21
  180. data/lib/carray/struct_builder.rb +19 -8
  181. data/lib/carray/time.rb +726 -104
  182. data/lib/carray/window_iterator.rb +122 -16
  183. data/lib/carray.rb +7 -0
  184. data/yard-stubs/ca_obj_bitfield.rb +1 -1
  185. data/yard-stubs/ca_obj_field.rb +4 -0
  186. data/yard-stubs/ca_obj_grid.rb +21 -10
  187. data/yard-stubs/ca_obj_refer.rb +2 -1
  188. data/yard-stubs/ca_obj_roll.rb +2 -2
  189. data/yard-stubs/ca_obj_shift.rb +5 -1
  190. data/yard-stubs/ca_obj_tile.rb +1 -1
  191. data/yard-stubs/ca_obj_window.rb +8 -3
  192. data/yard-stubs/carray_access.rb +6 -1
  193. data/yard-stubs/carray_attribute.rb +0 -5
  194. data/yard-stubs/carray_cast.rb +10 -0
  195. data/yard-stubs/carray_conversion.rb +2 -1
  196. data/yard-stubs/carray_copy.rb +4 -2
  197. data/yard-stubs/carray_count.rb +4 -4
  198. data/yard-stubs/carray_element.rb +10 -4
  199. data/yard-stubs/carray_generate.rb +21 -1
  200. data/yard-stubs/carray_mask.rb +26 -2
  201. data/yard-stubs/carray_math.rb +27 -1
  202. data/yard-stubs/carray_median_percentile.rb +5 -2
  203. data/yard-stubs/carray_memory_view.rb +40 -23
  204. data/yard-stubs/carray_order.rb +29 -18
  205. data/yard-stubs/carray_random.rb +13 -4
  206. data/yard-stubs/carray_scatter.rb +14 -9
  207. data/yard-stubs/carray_slab.rb +9 -2
  208. data/yard-stubs/carray_sort.rb +6 -5
  209. data/yard-stubs/carray_test.rb +5 -3
  210. metadata +25 -3
  211. data/lib/carray/arrow_tensor.rb +0 -401
  212. data/lib/carray/table.rb +0 -108
@@ -62,23 +62,25 @@ class CAFrame
62
62
  # unknown reduction or a multi-dimensional value column with it, or when
63
63
  # two output columns would share a name.
64
64
  def pivot(index:, columns:, values:, aggregate: nil)
65
- plan = pivot_plan(index.to_s, columns.to_s, aggregate, "pivot")
65
+ plan = pivot_plan(column_key(index, "pivot"), column_key(columns, "pivot"), aggregate, "pivot")
66
66
  labels = Array.new(plan[:nc]) { |j| plan[:clab][j].to_s }
67
- value_names = values.is_a?(Array) ? values.map(&:to_s) : nil
67
+ if labels.include?("") && !values.is_a?(Array)
68
+ raise ArgumentError,
69
+ "pivot: a value of columns: #{columns.inspect} is the empty string, " \
70
+ "which cannot name a column; recode it first"
71
+ end
72
+ value_names = values.is_a?(Array) ? column_keys(values, "pivot") : nil
68
73
  names =
69
74
  if value_names
70
75
  value_names.flat_map { |v| labels.map { |l| "#{v}_#{l}" } }
71
76
  else
72
77
  labels
73
78
  end
74
- dup = names.tally.select { |_, count| count > 1 }.keys
75
- unless dup.empty?
76
- raise ArgumentError, "pivot: output columns would share the names #{dup.inspect}"
77
- end
79
+ CAFrame.__send__(:refuse_repeated_names, names, "pivot", "the output columns would name")
78
80
 
79
81
  source = plan[:source].reshape(plan[:nr], plan[:nc])
80
82
  cols = {}
81
- (value_names || [values.to_s]).each_with_index do |value, k|
83
+ (value_names || [column_key(values, "pivot")]).each_with_index do |value, k|
82
84
  val = pivot_value(plan, value)
83
85
  plan[:nc].times do |j|
84
86
  cols[names[k * plan[:nc] + j]] = project_rows(val, source[nil, j])
@@ -108,8 +110,8 @@ class CAFrame
108
110
  # and the axis-1 key values; the grid is new, not a view of +self+.
109
111
  # @raise [ArgumentError] as {#pivot}.
110
112
  def pivot_grid(index:, columns:, values:, aggregate: nil)
111
- plan = pivot_plan(index.to_s, columns.to_s, aggregate, "pivot_grid")
112
- val = pivot_value(plan, values.to_s)
113
+ plan = pivot_plan(column_key(index, "pivot_grid"), column_key(columns, "pivot_grid"), aggregate, "pivot_grid")
114
+ val = pivot_value(plan, column_key(values, "pivot_grid"))
113
115
  grid = project_rows(val, plan[:source]).reshape(plan[:nr], plan[:nc], *val.shape[1..])
114
116
  [grid, plan[:rlab], plan[:clab]]
115
117
  end
@@ -118,12 +120,14 @@ class CAFrame
118
120
  # in order (nil beside numbers, numbers beside strings) are refused,
119
121
  # naming the key.
120
122
  private def pivot_sorted_keys(key, kw, name, verb)
121
- key.unique(sort: true)
123
+ # The keys label rows or columns, so they show the key column's
124
+ # attributes, as a group index does.
125
+ CAFrame.__send__(:key_labels, key, key.unique(sort: true))
122
126
  rescue ArgumentError => e
123
- raise unless e.message.start_with?("sort:")
127
+ why = e.message[/\A[^:]+: (comparison of .+ failed)\z/, 1] or raise
124
128
  raise ArgumentError,
125
129
  "#{verb}: #{kw}: #{name.inspect} holds values that cannot be put in order " \
126
- "(#{e.message.sub(/\Asort: /, "")}); its keys are sorted to make the " \
130
+ "(#{why}); its keys are sorted to make the " \
127
131
  "#{kw == "index" ? "rows" : "columns"}"
128
132
  end
129
133
 
@@ -131,13 +135,9 @@ class CAFrame
131
135
  # sorted keys and, for each of the nr * nc cells, the address of the row
132
136
  # (or with +aggregate:+, of the reduced pair) that fills it.
133
137
  private def pivot_plan(index, columns, aggregate, verb)
134
- rkey = column_or_index(index)
135
- ckey = column_or_index(columns)
136
- { "index" => [index, rkey], "columns" => [columns, ckey] }.each do |kw, (name, key)|
137
- next if key.ndim == 1
138
- raise ArgumentError,
139
- "#{verb}: #{kw}: #{name.inspect} has shape #{key.shape.inspect}, not one value per row"
140
- end
138
+ CAFrame.__send__(:refuse_repeated_names, [index, columns], verb, "index: and columns: name")
139
+ rkey = key_column(index, verb)
140
+ ckey = key_column(columns, verb)
141
141
  rlab = pivot_sorted_keys(rkey, "index", index, verb)
142
142
  clab = pivot_sorted_keys(ckey, "columns", columns, verb)
143
143
  nr = rlab.elements
@@ -145,6 +145,8 @@ class CAFrame
145
145
 
146
146
  r = rkey.locate_addr(rlab)
147
147
  c = ckey.locate_addr(clab)
148
+ rlab = pivot_key_kind(rkey, rlab)
149
+ clab = pivot_key_kind(ckey, clab)
148
150
  placed = r.is_not_masked & c.is_not_masked
149
151
  cell = r[placed] * nc + c[placed]
150
152
  source = CArray.int64(nr * nc)
@@ -160,6 +162,7 @@ class CAFrame
160
162
  source[CA_INT64(pairs.labels)] = CArray.int64(pairs.labels.size).seq
161
163
  else
162
164
  if cell.nunique != cell.elements
165
+ # names-check: two rows for one cell of the grid
163
166
  first = cell.to_a.tally.find { |_, count| count > 1 }.first
164
167
  raise ArgumentError,
165
168
  "pivot: more than one row for #{index}=#{rlab[first / nc].inspect}, " \
@@ -170,6 +173,15 @@ class CAFrame
170
173
  plan
171
174
  end
172
175
 
176
+ # The distinct values of a categorical key come back as its label values;
177
+ # the keys that label the result are a categorical again, with the key's
178
+ # labels, as a group index keeps a categorical key.
179
+ private def pivot_key_kind(key, sorted)
180
+ return sorted unless key.is_a?(CACategorical)
181
+ CAFrame.__send__(:key_labels, key,
182
+ sorted.to_type(:object).categorize(labels: key.labels))
183
+ end
184
+
173
185
  # The column the plan's addresses point into: the value column itself, or
174
186
  # with +aggregate:+ one reduced value per occupied pair.
175
187
  private def pivot_value(plan, name)
@@ -189,8 +201,8 @@ class CAFrame
189
201
 
190
202
  # Gather a wide frame into a long one. The +value_columns+ are stacked one
191
203
  # after another into a single +value_name+ column, a +var_name+ column says
192
- # which of them each row came from, and every +id+ column repeats once per
193
- # value column.
204
+ # which of them each row came from (a text column), and every +id+ column
205
+ # repeats once per value column.
194
206
  #
195
207
  # wide.melt(id: "time") # time / variable / value
196
208
  #
@@ -212,9 +224,9 @@ class CAFrame
212
224
  # @raise [ArgumentError] when there is nothing to melt, or the value
213
225
  # columns differ in data type.
214
226
  def melt(id: [], value_columns: nil, var_name: "variable", value_name: "value")
215
- ids = Array(id).map(&:to_s)
227
+ ids = column_keys(Array(id), "melt")
216
228
  ids.each { |name| self[name] }
217
- vars = (value_columns || column_names - ids).map(&:to_s)
229
+ vars = column_keys(value_columns || column_names - ids, "melt")
218
230
  raise ArgumentError, "melt: no columns to melt" if vars.empty?
219
231
  overlap = vars & ids
220
232
  unless overlap.empty?
@@ -240,12 +252,13 @@ class CAFrame
240
252
  out[@axis_name] = CArray.meld(Array.new(k, @index)) if @index
241
253
  ids.each { |name| out[name] = CArray.meld(Array.new(k, self[name])) }
242
254
  [var_name, value_name].each do |name|
255
+ column_name_arg(name, "melt")
243
256
  if out.key?(name)
244
257
  raise ArgumentError, "melt: #{name.inspect} is already an id column"
245
258
  end
246
259
  end
247
260
  n = nrow
248
- out[var_name] = CArray.object(n * k) { |i| vars[i / n] }
261
+ out[var_name] = CArray.string(Array.new(n * k) { |i| vars[i / n] })
249
262
  out[value_name] = CArray.meld(pieces)
250
263
  CAFrame.new(out)
251
264
  end
@@ -291,8 +304,9 @@ class CAFrame
291
304
  # @param on [String, nil] the column whose values line the rows up.
292
305
  # @return [CAFrame] one row per group, indexed by the group labels.
293
306
  def stack_rows(by:, on: nil)
294
- keys = (by.is_a?(Array) ? by : [by]).map { |k| k.is_a?(Symbol) ? k.to_s : k }
307
+ keys = (by.is_a?(Array) ? by : [by]).map { |k| k.is_a?(Symbol) ? column_key(k, "stack_rows") : k }
295
308
  raise ArgumentError, "stack_rows: by: names no key" if keys.empty?
309
+ CAFrame.__send__(:refuse_repeated_names, keys.grep(String), "stack_rows", "by: names")
296
310
  frame = @index ? CAFrame.new({ @axis_name => @index }.merge(@columns)) : self
297
311
  keys.each { |k| frame.__send__(:key_column, k, "stack_rows") }
298
312
  # With nothing but the key there is no column to stack, and the result
@@ -322,6 +336,7 @@ class CAFrame
322
336
  "stack_rows: every row has to belong to a group; " \
323
337
  "the key has no value for #{frame.nrow - stacked} of #{frame.nrow} rows"
324
338
  end
339
+ # names-check: equal sizes, not names
325
340
  unless sizes.uniq.size <= 1
326
341
  # One group of each size, so the message says where to look.
327
342
  examples = sizes.each_with_index.uniq { |n, _| n }.map do |n, g|
@@ -354,10 +369,7 @@ class CAFrame
354
369
  if keys.include?(on)
355
370
  raise ArgumentError, "stack_rows: #{on.inspect} is a key; on: names another column"
356
371
  end
357
- place = frame[on]
358
- unless place.ndim == 1
359
- raise ArgumentError, "stack_rows: on: #{on.inspect} has shape #{place.shape.inspect}, not one value per row"
360
- end
372
+ place = frame.__send__(:key_column, on, "stack_rows")
361
373
  grouped = frame.group_by(*keys)
362
374
  bounds = grouped.__send__(:group_bounds).to_a
363
375
  groups = bounds.size - 1
@@ -423,6 +435,7 @@ class CAFrame
423
435
  raise ArgumentError, "unstack_rows: the frame has no N-D column to unstack"
424
436
  end
425
437
  lengths = stacked.transform_values { |c| c.shape[1] }
438
+ # names-check: equal lengths, not names
426
439
  unless lengths.values.uniq.size == 1
427
440
  raise ArgumentError,
428
441
  "unstack_rows: the N-D columns differ in length along their first axis #{lengths}"
@@ -439,10 +452,4 @@ class CAFrame
439
452
  CAFrame.new(cols, axis_name: @axis_name, index: @index && @index[repeat])
440
453
  end
441
454
 
442
- # A column, or the index when +name+ is the row axis name (pivot and resample
443
- # keys may be either).
444
- private def column_or_index(name)
445
- return @index if @index && !@columns.key?(name) && @axis_name == name
446
- self[name]
447
- end
448
455
  end
@@ -40,8 +40,9 @@ class CAFrame
40
40
  validate_sort_order(order)
41
41
  named = specs.map do |spec|
42
42
  name, dir = parse_sort_key_spec(spec, order)
43
- [name.to_s, sort_key_column(name), dir]
43
+ [column_key(name, "sort_by_key"), key_column(column_key(name, "sort_by_key"), "sort_by_key"), dir]
44
44
  end
45
+ CAFrame.__send__(:refuse_repeated_names, named.map(&:first), "sort_by_key")
45
46
  keys = named.map do |name, col, dir|
46
47
  dir == :desc ? col.order(descending: true, method: :dense) : col
47
48
  end
@@ -55,8 +56,11 @@ class CAFrame
55
56
  # The core names the conversion as an array method; in a frame it is
56
57
  # written with sort_by.
57
58
  reason = "#{reason.split(";").first}; sort by its values with " \
58
- "sort_by { |f| f[#{name.inspect}].to_type(:object) }, or by its " \
59
- "storage with sort_by { |f| f[#{name.inspect}].parent }"
59
+ "sort_by { |f| f[#{name.inspect}].to_type(:object) }"
60
+ if (storage = col.__send__(:face_storage_method))
61
+ reason += ", or by its storage with " \
62
+ "sort_by { |f| f[#{name.inspect}].#{storage} }"
63
+ end
60
64
  end
61
65
  raise e.class, "sort_by_key: the column #{name.inspect} cannot be sorted: #{reason}"
62
66
  end
@@ -118,20 +122,4 @@ class CAFrame
118
122
  raise ArgumentError, "sort order must be :asc or :desc, got #{dir.inspect}"
119
123
  end
120
124
 
121
- private def sort_key_column(name)
122
- key = name.to_s
123
- col = if @columns.key?(key)
124
- @columns[key]
125
- elsif @index && @axis_name == key
126
- @index
127
- else
128
- raise KeyError, "no column #{key.inspect}"
129
- end
130
- unless col.ndim == 1
131
- raise ArgumentError,
132
- "sort_by_key: key column #{key.inspect} is #{col.ndim}-D; " \
133
- "sort by a scalar (1-D) column"
134
- end
135
- col
136
- end
137
125
  end