carray 3.0.1 → 3.0.3

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 (229) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +824 -0
  3. data/README.md +5 -6
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_dispatch.c +258 -4
  6. data/ext/ca_axis_group.c +368 -184
  7. data/ext/ca_binop_dispatch.c +54 -32
  8. data/ext/ca_binop_dispatch.h +1 -0
  9. data/ext/ca_categorical_iterator.c +90 -41
  10. data/ext/ca_compare.c +8 -2
  11. data/ext/ca_for_buffer.h +21 -68
  12. data/ext/ca_group_iter.c +27 -11
  13. data/ext/ca_kernel_iterator.c +960 -245
  14. data/ext/ca_kernel_iterator.h +352 -181
  15. data/ext/ca_monop_dispatch.c +4 -4
  16. data/ext/ca_monop_dispatch.h +4 -0
  17. data/ext/ca_obj_array.c +194 -42
  18. data/ext/ca_obj_bincmp.c +152 -121
  19. data/ext/ca_obj_binop.c +107 -210
  20. data/ext/ca_obj_bitarray.c +37 -24
  21. data/ext/ca_obj_bitfield.c +95 -62
  22. data/ext/ca_obj_block.c +5 -4
  23. data/ext/ca_obj_byte_swap.c +40 -40
  24. data/ext/ca_obj_const_string.c +180 -50
  25. data/ext/ca_obj_face.c +428 -62
  26. data/ext/ca_obj_face.h +107 -47
  27. data/ext/ca_obj_fake.c +21 -50
  28. data/ext/ca_obj_farray.c +1 -0
  29. data/ext/ca_obj_field.c +16 -3
  30. data/ext/ca_obj_fixlen_string.c +39 -26
  31. data/ext/ca_obj_grid.c +181 -124
  32. data/ext/ca_obj_meld.c +235 -55
  33. data/ext/ca_obj_moncmp.c +61 -61
  34. data/ext/ca_obj_monop.c +125 -107
  35. data/ext/ca_obj_object.c +551 -233
  36. data/ext/ca_obj_record.c +51 -16
  37. data/ext/ca_obj_reduce.c +5 -8
  38. data/ext/ca_obj_refer.c +117 -33
  39. data/ext/ca_obj_remap.c +57 -17
  40. data/ext/ca_obj_repeat.c +21 -5
  41. data/ext/ca_obj_roll.c +115 -11
  42. data/ext/ca_obj_select.c +88 -75
  43. data/ext/ca_obj_select_axis.c +58 -60
  44. data/ext/ca_obj_shift.c +22 -102
  45. data/ext/ca_obj_stack.c +223 -123
  46. data/ext/ca_obj_stride.c +267 -35
  47. data/ext/ca_obj_string.c +25 -22
  48. data/ext/ca_obj_tile.c +32 -16
  49. data/ext/ca_obj_time.c +23 -25
  50. data/ext/ca_obj_timedelta.c +21 -23
  51. data/ext/ca_obj_transpose.c +12 -3
  52. data/ext/ca_obj_triop.c +110 -117
  53. data/ext/ca_obj_window.c +157 -29
  54. data/ext/ca_op_ipower.c +25 -13
  55. data/ext/ca_op_powi.h +57 -30
  56. data/ext/ca_pow5_table.h +656 -0
  57. data/ext/ca_rng_normal.h +42 -0
  58. data/ext/ca_rng_xoshiro256pp.h +105 -0
  59. data/ext/ca_sort_kernels.h +70 -0
  60. data/ext/ca_sweep_engine.c +385 -198
  61. data/ext/ca_sweep_engine.h +31 -23
  62. data/ext/ca_triop_dispatch.c +4 -1
  63. data/ext/ca_triop_dispatch.h +5 -2
  64. data/ext/caframe_csv_reader.c +463 -0
  65. data/ext/carray.h +90 -28
  66. data/ext/carray_access.c +487 -231
  67. data/ext/carray_address_basis.c +731 -0
  68. data/ext/carray_attribute.c +45 -3
  69. data/ext/carray_bincount.c +32 -2
  70. data/ext/carray_broadcast.c +49 -17
  71. data/ext/carray_call_cfunc.c +667 -511
  72. data/ext/carray_cast.c +1556 -247
  73. data/ext/carray_cast_func.rb +34 -21
  74. data/ext/carray_conversion.c +197 -73
  75. data/ext/carray_copy.c +84 -38
  76. data/ext/carray_core.c +591 -119
  77. data/ext/carray_count.c +148 -96
  78. data/ext/carray_element.c +18 -27
  79. data/ext/carray_factorize.c +604 -291
  80. data/ext/carray_generate.c +76 -40
  81. data/ext/carray_histogram.c +152 -94
  82. data/ext/carray_hold.c +2 -1
  83. data/ext/carray_index_classifier.c +63 -96
  84. data/ext/carray_internal.h +400 -1
  85. data/ext/carray_lazy.c +834 -79
  86. data/ext/carray_loop.c +88 -66
  87. data/ext/carray_mask.c +705 -324
  88. data/ext/carray_math_kernel.h +1 -0
  89. data/ext/carray_median_percentile.c +263 -63
  90. data/ext/carray_memory_view.c +254 -248
  91. data/ext/carray_operator.c +1093 -363
  92. data/ext/carray_order.c +133 -69
  93. data/ext/carray_partition.c +102 -56
  94. data/ext/carray_random.c +582 -204
  95. data/ext/carray_scatter.c +308 -65
  96. data/ext/carray_segment.c +257 -0
  97. data/ext/carray_slab.c +183 -70
  98. data/ext/carray_sort.c +235 -240
  99. data/ext/carray_sort_kernel.c +8 -1
  100. data/ext/carray_struct.c +99 -21
  101. data/ext/carray_test.c +167 -64
  102. data/ext/carray_time_text.c +1182 -0
  103. data/ext/carray_utils.c +363 -98
  104. data/ext/extconf.rb +27 -2
  105. data/ext/mk_call_cfunc.rb +103 -118
  106. data/ext/mk_pow5_table.rb +35 -0
  107. data/ext/mkkernel.rb +1349 -851
  108. data/ext/ruby_carray.c +17 -2
  109. data/ext/version.h +4 -4
  110. data/ext/version.rb +4 -2
  111. data/lib/carray/attribute.rb +39 -0
  112. data/lib/carray/autoload_carray.rb +24 -3
  113. data/lib/carray/autoload_method_extension.rb +18 -0
  114. data/lib/carray/axis_group.rb +245 -20
  115. data/lib/carray/basics.rb +95 -25
  116. data/lib/carray/bincount_nd.rb +6 -0
  117. data/lib/carray/block_iterator.rb +200 -54
  118. data/lib/carray/boolean_reduce.rb +21 -29
  119. data/lib/carray/categorical.rb +244 -59
  120. data/lib/carray/categorical_iterator.rb +391 -757
  121. data/lib/carray/complex.rb +36 -29
  122. data/lib/carray/conditional.rb +94 -7
  123. data/lib/carray/const_string.rb +177 -27
  124. data/lib/carray/construct.rb +40 -0
  125. data/lib/carray/data_type_extension.rb +3 -0
  126. data/lib/carray/data_type_limits.rb +91 -0
  127. data/lib/carray/fixlen_string.rb +1 -1
  128. data/lib/carray/frame/concat.rb +157 -6
  129. data/lib/carray/frame/convert.rb +13 -7
  130. data/lib/carray/frame/csv_parser.rb +590 -76
  131. data/lib/carray/frame/describe.rb +79 -0
  132. data/lib/carray/frame/frame.rb +205 -35
  133. data/lib/carray/frame/group.rb +199 -40
  134. data/lib/carray/frame/io.rb +452 -60
  135. data/lib/carray/frame/join.rb +134 -46
  136. data/lib/carray/frame/records.rb +54 -19
  137. data/lib/carray/frame/reshape.rb +448 -0
  138. data/lib/carray/frame/sort.rb +26 -2
  139. data/lib/carray/frame/verbs.rb +726 -56
  140. data/lib/carray/frame.rb +2 -0
  141. data/lib/carray/fuse_source.rb +111 -2
  142. data/lib/carray/fusion.rb +229 -38
  143. data/lib/carray/inspect.rb +71 -20
  144. data/lib/carray/iterator.rb +157 -2
  145. data/lib/carray/lazy.rb +54 -67
  146. data/lib/carray/mask_gap_fill.rb +66 -50
  147. data/lib/carray/math.rb +32 -19
  148. data/lib/carray/meld_reduce.rb +40 -7
  149. data/lib/carray/methods/align_addr.rb +8 -4
  150. data/lib/carray/methods/bincount.rb +2 -2
  151. data/lib/carray/methods/bit_string.rb +14 -3
  152. data/lib/carray/methods/composition.rb +16 -10
  153. data/lib/carray/methods/discovery_along.rb +74 -0
  154. data/lib/carray/methods/factorize.rb +50 -0
  155. data/lib/carray/methods/gather_nd.rb +13 -2
  156. data/lib/carray/methods/insert_block.rb +9 -14
  157. data/lib/carray/methods/is_in.rb +130 -6
  158. data/lib/carray/methods/join.rb +1 -1
  159. data/lib/carray/methods/locate_addr.rb +235 -21
  160. data/lib/carray/methods/mask_duplicates.rb +35 -1
  161. data/lib/carray/methods/mode.rb +18 -1
  162. data/lib/carray/methods/nunique.rb +22 -1
  163. data/lib/carray/methods/repeat.rb +91 -0
  164. data/lib/carray/methods/resize.rb +21 -23
  165. data/lib/carray/methods/snap.rb +34 -12
  166. data/lib/carray/methods/unique.rb +52 -3
  167. data/lib/carray/record_order.rb +208 -0
  168. data/lib/carray/rng.rb +86 -0
  169. data/lib/carray/runtime.rb +14 -11
  170. data/lib/carray/segment_iterator.rb +990 -0
  171. data/lib/carray/serialize.rb +197 -27
  172. data/lib/carray/slab_iterator.rb +109 -40
  173. data/lib/carray/stack.rb +10 -4
  174. data/lib/carray/string_operation_extension.rb +9 -4
  175. data/lib/carray/struct.rb +142 -51
  176. data/lib/carray/struct_builder.rb +110 -14
  177. data/lib/carray/table.rb +2 -2
  178. data/lib/carray/time.rb +535 -105
  179. data/lib/carray/window_iterator.rb +259 -82
  180. data/lib/carray.rb +4 -2
  181. data/yard-stubs/ca_obj_array.rb +4 -4
  182. data/yard-stubs/ca_obj_bitarray.rb +7 -0
  183. data/yard-stubs/ca_obj_bitfield.rb +23 -12
  184. data/yard-stubs/ca_obj_block.rb +2 -7
  185. data/yard-stubs/ca_obj_fake.rb +1 -1
  186. data/yard-stubs/ca_obj_field.rb +1 -1
  187. data/yard-stubs/ca_obj_refer.rb +3 -3
  188. data/yard-stubs/ca_obj_roll.rb +0 -1
  189. data/yard-stubs/ca_obj_stride.rb +10 -7
  190. data/yard-stubs/ca_obj_transpose.rb +1 -1
  191. data/yard-stubs/ca_obj_window.rb +11 -4
  192. data/yard-stubs/carray_access.rb +6 -5
  193. data/yard-stubs/carray_broadcast.rb +2 -2
  194. data/yard-stubs/carray_cast.rb +78 -43
  195. data/yard-stubs/carray_conversion.rb +13 -1
  196. data/yard-stubs/carray_copy.rb +8 -3
  197. data/yard-stubs/carray_core.rb +3 -83
  198. data/yard-stubs/carray_count.rb +16 -2
  199. data/yard-stubs/carray_lazy.rb +210 -1
  200. data/yard-stubs/carray_loop.rb +4 -1
  201. data/yard-stubs/carray_mask.rb +34 -10
  202. data/yard-stubs/carray_math.rb +1499 -4
  203. data/yard-stubs/carray_median_percentile.rb +26 -8
  204. data/yard-stubs/carray_order.rb +43 -82
  205. data/yard-stubs/carray_scatter.rb +10 -3
  206. data/yard-stubs/carray_segment.rb +72 -0
  207. data/yard-stubs/carray_slab.rb +11 -8
  208. data/yard-stubs/carray_sort.rb +50 -8
  209. data/yard-stubs/carray_test.rb +10 -0
  210. data/yard-stubs/carray_utils.rb +0 -26
  211. metadata +19 -19
  212. data/ext/ca_for_each_element.h +0 -239
  213. data/ext/carray_build_flags.h +0 -3
  214. data/ext/carray_config.h +0 -28
  215. data/ext/carray_kernels_bincmp.c +0 -4446
  216. data/ext/carray_kernels_binop.c +0 -11001
  217. data/ext/carray_kernels_init.c +0 -1131
  218. data/ext/carray_kernels_map.c +0 -3467
  219. data/ext/carray_kernels_moncmp.c +0 -2097
  220. data/ext/carray_kernels_monop.c +0 -18313
  221. data/ext/carray_kernels_reduce_aggregate.c +0 -25837
  222. data/ext/carray_kernels_reduce_boolean.c +0 -330
  223. data/ext/carray_kernels_reduce_cumulative.c +0 -14593
  224. data/ext/carray_kernels_reduce_extreme.c +0 -16948
  225. data/ext/carray_kernels_reduce_variance.c +0 -3910
  226. data/ext/carray_kernels_scan.c +0 -3693
  227. data/ext/carray_kernels_search.c +0 -32138
  228. data/ext/carray_kernels_sort.c +0 -10626
  229. data/ext/carray_kernels_triop.c +0 -1392
@@ -9,6 +9,9 @@
9
9
  #
10
10
  # Both are class methods (symmetric N-ary; no frame is privileged), like
11
11
  # +CArray.meld+ / +CArray.concatenate+, not instance verbs.
12
+ #
13
+ # +CAFrame.stack+ and +CAFrame#unstack+ add and take away a layer axis
14
+ # instead of rows.
12
15
 
13
16
  class CAFrame
14
17
  # Weld frames along the row axis, view-style. Each output column is
@@ -28,6 +31,13 @@ class CAFrame
28
31
  #
29
32
  # The index is welded too when every frame has one (their +axis_name+
30
33
  # must agree); if none do, the result has no index; a mix raises.
34
+ #
35
+ # A frame with no rows adds no values, so it takes no part in the result
36
+ # past these checks: its column set, its N-D columns' trailing dimensions
37
+ # and its index still have to agree, but the
38
+ # data types are the other frames' (a header-only CSV reads as object
39
+ # columns, which would otherwise refuse or demote the rest). When every
40
+ # frame is empty, the first one stands for them.
31
41
  # Column-set mismatch raises — a union-with-UNDEF mode is a possible
32
42
  # future opt-in, kept out here to stay explicit (memo §4.2).
33
43
  #
@@ -37,8 +47,11 @@ class CAFrame
37
47
  frames = frames.flatten
38
48
  check_concat_inputs(frames, verb: "meld")
39
49
  first = frames.first
40
- names = first.variable_names
50
+ names = first.column_names
41
51
  check_column_sets(frames, names, verb: "meld")
52
+ index_pieces(frames, verb: "meld")
53
+ check_trailing_shapes(frames, names, verb: "meld")
54
+ frames = frames_with_rows(frames)
42
55
  cols = {}
43
56
  names.each do |name|
44
57
  cols[name] = CArray.meld(frames.map { |f| f[name] }, axis: 0)
@@ -54,8 +67,9 @@ class CAFrame
54
67
  # For a view frame that shares storage with the inputs (strict same data type
55
68
  # per column, chain composability preserved) use {CAFrame.meld}.
56
69
  #
57
- # Column matching, index handling, and column-set / index-mix rules
58
- # match {CAFrame.meld}.
70
+ # Column matching, index handling, column-set / index-mix rules and the
71
+ # treatment of a frame with no rows match {CAFrame.meld}: an empty frame's
72
+ # data types take no part in the common type.
59
73
  #
60
74
  # CAFrame.concatenate(jan, feb, mar) # eager, independent result
61
75
  # CAFrame.concatenate([jan, feb, mar]) # an Array is accepted too
@@ -63,8 +77,11 @@ class CAFrame
63
77
  frames = frames.flatten
64
78
  check_concat_inputs(frames, verb: "concatenate")
65
79
  first = frames.first
66
- names = first.variable_names
80
+ names = first.column_names
67
81
  check_column_sets(frames, names, verb: "concatenate")
82
+ index_pieces(frames, verb: "concatenate")
83
+ check_trailing_shapes(frames, names, verb: "concatenate")
84
+ frames = frames_with_rows(frames)
68
85
  cols = {}
69
86
  names.each do |name|
70
87
  cols[name] = CArray.concatenate(frames.map { |f| f[name] })
@@ -72,6 +89,108 @@ class CAFrame
72
89
  new(cols, axis_name: first.axis_name, index: concatenate_index(frames))
73
90
  end
74
91
 
92
+ # Stack frames of the same shape as layers. Each output column is
93
+ # +CArray.stack+ of that column across the frames along +axis+, counted in
94
+ # the column's own axes (0 is the row axis, so +axis+ starts at 1): a
95
+ # scalar column of K frames becomes a column of shape (nrow, K). The rows
96
+ # stay as they are, so row verbs work on the stack unchanged and a layer is
97
+ # a column axis -- +s["temp"][nil, k]+ is frame k's column, and
98
+ # +s["temp"].mean(axis: 1)+ reduces across the layers.
99
+ #
100
+ # The result is a view frame: its columns are CAStack views over the
101
+ # frames' columns, and writes reach them. Call +copy+ for an independent
102
+ # frame. The layers carry no labels; keep them alongside (the dates of the
103
+ # files, say) as an array of your own.
104
+ #
105
+ # Every frame must have the same columns, the same number of rows and the
106
+ # same index (by value) under the same axis name, or none; the result
107
+ # takes the first frame's. +unstack+ is the inverse:
108
+ #
109
+ # s = CAFrame.stack(jan1, jan2, jan3) # temp:float64 -> temp:float64[3]
110
+ # CAFrame.stack(*s.unstack(axis: 1)) # the same frame again
111
+ def self.stack(*frames, axis: 1)
112
+ frames = frames.flatten
113
+ check_concat_inputs(frames, verb: "stack")
114
+ check_layer_axis(axis, "stack")
115
+ first = frames.first
116
+ names = first.column_names
117
+ check_column_sets(frames, names, verb: "stack")
118
+ frames.each_with_index do |f, i|
119
+ next if i.zero?
120
+ unless f.nrow == first.nrow
121
+ raise ArgumentError,
122
+ "stack: frame #{i} has #{f.nrow} rows, frame 0 has #{first.nrow}"
123
+ end
124
+ end
125
+ indexes, = index_pieces(frames, verb: "stack")
126
+ if indexes && ! indexes.all? { |x| same_index_values?(x, indexes.first) }
127
+ raise ArgumentError, "stack: the frames' indexes differ; stacked rows have to be the same rows"
128
+ end
129
+ cols = {}
130
+ names.each do |name|
131
+ parts = frames.map { |f| f[name] }
132
+ if axis > parts.first.ndim
133
+ raise ArgumentError,
134
+ "stack: axis #{axis} out of range for column #{name.inspect} " \
135
+ "(a #{parts.first.ndim}-D column takes 1 to #{parts.first.ndim})"
136
+ end
137
+ cols[name] = CArray.stack(parts, axis: axis)
138
+ end
139
+ new(cols, axis_name: first.axis_name, index: first.index)
140
+ end
141
+
142
+ # Split the frame along a column axis into an Array of frames, one per
143
+ # position on that axis -- the inverse of {CAFrame.stack}. +axis+ counts
144
+ # the columns' own axes (0 is the row axis, so it starts at 1). Every
145
+ # column must have that axis, and the same length along it: a column
146
+ # without it has no layer to give each frame. Each frame's columns are
147
+ # views of this frame's, and each has this frame's index.
148
+ #
149
+ # s.unstack(axis: 1) # => [frame of layer 0, frame of layer 1, ...]
150
+ def unstack(axis:)
151
+ self.class.send(:check_layer_axis, axis, "unstack")
152
+ if @columns.empty?
153
+ raise ArgumentError, "unstack: the frame has no columns to unstack"
154
+ end
155
+ length = nil
156
+ @columns.each do |name, col|
157
+ unless axis < col.ndim
158
+ raise ArgumentError,
159
+ "unstack: column #{name.inspect} is #{col.ndim}-D and has no axis #{axis}"
160
+ end
161
+ length ||= col.shape[axis]
162
+ unless col.shape[axis] == length
163
+ raise ArgumentError,
164
+ "unstack: column #{name.inspect} has #{col.shape[axis]} along axis #{axis}, " \
165
+ "the others #{length}"
166
+ end
167
+ end
168
+ (0...length).map do |k|
169
+ cols = @columns.to_h do |name, col|
170
+ idx = [nil] * col.ndim
171
+ idx[axis] = k
172
+ [name, col[*idx]]
173
+ end
174
+ CAFrame.new(cols, axis_name: @axis_name, index: @index)
175
+ end
176
+ end
177
+
178
+ def self.check_layer_axis(axis, verb)
179
+ unless axis.is_a?(Integer)
180
+ raise TypeError, "#{verb}: axis must be an Integer (got #{axis.inspect})"
181
+ end
182
+ if axis == 0
183
+ raise ArgumentError,
184
+ "#{verb}: axis 0 is the row axis; rows are joined by meld or concatenate"
185
+ end
186
+ if axis < 0
187
+ raise ArgumentError,
188
+ "#{verb}: axis counts a column's axes from 1 (got #{axis}); " \
189
+ "a negative axis would mean a different axis in columns of different ndim"
190
+ end
191
+ end
192
+ private_class_method :check_layer_axis
193
+
75
194
  # ---- shared validators -------------------------------------------------
76
195
 
77
196
  def self.check_concat_inputs(frames, verb:)
@@ -86,15 +205,47 @@ class CAFrame
86
205
  expected = names.sort
87
206
  frames.each_with_index do |f, i|
88
207
  next if i.zero?
89
- if f.variable_names.sort != expected
208
+ if f.column_names.sort != expected
90
209
  raise ArgumentError,
91
- "#{verb}: frame #{i} has columns #{f.variable_names.inspect}, " \
210
+ "#{verb}: frame #{i} has columns #{f.column_names.inspect}, " \
92
211
  "expected the same set as #{names.inspect}"
93
212
  end
94
213
  end
95
214
  end
96
215
  private_class_method :check_column_sets
97
216
 
217
+ # A column's trailing dimensions are its shape, not a type the other
218
+ # frames can settle, so a frame with no rows has to agree on them too.
219
+ def self.check_trailing_shapes(frames, names, verb:)
220
+ names.each do |name|
221
+ trailing = frames.first[name].shape[1..]
222
+ frames.each_with_index do |f, i|
223
+ next if i.zero? || f[name].shape[1..] == trailing
224
+ raise ArgumentError,
225
+ "#{verb}: column #{name.inspect} has trailing dimensions " \
226
+ "#{f[name].shape[1..].inspect} in frame #{i}, #{trailing.inspect} in frame 0"
227
+ end
228
+ end
229
+ end
230
+ private_class_method :check_trailing_shapes
231
+
232
+ # Two indexes hold the same rows when their values agree cell by cell; a
233
+ # NaN label matches a NaN label, and a masked cell a masked cell.
234
+ def self.same_index_values?(a, b)
235
+ return false unless a.shape == b.shape
236
+ a.to_a.zip(b.to_a).all? do |x, y|
237
+ x.equal?(y) || x == y || (x.is_a?(Float) && y.is_a?(Float) && x.nan? && y.nan?)
238
+ end
239
+ end
240
+ private_class_method :same_index_values?
241
+
242
+ # The frames that add rows, or the first frame when none does.
243
+ def self.frames_with_rows(frames)
244
+ with_rows = frames.reject { |f| f.nrow.zero? }
245
+ with_rows.empty? ? [frames.first] : with_rows
246
+ end
247
+ private_class_method :frames_with_rows
248
+
98
249
  # ---- index helpers -----------------------------------------------------
99
250
 
100
251
  # Index welded via CArray.meld (view). Every frame must agree: all
@@ -16,21 +16,25 @@ class CAFrame
16
16
  # column name => value; a scalar cell is a Ruby value, an N-D cell is a Ruby
17
17
  # Array, and a masked cell (UNDEF) is +nil+. Normalizing UNDEF -> nil and
18
18
  # CArray -> Array (unlike +each_row+, which yields the raw view with UNDEF /
19
- # CArray slices) is what makes it round-trip: +from_records(df.to_records)+
20
- # rebuilds the same typing, and the result is JSON-serializable.
19
+ # CArray slices) makes the result JSON-serializable and lets
20
+ # +from_records(df.to_records)+ give back the column names, the values and
21
+ # the mask. The rest is not in a record: +from_records+ rebuilds each type
22
+ # from the values (an int32 comes back int64, a time column as an object
23
+ # column of its elements), the index comes back as an ordinary first
24
+ # column, and a frame with no rows gives +[]+, which has no column names.
21
25
  def to_records
22
26
  each_row.map { |row| row.transform_values { |v| record_value(v) } }
23
27
  end
24
28
 
25
29
  # Hand the frame over as a CArray with the minimum work (memo §11.9): a
26
- # 2-D **view** of shape (nrow, nvar), one column per variable in column
30
+ # 2-D **view** of shape (nrow, ncol), one per column in column
27
31
  # order (§12-C). Nothing is materialised — the result is a +CAStack+ over
28
32
  # the stored columns, so reads gather from them and writes flow back
29
33
  # (§3.6). Call +copy+ on it for an independent, owned matrix; +CArray.tabulate+
30
34
  # is the eager sibling that builds one directly.
31
35
  #
32
36
  # Only same-shape scalar (1-D) columns qualify. An N-D column has no single
33
- # matrix form and raises — escape it per column with +df["name"]+. A mixed
37
+ # matrix form and raises — take it per column with +df["name"]+. A mixed
34
38
  # data type set is promoted to a common type (+result_type+, §12-F) through
35
39
  # lazy cast lanes, so the promotion costs no buffer either.
36
40
  #
@@ -48,12 +52,12 @@ class CAFrame
48
52
  if nd
49
53
  raise ArgumentError,
50
54
  "to_ca needs all-scalar (1-D) columns; #{nd.first.inspect} is " \
51
- "#{nd.last.ndim}-D — escape per column with df[name]"
55
+ "#{nd.last.ndim}-D — take it per column with df[name]"
52
56
  end
53
57
  refuse_unshared_columns(cols) if writable
54
58
  begin
55
59
  CArray.stack(cols, axis: 1)
56
- rescue ArgumentError, RuntimeError => e
60
+ rescue ArgumentError, TypeError, CArray::DataTypeError => e
57
61
  # The columns have no common type (a text or Face column beside a
58
62
  # numeric one). Point at the frame-level verb that gives them one.
59
63
  raise e.class,
@@ -87,7 +91,9 @@ class CAFrame
87
91
  if UNDEF.equal?(v)
88
92
  nil
89
93
  elsif v.is_a?(CArray)
90
- v.to_a.map { |e| UNDEF.equal?(e) ? nil : e }
94
+ record_value(v.to_a)
95
+ elsif v.is_a?(Array)
96
+ v.map { |e| record_value(e) }
91
97
  else
92
98
  v
93
99
  end