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
data/CHANGELOG.md CHANGED
@@ -34,6 +34,830 @@ and a newer one. The 1.x history, up to the 2.0.0 release, is in
34
34
  neighbour ("as well", "the producer above") or leave unnamed the
35
35
  method, class or keyword it is about. -->
36
36
 
37
+ ## 3.0.3
38
+
39
+ - New: `CAFrame.from_csv` reads messy tables: `types: :infer` (numbers,
40
+ booleans, year-first dates and times), `types: { default: ... }`,
41
+ `missing:` tokens, `on_error:` (`:mask`, `:warn`, `:raise`), `columns:` to
42
+ read only some columns, and `header:` / `data:` / `column_names:` by line.
43
+ `CAFrame#cast` takes the same map; `parse_to_time(name, :infer)` finds a
44
+ column's date format; `describe` and `split_column` are new; `to_csv`
45
+ takes `encoding:` and `missing:`.
46
+
47
+ - New: `CAFrame` reshapes and resamples: `pivot` / `pivot_grid` / `melt`,
48
+ `stack_rows(by:)` / `unstack_rows`, `stack_columns` / `unstack_column`,
49
+ `CAFrame.stack` / `unstack` (frames as layers), and `resample(name, unit)`
50
+ for time bins. The stacked forms are views. `protect` makes a frame
51
+ read-only.
52
+
53
+ - New: `CAFrame` group reductions and `aggregate` reduce N-D columns along
54
+ the rows (a column `(N, L)` gives `(groups, L)`), take `min_count:` and
55
+ `fill_value:`, and pass a reduction its arguments
56
+ (`"p90" => ["temp", :percentile, 90.0]`).
57
+
58
+ - New: `value.segments(offsets:)` / `segments(lengths:)` iterate over
59
+ consecutive runs of cells, with `CArray.segment_offsets` /
60
+ `segment_index` to convert between lengths, boundaries and segment
61
+ numbers. The scans, `map`, `median` and `percentile` of
62
+ `group_by_category` take `axis:`, and a window's `median`, `percentile`
63
+ and `quantile` answer under the default `bounds: :skip`.
64
+
65
+ - New: `bitfield(range, type)` reads a field as a signed or wider integer;
66
+ `set_attrs(hash)`; `order(kind:)`; `tolerance: "10 minutes"` for time keys
67
+ in `locate_nearest_addr` and `CAFrame#join_asof`. A `CARecord` answers
68
+ `unique`, `value_counts`, `mode`, `is_in`, the set operations,
69
+ `categorize`, `count(v)` and `locate_addr`.
70
+
71
+ - New: for whoever registers a `CArray.expression_evaluator` (carray-jit):
72
+ mixed data types, integer powers, comparisons, `then_else`, `shift`, and
73
+ reductions, scans and sorts of an expression now reach the evaluator. An
74
+ evaluator that does not know a node declines, and CArray computes the
75
+ expression as before.
76
+
77
+ - New: for C extensions: `ca_iter_ensure`, `ca_sync_detach`,
78
+ `ca_attach_n` / `ca_allocate_n`, `ca_iter_state_init_l2_paired`,
79
+ `ca_stride_setup(NULL, ...)` and `ca_check_uninitialized` let an
80
+ extension clean up when its code raises.
81
+
82
+ - Change: `CAFrame` names its columns "columns": `variable_names` is now
83
+ `column_names`, `variables` is `columns` and `nvar` is `ncol`. The old
84
+ names are gone.
85
+
86
+ - Change: `sort_index`, `rank_index`, `partition_index`, `partition` and
87
+ `partition_copy` without `axis:` work on the whole array, as `sort` does;
88
+ they worked along axis 0. On an N-D array pass `axis: 0` to keep the old
89
+ result.
90
+
91
+ - Change: errors a caller can cause no longer raise `RuntimeError`: a bad
92
+ shape, step, axis, option or data type raises `ArgumentError`,
93
+ `TypeError`, `RangeError` or `CArray::DataTypeError`, and every index out
94
+ of range raises `IndexError` (`take_along_axis`, `axis2addr` and
95
+ `addr2index` raised others). `CArray::DataTypeError` is now a
96
+ `TypeError`. A write to a read-only array still raises `RuntimeError`.
97
+ Messages name the method you called and the value you gave. Update a
98
+ `rescue` that matched the old class or wording.
99
+
100
+ - Change: arguments that were truncated or ignored now raise: an axis,
101
+ `min_count:`, `kth` or `n` that is not an Integer; a misspelt keyword; a
102
+ keyword a method does not take (`min_count:` to `all` / `any` of an
103
+ iterator, as to the core); a Symbol column name in `cast` and `types:`;
104
+ `windows` given anything but Ranges (an exclusive range now leaves out
105
+ its end); `CA_INT32(0, 2)`; a fixlen array without `bytes:`. A negative
106
+ axis counts from the end everywhere. The indexer's method form
107
+ (`a[:eq, 0]`) calls public methods only.
108
+
109
+ - Change: a Hash passed as a positional argument is an argument, as in
110
+ Ruby 3, not keywords: `a.sum({axis: 0})` raises (write `a.sum(axis: 0)`
111
+ or `a.sum(**opts)`). An object array of Hashes can now be searched and
112
+ counted.
113
+
114
+ - Change: values that cannot be held raise instead of being bent: storing
115
+ `nil` into a numeric or boolean array, an Integer operand the array's type
116
+ cannot hold (`RangeError`; the comparisons answer by value instead), and a
117
+ String that is not a decimal number (`"010"` is now 10, and `to_type` gives
118
+ `UNDEF`). `CAFrame#cast` to an integer makes `2.5` `UNDEF` instead of 2.
119
+ Store `Float::NAN` or `UNDEF` for a missing value.
120
+
121
+ - Change: reductions with too few values: `variance` / `stddev` of fewer
122
+ than two values is `UNDEF` (pass `fill_value: 0.0` for the old answer),
123
+ and `minmax` of nothing is `[UNDEF, UNDEF]`. `fill_value:` is stored into
124
+ the result's type with or without `axis:`; `fill_value: UNDEF`,
125
+ `strip_mask(UNDEF)` and `unmask(UNDEF)` leave the cells masked.
126
+
127
+ - Change: arrays of mixed types meet in a type that holds both: signed with
128
+ unsigned integers (`uint8` with `int8` is `int16`, `uint64` with `int64` is
129
+ `:object`) and 32-bit or wider integers with `float32` (in `float64`) when
130
+ joined or matched. `CArray.result_type` lets a scalar take the arrays'
131
+ type, as an operator does (`result_type(float32_array, 0.1)` is
132
+ `:float32`). Arithmetic is unchanged.
133
+
134
+ - Change: integer and float edge cases no longer depend on the machine or
135
+ the argument order: a shift count means what it does to `Integer#<<`,
136
+ signed `rcp` / `rcp_mul` round down as `/` does, `to_type` to an integer
137
+ makes NaN `UNDEF`, `maximum` / `minimum` of two zeros are `0.0` / `-0.0`,
138
+ and `clip` raises when `min` exceeds `max` (a one-sided fill clamps the
139
+ other side).
140
+
141
+ - Change: `CAFrame#join` matches as SQL joins do: a key on 2 left and 3
142
+ right rows gives 6 rows, and a masked key matches nothing and stays as an
143
+ unmatched row. When the other frame's key is unique, `:left` gives the
144
+ same rows as before.
145
+
146
+ - Change: `CAFrame.from_csv`'s reading block takes the reader as its
147
+ parameter and its `body` verb is `data`
148
+ (`from_csv(path) { |r| r.skip 2; r.header; r.data }`). Text columns come
149
+ back as `CAString`. Contents that cannot be read raise
150
+ `CAFrame::UnreadableColumn` or `CAFrame::AmbiguousTimeFormat`, which are
151
+ not `ArgumentError`.
152
+
153
+ - Change: reading time from text is stricter: `parse_to_time` without a
154
+ format reads year-first text only (pass a format, or `:mixed` for the old
155
+ guess) in the finest unit the text shows; `CArray.time` and strptime
156
+ formats refuse trailing text and a time of day they cannot read; a date
157
+ the column's unit cannot hold raises `RangeError`. Before, these cells
158
+ were guessed, floored or masked.
159
+
160
+ - Change: `CAFrame#to_csv` writes a boolean as `1` / `0` and a float32 as
161
+ its shortest decimal, refuses a record column, and raises naming the row
162
+ and column of a cell it cannot encode. `extract` masks a cell its pattern
163
+ does not match, where it gave `""`.
164
+
165
+ - Change: `CAFrame`: a frozen frame refuses `[]=`, `set_index`, `cast` and
166
+ the other in-place verbs; `at` on a numeric index takes numbers only; the
167
+ index of a `group_by` result keeps the key's type (a `CATime` key gives a
168
+ `CATime` index); group `sum` / `mean` / `min` / `max` include every column
169
+ whose type has the reduction (times and strings among them).
170
+
171
+ - Change: `CAStruct` / `CARecord`: records are ordered only by the members
172
+ named in `CArray.struct(order_by:)`; without it sorting, `min` / `max` and
173
+ comparison raise. An element's `[]` takes a String or a position (a
174
+ Symbol raises), a member named like a method (`hash`, `size`) gets no
175
+ accessor, a nested member reads as a view (`dup` it for a copy), boolean
176
+ and 1-bit members read as `true` / `false`, and `UNDEF` cannot be stored
177
+ in a member. Records of another struct are refused.
178
+
179
+ - Change: a `CARecord` exported through MemoryView uses Ruby's own format
180
+ (`ruby/memory_view.h`) instead of PEP 3118 `T{...}`; the member names are
181
+ `CARecord#memory_view_field_names`. A union is no longer exported.
182
+
183
+ - Change: a time array compares with, searches for and stores a `String` or
184
+ `Date` as well as a `Time`, at the precision the value carries: a value
185
+ off the array's grid (09:00 against a day array) raises, where a `Time`
186
+ was floored. `union` of two units gives the finer unit. `inspect` shows
187
+ the tick and each cell as `to_s` does.
188
+
189
+ - Change: `==` on a Face (a time, string or categorical array) compares the
190
+ values it holds, and only with a Face of its own class: time arrays of
191
+ different units holding the same instants are `==`, and a `CAString` is
192
+ no longer `==` to an object array of the same strings (compare
193
+ `to_type(:object)` for that).
194
+
195
+ - Change: Faces (`CATime`, `CATimedelta`, `CACategorical`, string arrays):
196
+ `.lazy` returns the Face and computes eagerly; storing one into a
197
+ non-object plain array raises (convert with `to_type(:object)` or
198
+ `ticks`); `CArray.save`, `dump` and `Marshal.dump` refuse one, and a
199
+ `CAWrap` (save `.parent` or `.copy`); `CArray.stack([face])` returns the
200
+ Face, and mixing Faces raises.
201
+
202
+ - Change: for whoever writes a Face: the hooks are renamed
203
+ `storage_to_element` / `element_to_storage`, and a stored value goes
204
+ through `element_to_storage` once (it went through twice). A Face written
205
+ in Ruby on `CAObject` over a numeric storage refuses arithmetic, math and
206
+ reductions unless it passes `numeric: true`.
207
+
208
+ - Change: views: a view of a frozen array is read-only rather than frozen
209
+ (`RuntimeError`, `frozen?` false), so `sort`, selections and lazy
210
+ expressions work on a frozen array. `then_else` and `sort` on a lazy
211
+ expression return a read-only lazy view (call `copy` for a snapshot), and
212
+ `then_else` branches broadcast as operands do. `dup` of a `CAWrap` is a
213
+ plain `CArray`. Attributes survive `copy` and `to_type`.
214
+
215
+ - Change: a `bitarray` view has no mask and refuses a masked array (use
216
+ `value.bitarray`); `pack_bits` raises on masked cells (pack the mask with
217
+ `validity_bits`). `bitfield` refuses a complex array.
218
+
219
+ - Change: on an object array, `min`, `max`, `cummin`, `pmin` and the rest
220
+ compare with `<=>`, as `sort` does; a pair that cannot be ordered raises
221
+ `ArgumentError`. `median`, `percentile` and `quantile` answer in the
222
+ elements' own arithmetic (`CA_OBJECT([1, 2]).median` is `3/2`; convert
223
+ with `to_type(:float64)` for a Float). `&`, `|` and `^` with a boolean
224
+ array read its cells as `true` / `false`, eager and lazy.
225
+
226
+ - Change: smaller behaviour changes: `locate_nearest_addr` (and `join_asof`,
227
+ `snap_to`) with `direction:` matches beyond the reference's ends (limit it
228
+ with `tolerance:`); `map_slab` carries the mask of the block's result; a
229
+ masked cell of a one-axis boolean selector selects nothing; `shift`,
230
+ `roll`, `tile` and `window` on a zero-length axis return an empty view;
231
+ boolean sums in `axis_group` and categorical scans are uint64, as in the
232
+ core; `group_by_category` raises `TypeError` for a non-categorical.
233
+
234
+ - Change: an operand that exports MemoryView and defines `to_ca` (an Arrow
235
+ array, with a bridge) is taken through `to_ca`, so Arrow nulls arrive as
236
+ masked cells and time columns as `CATime`.
237
+
238
+ - Change: for C extensions: `CArray::VERSION_CODE` is
239
+ `major*10000 + minor*100 + teeny` (`30003`); the `CA_FOR_EACH_ELEMENT`
240
+ macros, `CA_WITH_BUFFER` and `CA_CHECK_BOUND` are removed (use
241
+ `ca_call_cslab_N_r`, `ca_call_cfunc_N_r` or `rb_ca_call_with_buffer`);
242
+ the `_INOUT` and `_PAIR` fiber macros raise on a shape mismatch; kernels
243
+ are built with `-fwrapv`. Rebuild the extension.
244
+
245
+ - Change: faster, with the same results: `from_csv` with `types:` (two to
246
+ five times), text to float (eight times), `CAConstString#to_string`,
247
+ reductions over gathered, `shift` and `window` views, a repeated
248
+ `CArray.fuse` block, and selecting from a lazy expression.
249
+
250
+ - Fix: wrong answers that came without an error. An in-place operation
251
+ reading an argument that shares its memory read cells it had already
252
+ written (`a[1..5].add!(a[0..4])` gave a running sum; `scatter_*!`,
253
+ `inherit_mask` and `mask=` too); `refer` to a wider type on a transposed
254
+ or stepped parent read neighbouring values; views of a stack or meld,
255
+ and a slice of a lazy expression, returned other cells; a Float operand
256
+ of an integer array was truncated (`count(1.5)` found `1`); `mask=` with
257
+ another shape copied cells by count; a masked index read cell 0 in
258
+ `gather_nd`; `stddev` of NaN data was 0.0.
259
+
260
+ - Fix: wrong answers that came without an error, time and strings: time
261
+ keys of different units matched or not depending on the side in `is_in`,
262
+ `join` and `align`; `locate_nearest_addr` picked a wrong row for a NaN,
263
+ an infinite or a nanosecond key; `unmask(time)`, `resize(fill_value:)`
264
+ and `insert_block` read a `Time` as a count; `CAConstString` comparisons
265
+ compared buffer positions; a `CAString` compared against a time,
266
+ categorical or const string compared their encodings.
267
+
268
+ - Fix: writes that were lost now reach the array: through a grid, selection
269
+ or `select_axis` over a reshaped transpose, through a stack or meld of
270
+ views, into a nested struct member, into the mask copy of a stack or meld,
271
+ and the masked cells of `c[] = c.flip(0)`.
272
+
273
+ - Fix: crashes and reads outside an array: a garbage collection during an
274
+ operation on a converted or computed array (`CAObject`, lazy, object
275
+ data); a slab kept after `each_slab`; a `CARecord` after `Marshal.load`;
276
+ shapes and indices whose arithmetic overflows; `refer(:object)` and
277
+ `bitfield` on object data; `scatter_*!` on complex; `MIN / -1` on Linux
278
+ x86; struct members outside the record; fixlen stores of another width;
279
+ reductions in several Ractors.
280
+
281
+ - Fix: an operation that raises part way (a block, a conversion, a
282
+ division by zero) leaves its arrays usable and frees its memory. Before,
283
+ an array could stay attached, and after 32 such errors every lazy
284
+ expression failed with "all 32 slots in use".
285
+
286
+ - Fix: iterator reductions answer as the same reduction on an array: every
287
+ one takes `min_count:` and `fill_value:` (`blocks(n).percentile(p,
288
+ min_count: k)` read them as a second percentile), a group's counts apply
289
+ `min_count:` to the right cells, a group's `wmean` no longer takes
290
+ `fill_value:` as a weight, a window of only NaN gives NaN, and object
291
+ weights in `segments` stay exact.
292
+
293
+ - Fix: `CAFrame` round trips through CSV and records: quotes, separators of
294
+ several characters or spaces, lines of spaces, a duplicated header name,
295
+ complex `Infinity` / `NaN`, months and years, hour 24, a one-column file
296
+ with a blank name; nested arrays, `Complex` and `Rational` in
297
+ `from_records`. `fill`, `to_table` and `filter(keep_masked:)` work with
298
+ N-D and categorical columns; `meld` and `concatenate` take empty frames;
299
+ `group_by` takes a categorical key and treats NaN in a composite key as
300
+ one value.
301
+
302
+ - Fix: Faces stay Faces: the reductions of `windows` and `blocks`,
303
+ `partition_copy`, `then_else`, `clip`, `mask_eq` and `meld` return the
304
+ Face, and a Face's own state survives `min(axis:)`, `cummax` and
305
+ `partition_copy`. `axis_group` and categorical reductions take boolean,
306
+ complex, object and Face values; a time array's `fill_value:` is a time
307
+ on every path. `CAFrame#fill(:linear)` interpolates any Face that defines
308
+ `linear_fetch`. A categorical refuses `clip` and `minmax` as it refuses
309
+ `min`.
310
+
311
+ - Fix: types kept through operations: complex arrays work with `unique`,
312
+ `is_in` and the rest of that family; `meld` reductions answer as on a
313
+ copy; fixlen strings of different widths match; `atan2`, `hypot` and
314
+ others keep float32 and integer types; a record member declared with
315
+ `endian:` reads, sorts, saves and loads correctly.
316
+
317
+ - Fix: NaN and masked cells are treated the same everywhere: NaN is last in
318
+ object sorts and `categorize`, loses `min` / `max`, and is found by
319
+ `CAFrame#at(Float::NAN)`; `min_index` never points at a masked cell or a
320
+ NaN; `min_count:` and `fill_value:` are honoured where they were ignored;
321
+ a lazy expression's mask follows its operands; a selection or `shift`
322
+ follows later mask changes; `invert_mask` works on a selection.
323
+
324
+ - Fix: MemoryView: a `CAFixlenString` exports as bytes and a frozen fixlen
325
+ array can be exported; `CATime`, `CACategorical` and `CAConstString` are
326
+ refused by `memory_view_available?` with the reason, instead of answering
327
+ `true` and failing.
328
+
329
+ - Fix: with carray-jit: two expressions that differ only in which operand an
330
+ operation reads no longer share a kernel, and a store whose destination
331
+ overlaps an operand is computed by CArray.
332
+
333
+ - Fix: for C extensions: a `CA_KERNEL_WRITE` walk over a view that holds a
334
+ cell more than once keeps the cells the kernel wrote; a walk left with
335
+ `break` writes back its slab; writes through a `CAGrid`, `CAWindow` or
336
+ `CAShift` over a view reach the array; the sweep engine and paired walks
337
+ read an argument sharing the output's memory as it was; a view class
338
+ that is not registered with `ca_iter_register_source_kind` is still
339
+ accepted. Rebuild the extension.
340
+
341
+ ## 3.0.2
342
+
343
+ - New: `CArray::AddressBasis`, for a C extension whose code addresses cells
344
+ itself rather than being handed them — a kernel generated from an
345
+ expression, which writes its own loop. `open` lends a pointer and one byte
346
+ stride per axis for the length of a block, and closes what it opened even
347
+ when the block raises; `classify` reports how an array would be opened
348
+ without opening it. It is a runtime facility at the `ca_attach` layer, not
349
+ a user API: what it lends is a raw machine address, so it is described in
350
+ the developer's guide rather than in the user documentation.
351
+
352
+ - New: a `CAString` column can be searched, not only sorted: `bsearch`,
353
+ `bsearch_addr`, `search` and `count(v)` answer where they used to raise
354
+ `ArgumentError`. A cell of one is the Ruby String it shows, so a String query
355
+ compares against it directly, with nothing to reconcile. Sorting, which
356
+ already worked, is unchanged, and so is `CAConstString`, which answers
357
+ `search` / `count(v)` natively and still has no `bsearch`.
358
+
359
+ - New: `count(v)` counts an object or fixlen array, which used to raise
360
+ `CArray::DataTypeError`. An object array compares by Ruby `==`, so
361
+ `count(1)` and `count(1.0)` agree, and `true` / `false` / `nil` are values to
362
+ count rather than the boolean array's `true` / `false`. A fixlen array
363
+ compares the whole cell by `memcmp`, with a short String query padded out to
364
+ the cell width -- so a 4-byte cell holding `"a\0\0\0"` is counted by
365
+ `count("a")`.
366
+
367
+ - New: `CArray.empty(data_type, dim, bytes: nil)` allocates without the zero
368
+ fill, for an array whose every cell is written before anything reads it. One
369
+ existing call changes: `CArray.empty(3, [4])` raised `TypeError` in 3.0.1
370
+ and now matches `CArray.new(3, [4])`.
371
+
372
+ - New: `CAFrame.from_csv` reads an open IO as well as a path, so CSV already
373
+ in memory need not go through a temporary file first. A String argument is
374
+ still always a path, never CSV text.
375
+
376
+ - New: `inspect_full` renders an array the way `inspect` does but without the
377
+ `...` abbreviation.
378
+
379
+ - New: `repeat` lays each element of an array down several times --
380
+ `v.repeat(2)`, or `v.repeat([3, 1, 2])` for a count each. It is not `tile`,
381
+ which lays the whole array down again. The result is a view.
382
+
383
+ - New: `unique`, `nunique` and `mask_duplicates` take `along: k`, comparing
384
+ whole sub-arrays instead of cells -- `z.unique(along: 0)` gives the distinct
385
+ rows of a 2-D array. Giving both `along:` and `axis:` raises.
386
+
387
+ - New: each numeric data type names its own limits on its class --
388
+ `CArray::Int32::MIN` / `MAX`, and `TINY` / `EPSILON` for float and complex
389
+ types. `MIN` is the bottom of the range, where Ruby's `Float::MIN` is what
390
+ is called `TINY` here.
391
+
392
+ - New: `CArray::Rng` is a random number generator with its own state, which
393
+ `random!`, `randomn!` and `shuffle!` accept as `rng:` alongside a Ruby
394
+ `Random`. Without `rng:`, or with a Ruby `Random`, nothing changes.
395
+
396
+ - New: `CArray#factorize` answers `[codes, levels]` in one pass, for a caller
397
+ who wants the codes as storage rather than the `CACategorical` that
398
+ `categorize` builds from the same two.
399
+
400
+ - New: C extensions only. `CA_FOR_EACH_FIBER_PAIR` and
401
+ `CA_FOR_EACH_FIBER_PAIR_MASKED` yield one contiguous fiber from each of two
402
+ sources at the same position.
403
+
404
+ - Change: `CArray.meld` (and `CAMeld.new`, and so `CAFrame.meld`) now treats a
405
+ homogeneous list of Faces the way `CArray.stack` does: a Face whose state is
406
+ per-parent is refused, and one that can be carried is kept on the result.
407
+ `CAConstString` and `CAString` now raise `ArgumentError` — weld the storage
408
+ instead, with `.parent`, or use `CArray.concatenate`. `CAFixlenString`,
409
+ `CATime` and `CATimedelta` come back as themselves rather than as the raw
410
+ storage; melding pieces whose Face state differs, such as two `CATime`
411
+ columns in different units, now raises rather than welding the ticks. Lists
412
+ of plain arrays, and a list of one, are unaffected.
413
+
414
+ - Change: `CAConstString.wrap` now checks the `(start, end)` pairs it is given
415
+ against the buffer, and takes ownership of the offsets entity by marking it
416
+ read-only. A pair outside the buffer raises `ArgumentError` naming the
417
+ element; masked cells are exempt, since their bytes may be anything. Code
418
+ that built a column with well-formed offsets is unaffected, except that the
419
+ entity it passed can no longer be written afterwards — pass `.copy` to keep
420
+ a mutable one. This also means the storage behind an existing column
421
+ (`column.parent[i] = ...`) now raises rather than silently rewriting a column
422
+ that reports itself read-only.
423
+
424
+ - Change: `all` and `any` on an `axis_group` reduction now require a boolean
425
+ payload, as `CArray#all` / `#any` and the other iterators do. They folded any
426
+ numeric payload, counting a non-zero cell as true, so `data.all` refused and
427
+ `data[g].all(axis: :group)` answered for the same float array. Convert first
428
+ if you meant the old reading: `data.ne(0)[g].all(axis: :group)`.
429
+
430
+ - Change: the `axis:` reductions on `group_by_category` now read the source
431
+ array when asked, rather than some of them answering from a result kept
432
+ from an earlier call. `sum`, `mean`, `min`, `max` and the counts shared a
433
+ kept result per axis while `prod`, the variance family and `wsum` / `wmean`
434
+ did not, so after a write through the source one iterator could report a
435
+ mean and a variance that no data can produce together. Reading several
436
+ members off one iterator now costs one kernel run each instead of one
437
+ shared run; keep the result if you want the old sharing. The no-axis
438
+ reductions are unchanged: they still work from the copy taken when the
439
+ iterator was built.
440
+
441
+ - Change: `CAFrame#at(UNDEF)` now raises `ArgumentError` instead of returning a
442
+ row. An index can hold a masked cell -- an `:outer` / `:right` join and
443
+ `align` both produce one -- but a row with no label cannot be identified by
444
+ one, and two undefined labels are not the same label; the key matching behind
445
+ `join` and `align` already treats a masked key as matching nothing. Use
446
+ `df.filter { |f| f.index.is_masked }` for the rows with no label, which also
447
+ handles more than one of them. Asking for a real label whose cell is masked
448
+ still raises `KeyError`, unchanged.
449
+
450
+ - Change: functions built on the C-extension bridge (`ca_call_cfunc_*`,
451
+ `ca_call_cslab_*`, and `CAMath.spherical_to_xyz` / `xyz_to_spherical`)
452
+ pair two array operands only when their shapes agree, and otherwise
453
+ raise `ArgumentError` naming both shapes. Arrays of the same size but
454
+ different shape, such as (2,3) and (3,2), used to be accepted and read
455
+ in flat order; reshape one of them first. Arrays of different sizes
456
+ raised `RuntimeError` before, so a `rescue` of that class needs
457
+ updating. A scalar still pairs with any array.
458
+
459
+ - Change: `min`, `max`, `minmax`, `cummin` and `cummax` answer `NaN`, and
460
+ `min_index`, `max_index`, `min_addr` and `max_addr` answer `UNDEF`, when every
461
+ cell a float array contributes is `NaN`. They used to answer `Infinity`,
462
+ `-Infinity`, the interval `[Infinity, -Infinity]` and position `0`. A `NaN`
463
+ still loses to any number, so an array holding at least one number answers as
464
+ before, as does one holding only real infinities. Empty and all-masked still
465
+ answer `UNDEF`, and integer, boolean, fixlen and object arrays are unchanged
466
+ -- an object array already answered `NaN`. To have `NaN` counted as missing
467
+ rather than skipped, call `mask_invalid` first; `min_count:` and `fill_value:`
468
+ act on masked cells and do not reach `NaN` ones.
469
+
470
+ - Change: the `CAConstString` ordering family takes `axis:` -- and `kind:` /
471
+ `masked_position:` / `keep_axis:` where CArray does -- across `min`, `max`,
472
+ `minmax`, `min_index`, `max_index`, `sort`, `sort_copy`, `sort_addr`,
473
+ `sort_index`, `rank_index`, `order`, `partition_copy` and `partition_index`.
474
+ Three answers move to CArray's: `sort_index` gives per-fiber indices where it
475
+ gave view-flat addresses (ask `sort_addr` for those); `sort` with no `axis:`
476
+ flattens first, where it kept the shape (a 1-D column is unaffected); and
477
+ `min` / `max` on an empty or wholly masked column give UNDEF, not nil.
478
+
479
+ - Change: `CABlock#count` and `CAWindow#count` are gone. They gave back the
480
+ per-axis number of cells the view exposes -- which is what `shape` answers
481
+ -- and in doing so hid `CArray#count` on the two classes an indexing
482
+ expression lands on most: `a[2...8].count(true)` raised `ArgumentError`,
483
+ and `a[2...8].count` gave a shape rather than a population. Read the
484
+ geometry with `shape`. `size0` / `start` / `step` / `offset`, which say
485
+ where the view sits in its parent, are unchanged.
486
+
487
+ - Change: `CArray.jit_for`, `CArray.jit_each` and `CArray.jit_map` are no
488
+ longer defined here; they arrive with `require "carray/jit"`. Without it a
489
+ call raises `NoMethodError` where 3.0.1 raised `NotImplementedError`, so
490
+ code that rescued that to fall back asks `CArray.respond_to?(:jit_each)`.
491
+
492
+ - Change: the Ruby attach surface is gone from released builds:
493
+ `CArray.attach` / `.attach!`, `CArray#attach` / `#attach!`, and
494
+ `#__attach__` / `#__sync__` / `#__detach__`. Write through the array
495
+ directly instead. `CArray#attached?` and the C lifecycle are unchanged.
496
+
497
+ - Change: `a[1, :_]` returns a view of the axes `:_` asked for instead of
498
+ raising `IndexError`. To keep an axis rather than drop it, index it with
499
+ something that is not a scalar -- `a[[1], :_]`.
500
+
501
+ - Change: C extensions only. A kernel iterator init the engine refuses now
502
+ raises instead of returning a code the block macros discarded. To handle a
503
+ refusal rather than propagate it, call `ca_iter_state_init_l1` / `_l2`
504
+ directly and read the code.
505
+
506
+ - Change: `CACategorical.from_codes` now materialises `codes` when it is a
507
+ view rather than an array of its own, so writing through the array the view
508
+ was taken from no longer changes the categorical underneath it. A wrapped
509
+ memory view is an array of its own and is still adopted without a copy, so a
510
+ zero-copy import stays zero-copy. The array you pass is never marked
511
+ read-only beyond what you handed over.
512
+
513
+ - Change: `CACategorical.from_codes` now checks what it is handed and
514
+ normalises it. It raises `ArgumentError` for duplicate labels, for more
515
+ labels than the codes data type can carry once its top value is reserved as
516
+ the exclusion sentinel, and for an unmasked code outside `0...labels.size`
517
+ that is not the sentinel. A cell that arrives masked also gets the sentinel
518
+ written into its code byte, so the mask and the byte now agree for every
519
+ reader, a byte-reinterpret export included. Codes built by `categorize`
520
+ already satisfy all of this, so nothing changes for a categorical made that
521
+ way.
522
+
523
+ - Change: `search_nearest` and `search_nearest_addr` work on an object array of
524
+ numbers, and say why when they cannot. They measured only with `#distance`,
525
+ and since `Numeric#distance` became an opt-in refinement -- which a C-level
526
+ call does not see -- that raised `NoMethodError` for an Integer as readily as
527
+ for a String. A number is now measured as `(query - cell).abs`, exactly for
528
+ Rational and BigDecimal; an object defining a real `#distance` still uses it;
529
+ anything else raises `CArray::DataTypeError` naming the query's class, and
530
+ points at `search` / `bsearch` for an exact match. Numeric arrays are
531
+ unaffected.
532
+
533
+ - Change: `CAFrame.from_csv` reads a missing field as UNDEF in every column,
534
+ not only in one named by `types:`. An unquoted empty field, and a cell a
535
+ short row never reached, used to arrive as a Ruby `nil` sitting in an
536
+ uncast column, so a mask written by `to_csv` did not survive the trip back.
537
+ A quoted empty field (`""`) is still the empty string, which is a value.
538
+ Code that worked around this with `col[:eq, nil] = UNDEF` can drop the line.
539
+
540
+ - Change: `CArray.time` reads a string array about eight times faster with an
541
+ explicit `format:`, and about three times faster letting it auto-detect --
542
+ so `CAFrame#parse_to_time`, which calls it, speeds up by the same amount.
543
+ Parsed values are unchanged.
544
+
545
+ - Change: `window` accepts `bounds:` as a Symbol as well as a String, which is
546
+ the spelling `windows` already took. Strings keep working.
547
+
548
+ - Change: C extensions only. A partial fill of an array backed by a CAObject
549
+ or CASource subclass takes the `fill_block` / `fill_addrs` slots where the
550
+ subclass defines them, instead of one `store_addr` per cell. Which cells are
551
+ written is unchanged, and a subclass defining no fill slot keeps the
552
+ per-cell path.
553
+
554
+ - Fix: `is_in`, `count(v)`, the set operations, `locate_addr`, `search`,
555
+ `bsearch` and `linear_section` no longer compare a Face operand by its
556
+ storage when that storage is not the value it shows. Passing a
557
+ `CAConstString` (whose cells are byte ranges) to one of these on another
558
+ Face used to answer from the byte ranges: where the two cell widths
559
+ coincided — a `CAConstString` cell is 16 bytes, and so is a
560
+ `CAFixlenString` cell whose column is 16 bytes wide — you got a wrong
561
+ answer with no error, and a set operation could return raw offset bytes as
562
+ its values. Such an operand now raises `ArgumentError`; convert it first,
563
+ with `#to_string` for a string Face, or pass `.parent` on both sides to work
564
+ in storage space. Plain operands, and Faces whose cells are their values
565
+ (`CAString`, `CAFixlenString`), are unaffected, as is the cross-unit
566
+ reconciliation `CATime` does through `to_comparable`.
567
+
568
+ - Fix: the reductions without `axis:` on `group_by_category` now agree with one
569
+ another about which values they are reducing. `cumsum` and the other scans
570
+ read the array when called while every other member worked from the copy
571
+ taken when the iterator was built, so a write through the source between two
572
+ calls was visible to one and not the other. All of them now answer about the
573
+ values as they were when the iterator was built; build a new iterator to pick
574
+ up a write. The `axis:` reductions read the array when called, unchanged.
575
+
576
+ - Fix: `CArray.load_from_file` no longer exhausts the stack. It was
577
+ registered for autoload but defined nowhere, so calling it recursed until
578
+ Ruby gave up; it now raises `NoMethodError` like any other method that does
579
+ not exist. Use `CArray.load`, which is unchanged. An autoload registration
580
+ whose library defines no such method now says which method and which
581
+ library, rather than recursing.
582
+
583
+ - Fix: a `group_by_category` reduction over a read-only Face — a
584
+ `CAConstString` column — no longer fails with an `IndexError` about a buffer
585
+ range. Such a Face cannot be built by writing into it, so `min`, `max` and
586
+ the other value members hand back the surface values (the strings) rather
587
+ than the Face. A writable Face such as `CATime` still answers in its Face.
588
+
589
+ - Fix: a group iterator from `axis_group` now answers `shape`, `ndim` and
590
+ `dim`, which every other iterator answers and which it returned `nil` for,
591
+ and its `count` takes the two forms the family declares: `count(UNDEF)` for
592
+ masked cells and `count(v)` for cells equal to `v`. Both previously raised
593
+ `ArgumentError` about the number of arguments.
594
+
595
+ - Fix: a `group_by_category` reduction over values that carry a Face (a
596
+ `CATime` column, say) now answers in that Face, as `CArray`'s own reduction
597
+ does: `min`, `max` and `median` come back as a `CATime` of elements rather
598
+ than failing with an internal message about a zero width. A member the core
599
+ does not define for that Face still refuses, in the core's own words.
600
+
601
+ - Fix: the band-only classifier shape for a `group_by_category(axis:)`
602
+ reduction is now reachable on a two-dimensional source, where it was
603
+ refused and the refusal listed it among the accepted forms. Where both the
604
+ case A shape and the band-only shape fit, which a square source allows,
605
+ case A is taken, as before. The refusal no longer names `sum` when another
606
+ reduction was the one called.
607
+
608
+ - Fix: `count(axis:)` and `count_not_masked(axis:)` on `group_by_category`
609
+ now work for a complex, boolean or object payload. They counted cells
610
+ through a numeric-only kernel, which refused those payloads for an answer
611
+ that never depended on the payload. The no-axis form already worked.
612
+
613
+ - Fix: a `group_by_category` iterator whose classifier does not line up
614
+ cell-for-cell with the value now says so. A no-axis reduction on one raises
615
+ `ArgumentError` naming the mismatch and pointing at the `axis:` form, rather
616
+ than a `NoMethodError` about `nil`; `elements` raises the same instead of
617
+ answering `nil`; and `inspect` says "per-fiber only" instead of printing an
618
+ empty grouping. `accumulate(axis:)`, which failed outright on such an
619
+ iterator, now works.
620
+
621
+ - Fix: a reduction from `group_by_category` now hands back an array of the
622
+ caller's own. `min`, `max`, `minmax`, `count`, `count_not_masked`,
623
+ `elements`, `min_index` and `max_index` returned the iterator's memo itself,
624
+ so writing into a result changed what that iterator answered from then on,
625
+ and changed it for the other members reading the same memo. `sum` already
626
+ copied. Nothing to change in calling code unless you relied on writing
627
+ through a result.
628
+
629
+ - Fix: an `axis_group` reduction or scan that raises part-way through no
630
+ longer leaks the working memory it had taken. An object-valued scan
631
+ (`cumsum`, `cummax` and the rest) calls back into Ruby for every cell, so a
632
+ value that will not coerce or a `<=>` that answers `nil` raises from an
633
+ ordinary call and used to leave roughly 36 bytes per source element behind
634
+ each time. Nothing to change in calling code.
635
+
636
+ - Fix: `CAFrame#set_index` on a frame that already has an index no longer
637
+ discards it. The index being replaced now goes back to being a column, the
638
+ same demotion `reset_index` performs and in the same position, so re-indexing
639
+ keeps every column and `set_index("b")` on a frame indexed by `"a"` is the
640
+ same as `reset_index` followed by `set_index("b")`. Previously the column the
641
+ old index had been made from was gone, with nothing said.
642
+
643
+ - Fix: `CAFrame#reset_index` now restores the row axis name the frame had
644
+ before `set_index` promoted a column over it, so the two are each other's
645
+ inverse as documented. It used to leave `"row"`, which is user-visible: the
646
+ row axis name is the header of the index's column in `to_csv` and its key in
647
+ a row `Hash`. A frame built with an index, or derived from one, never had an
648
+ earlier name, so `reset_index` still leaves the default there.
649
+
650
+ - Fix: `min` and `max` on an `axis_group` reduction now answer in the source
651
+ array's data type, as `CArray#min` / `#max` do, instead of float64 -- an
652
+ int64 beyond the float mantissa came back rounded. A boolean array answers
653
+ as its 0/1 numeric storage (`all` / `any` are the boolean-returning twins).
654
+ A group holding nothing but `NaN` now answers `NaN` rather than the
655
+ accumulator's infinity, and `min_addr` / `max_addr` answer UNDEF for it,
656
+ since no cell won.
657
+
658
+ - Fix: an `axis_group` reduction over a grouping whose group axis has length
659
+ zero now answers each output cell the way a group with no member is answered
660
+ -- `sum` 0, `prod` 1, `count` 0, `all` true, `any` false, and UNDEF for
661
+ `mean`, `min`, `max`, `min_addr`, `max_addr` and the variance family. It
662
+ previously returned an unmasked 0 for all of them, so a mean and a variance
663
+ both read as 0.0. A zero-length band axis, which reduces to no cells at all,
664
+ is unchanged.
665
+
666
+ - Fix: a per-category `min`, `max`, `min_index`, `max_index`, `median`,
667
+ `percentile` or `quantile` from `group_by_category` now treats `NaN` the way
668
+ `CArray`'s own reduction does: a `NaN` loses every contest, a category
669
+ holding nothing but `NaN` answers `NaN` for an extremum and UNDEF for a
670
+ position, and an order statistic sorts `NaN` last. Before, the answer
671
+ depended on where in the category the `NaN` sat, so the same values in a
672
+ different row order gave different results. Nothing to change in calling
673
+ code.
674
+
675
+ - Fix: `p` / `inspect` on a `CAFrame` whose only data is its index now shows the
676
+ table. It printed the summary line alone, because it gated on the column set
677
+ while the table itself counts the index as a column.
678
+
679
+ - Fix: `CAFrame` no longer reports a row count that nothing in the frame backs.
680
+ Splicing a frame that has no columns into another that has neither columns nor
681
+ an index left the target claiming the spliced frame's row count, while its own
682
+ `copy`, `head` and `filter` all answered 0 and it would then accept only
683
+ columns of that length. The count is now read off a column, or off the index
684
+ when there are no columns. Nothing to change in calling code.
685
+
686
+ - Fix: `CAFrame`'s `df[rows] = UNDEF` now refuses a row outside the frame on a
687
+ frame with no columns, as the read and delete forms already did. It used to
688
+ return quietly, because the bound check came from the column indexer the
689
+ selector was handed to and there was no column to hand it to. Masking a row
690
+ that does exist on such a frame is still a no-op -- there are no data cells,
691
+ and the index is left alone by design.
692
+
693
+ - Fix: `CAFrame`'s `to_table` (and so `p` / `puts` / `to_s`) now prints a masked
694
+ element inside an N-D cell as `_`, the marker it already used for a masked
695
+ scalar cell, instead of the literal `UNDEF`. Nothing to change in calling
696
+ code.
697
+
698
+ - Fix: `CAFrame#group_by` with a composite key no longer makes a group of its
699
+ own for rows whose key has a masked component. Such a row now forms no group,
700
+ which is what a single masked key cell already did. Nothing to change in
701
+ calling code unless you relied on the UNDEF-labelled group.
702
+
703
+ - Fix: two `CAFrame` verbs that change every column now decide before changing
704
+ any, so a column that refuses no longer leaves the frame half-changed in an
705
+ order that depends on how the columns were inserted. `df[sel] = UNDEF` on a
706
+ frame holding a read-only column (a categorical) raises without masking
707
+ anything, and `promote(type)` raises without casting anything when some
708
+ column would narrow. Nothing to change in calling code.
709
+
710
+ - Fix: `CAFrame.from_records` now reads a `nil` cell back as UNDEF in every
711
+ column, not only in one a numeric cast happens to convert. A string, boolean,
712
+ object or N-D column used to keep the `nil` as a value, so a mask written by
713
+ `to_records` did not survive the trip and a row with no index label came back
714
+ labelled `nil`. Note that the data type is still rebuilt from the values, so
715
+ an integer column returns as `int64` and a boolean column as an object
716
+ column; `cast` afterwards if the exact type matters.
717
+
718
+ - Fix: a CSV written from a frame with a single column -- or with only an
719
+ index -- now reads back with all of its rows. A masked cell is written as an
720
+ empty field, which for a one-column row is a line with nothing on it, and the
721
+ reader skipped it as a blank line. Blank lines in a file with more than one
722
+ column are still skipped, as a row there always carries a separator. Nothing
723
+ to change in calling code.
724
+
725
+ - Fix: linear gap-fill on an **integer** array no longer fills the cells
726
+ outside the interpolable span with `0` and drops their mask. It now leaves
727
+ them masked, as it already did for a float array and as the documentation
728
+ says. This covers `unmask(method: :linear)` and `strip_mask(method: :linear)`
729
+ as well as `CAFrame#fill(name, :linear)`, with or without a frame index.
730
+ Nothing to change in calling code.
731
+
732
+ - Fix: on a frame grouped by a numeric column, `CAFrame`'s `mean`, `sum`, `min`
733
+ and `max` shortcuts now work. They raised `axis_name "..." collides with a
734
+ column of the same name`, because the key column was reduced into the result
735
+ while also being its index; a key only stayed out of the way when its data
736
+ type was one a reduction skips anyway -- a string, boolean, categorical or
737
+ time column. A composite numeric key no longer returns its key columns as
738
+ reduced columns either, so it gives the same column set a composite string
739
+ key gives. `aggregate` and `table` were never affected. Nothing to change in
740
+ calling code.
741
+
742
+ - Fix: `CAFrame#filter(keep_masked: true)` no longer hands back a frame whose
743
+ index writes through to the original. Its columns were already independent,
744
+ so writing the result's index changed the original while writing its columns
745
+ did not. The result is now materialized throughout -- columns and index --
746
+ whether or not the selector actually carries a masked cell, so the same call
747
+ site no longer switches between sharing and copying depending on the data.
748
+ Code that wants a frame sharing storage with the original should use plain
749
+ `filter`, which is still a view-frame.
750
+
751
+ - Fix: reductions, scans and order statistics no longer leak memory when
752
+ reading their source raises part way through -- for example
753
+ `cumsum`, `sum` or `median` over a float64 view of an object array
754
+ holding a cell that is not a number. Each call used to leave the
755
+ slab the walk was gathering into behind. Nothing to change in calling
756
+ code.
757
+
758
+ - Fix: `a[sel]` no longer leaks memory when reading the boolean selector
759
+ raises -- for example `fake(CA_BOOLEAN)` over an int32 array holding a
760
+ 2. Nothing to change in calling code.
761
+
762
+ - Fix: a view that converts on read -- for example `fake(CA_BOOLEAN)` over
763
+ an int32 array holding a 2 -- now raises every time it is read, where
764
+ the second read used to succeed silently and return values from a
765
+ half-converted buffer. A view stacked or melded over such an array no
766
+ longer leaves its other parents attached when the read raises, and a
767
+ reshape of a lazy view no longer leaks its buffer. Nothing to change in
768
+ calling code.
769
+
770
+ - Fix: for C extensions, a callback passed to `ca_call_cfunc_*` or
771
+ `ca_call_cslab_*` may now `rb_raise` to refuse a value: the bridge
772
+ detaches and frees what it holds before the exception propagates,
773
+ where it used to leave an output view attached and its scratch memory
774
+ behind. The outputs are left partly written. Nothing to change in
775
+ calling code.
776
+
777
+ - Fix: when `to_type` on a view raises part way through the cast -- for
778
+ example an int32 value other than 0 or 1 cast to boolean -- the view is
779
+ no longer left holding a stale copy of its parent, which made later
780
+ reads through it return the old values. Nothing to change in calling
781
+ code.
782
+
783
+ - Fix: `copy` and `strip_mask(fill)` no longer leak the result's memory
784
+ when reading the source raises part way through -- for example a
785
+ float64 view of an object array holding a cell that is not a number.
786
+ Nothing to change in calling code.
787
+
788
+ - Fix: functions built on the C-extension bridge (`ca_call_cfunc_*`,
789
+ `ca_call_cslab_*`, the `CA_FOR_EACH_ELEMENT` macros, and
790
+ `CAMath.lgamma` and its siblings) no longer leak memory, or leave an
791
+ output view attached, when reading an operand raises part way through
792
+ -- for example a float64 view of an object array holding a cell that is
793
+ not a number. Nothing to change in calling code.
794
+
795
+ - Fix: `is_in`, `intersection`, `difference` and `union` take an Array or Range
796
+ of Strings against a fixlen array, where every such call raised
797
+ `CArray::DataTypeError` -- `CAFixlenString` included. The set is built at the
798
+ array's cell width, so a short String matches a padded cell the way it does
799
+ everywhere else. A set given as a CArray must still be of that width; when it
800
+ is not, the refusal now says which width was wanted instead of reporting a
801
+ data type mismatch between two fixlen arrays.
802
+
803
+ - Fix: comparing a fixlen array against a String compares it as a value of
804
+ that array's cell width. The String became an object operand, so `eq` / `ne` /
805
+ `lt` / `gt` / `ge` / `le` and the `[:eq, v]` indexer ran `String#==` per cell
806
+ against the cell's NUL-padded text -- and an array pads a short String on
807
+ write, so `a[i] = "be"` then `a.eq("be")` was false, `a.gt("be")` was true,
808
+ and the scan took 30x longer than the same one in `search`. Two fixlen
809
+ arrays of different widths compare as before, as does a Regexp for `match`.
810
+ `CAFixlenString` was never affected.
811
+
812
+ - Fix: `percentile` and `median` no longer interpolate between objects that
813
+ have no arithmetic. On a column of Strings `percentile(30)` quietly answered
814
+ `""` and even-length `median` raised `NoMethodError` from inside a funcall;
815
+ both now raise `CArray::DataTypeError` naming `method: :lower` / `:higher` /
816
+ `:nearest`, which pick an element and work. A `p` that lands exactly on an
817
+ element (`percentile(50)` of five) still answers, as does an odd-length
818
+ `median`. Numbers stored as objects -- Integer, Rational, BigDecimal -- are
819
+ unaffected.
820
+
821
+ - Fix: `sort_copy` takes whatever `sort` takes. It refused everything its own
822
+ fast path could not handle, so an object or boolean array sorted through
823
+ `sort` and raised `CArray::DataTypeError` through `sort_copy`; complex, which
824
+ neither can order, refused differently depending on which one was asked, and
825
+ now refuses alike. Numeric arrays keep the fast path and are unchanged.
826
+
827
+ - Fix: `CAConstString#sort_addr`, `#sort_index`, `#rank_index`, `#order`,
828
+ `#min_index`, `#max_index`, `#partition_copy` and `#partition_index` read the
829
+ strings. They read the `(start, end)` offsets that hold them, which order by
830
+ how the column was packed, so they gave well-formed wrong answers rather than
831
+ raising: `sort_addr` on an unsorted column gave the identity, and
832
+ `partition_copy` gave NUL bytes. `#minmax` answers instead of raising, and
833
+ sorting a column with masked cells no longer raises.
834
+
835
+ - Fix: `to_const_string` gives an N-D source back with its shape instead of
836
+ flattened, and `CAConstString#unique` / `#mode` / `#mask_duplicates` /
837
+ `#intersection` / `#difference` / `#union` keep the column's encoding --
838
+ on a column that was not UTF-8 they raised out of the builder's check.
839
+
840
+ - Fix: `each_with_index` and `map_with_index!` no longer raise
841
+ `SystemStackError` on a long array, and neither do `CArray#format` /
842
+ `CArray.format`, which are built on them. The ceiling was the C stack, so
843
+ where it fell depended on where the code ran: around a million cells on the
844
+ main thread, under a hundred thousand inside a `Thread`.
845
+
846
+ - Fix: `CArray.concatenate` and `CArray.mosaic` take a zero-length piece --
847
+ an empty slice such as `a[0...0]`, or `CArray.int32(0)` -- instead of
848
+ raising `IndexError`. The piece contributes nothing and the remaining ones
849
+ concatenate as before. `CArray#paste` likewise accepts a source covering no
850
+ cell, and writes nothing.
851
+
852
+ - Fix: C extensions only. A kernel writing into a view the caller supplied now
853
+ reaches the array; writes were lost, or crashed, for several view kinds
854
+ iterated along an axis whose fiber is not contiguous. Kernels writing into
855
+ an array they allocated themselves were never affected.
856
+
857
+ - Fix: `CArray#each_slab` yields a read-only slab, and writing through it
858
+ raises rather than reaching the array on one axis and being dropped on
859
+ another. Return values from the block instead, or assign through the array.
860
+
37
861
  ## 3.0.1
38
862
 
39
863
  - New: `CArray.jit_for`, `CArray.jit_each` and `CArray.jit_map` name a block