carray 3.0.2 → 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 (218) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +304 -0
  3. data/README.md +3 -4
  4. data/carray.gemspec +1 -1
  5. data/ext/ca_axis_dispatch.c +225 -0
  6. data/ext/ca_axis_group.c +177 -99
  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 +66 -71
  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 +663 -214
  14. data/ext/ca_kernel_iterator.h +231 -167
  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 +132 -22
  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 +1 -0
  23. data/ext/ca_obj_byte_swap.c +40 -40
  24. data/ext/ca_obj_const_string.c +96 -25
  25. data/ext/ca_obj_face.c +406 -64
  26. data/ext/ca_obj_face.h +92 -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 +21 -21
  31. data/ext/ca_obj_grid.c +181 -124
  32. data/ext/ca_obj_meld.c +189 -107
  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 +543 -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 +47 -49
  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 -118
  46. data/ext/ca_obj_stride.c +195 -34
  47. data/ext/ca_obj_string.c +17 -18
  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 +149 -27
  54. data/ext/ca_op_ipower.c +24 -11
  55. data/ext/ca_op_powi.h +57 -30
  56. data/ext/ca_pow5_table.h +656 -0
  57. data/ext/ca_sort_kernels.h +70 -0
  58. data/ext/ca_sweep_engine.c +102 -79
  59. data/ext/ca_sweep_engine.h +7 -20
  60. data/ext/ca_triop_dispatch.c +4 -1
  61. data/ext/ca_triop_dispatch.h +5 -2
  62. data/ext/caframe_csv_reader.c +463 -0
  63. data/ext/carray.h +70 -27
  64. data/ext/carray_access.c +455 -211
  65. data/ext/carray_address_basis.c +177 -36
  66. data/ext/carray_attribute.c +45 -3
  67. data/ext/carray_bincount.c +32 -2
  68. data/ext/carray_broadcast.c +46 -14
  69. data/ext/carray_call_cfunc.c +0 -28
  70. data/ext/carray_cast.c +1444 -209
  71. data/ext/carray_cast_func.rb +34 -21
  72. data/ext/carray_conversion.c +197 -73
  73. data/ext/carray_copy.c +32 -11
  74. data/ext/carray_core.c +514 -122
  75. data/ext/carray_count.c +140 -87
  76. data/ext/carray_element.c +18 -27
  77. data/ext/carray_factorize.c +569 -277
  78. data/ext/carray_generate.c +76 -40
  79. data/ext/carray_histogram.c +152 -94
  80. data/ext/carray_hold.c +2 -1
  81. data/ext/carray_index_classifier.c +63 -96
  82. data/ext/carray_internal.h +385 -3
  83. data/ext/carray_lazy.c +834 -79
  84. data/ext/carray_loop.c +81 -65
  85. data/ext/carray_mask.c +682 -316
  86. data/ext/carray_math_kernel.h +1 -0
  87. data/ext/carray_median_percentile.c +221 -76
  88. data/ext/carray_memory_view.c +254 -248
  89. data/ext/carray_operator.c +1089 -359
  90. data/ext/carray_order.c +133 -69
  91. data/ext/carray_partition.c +102 -56
  92. data/ext/carray_random.c +273 -239
  93. data/ext/carray_scatter.c +308 -65
  94. data/ext/carray_segment.c +257 -0
  95. data/ext/carray_slab.c +170 -70
  96. data/ext/carray_sort.c +215 -218
  97. data/ext/carray_sort_kernel.c +8 -1
  98. data/ext/carray_struct.c +99 -21
  99. data/ext/carray_test.c +167 -64
  100. data/ext/carray_time_text.c +1182 -0
  101. data/ext/carray_utils.c +363 -98
  102. data/ext/extconf.rb +27 -2
  103. data/ext/mk_call_cfunc.rb +0 -2
  104. data/ext/mk_pow5_table.rb +35 -0
  105. data/ext/mkkernel.rb +1076 -846
  106. data/ext/ruby_carray.c +7 -1
  107. data/ext/version.h +4 -4
  108. data/ext/version.rb +4 -2
  109. data/lib/carray/attribute.rb +39 -0
  110. data/lib/carray/autoload_carray.rb +19 -0
  111. data/lib/carray/autoload_method_extension.rb +6 -0
  112. data/lib/carray/axis_group.rb +174 -26
  113. data/lib/carray/basics.rb +91 -25
  114. data/lib/carray/bincount_nd.rb +6 -0
  115. data/lib/carray/block_iterator.rb +118 -48
  116. data/lib/carray/boolean_reduce.rb +21 -29
  117. data/lib/carray/categorical.rb +94 -26
  118. data/lib/carray/categorical_iterator.rb +333 -826
  119. data/lib/carray/complex.rb +36 -29
  120. data/lib/carray/conditional.rb +94 -7
  121. data/lib/carray/const_string.rb +46 -0
  122. data/lib/carray/frame/concat.rb +157 -6
  123. data/lib/carray/frame/convert.rb +13 -7
  124. data/lib/carray/frame/csv_parser.rb +589 -82
  125. data/lib/carray/frame/describe.rb +79 -0
  126. data/lib/carray/frame/frame.rb +124 -25
  127. data/lib/carray/frame/group.rb +189 -63
  128. data/lib/carray/frame/io.rb +412 -72
  129. data/lib/carray/frame/join.rb +134 -46
  130. data/lib/carray/frame/records.rb +37 -16
  131. data/lib/carray/frame/reshape.rb +448 -0
  132. data/lib/carray/frame/sort.rb +26 -2
  133. data/lib/carray/frame/verbs.rb +715 -48
  134. data/lib/carray/frame.rb +2 -0
  135. data/lib/carray/fuse_source.rb +111 -2
  136. data/lib/carray/fusion.rb +229 -38
  137. data/lib/carray/inspect.rb +29 -11
  138. data/lib/carray/iterator.rb +14 -2
  139. data/lib/carray/lazy.rb +54 -30
  140. data/lib/carray/mask_gap_fill.rb +66 -52
  141. data/lib/carray/math.rb +32 -19
  142. data/lib/carray/meld_reduce.rb +40 -7
  143. data/lib/carray/methods/align_addr.rb +8 -4
  144. data/lib/carray/methods/bincount.rb +2 -2
  145. data/lib/carray/methods/bit_string.rb +14 -3
  146. data/lib/carray/methods/composition.rb +16 -10
  147. data/lib/carray/methods/factorize.rb +2 -2
  148. data/lib/carray/methods/gather_nd.rb +13 -2
  149. data/lib/carray/methods/insert_block.rb +9 -14
  150. data/lib/carray/methods/is_in.rb +117 -4
  151. data/lib/carray/methods/join.rb +1 -1
  152. data/lib/carray/methods/locate_addr.rb +177 -36
  153. data/lib/carray/methods/mode.rb +18 -1
  154. data/lib/carray/methods/repeat.rb +1 -20
  155. data/lib/carray/methods/resize.rb +21 -23
  156. data/lib/carray/methods/snap.rb +34 -12
  157. data/lib/carray/methods/unique.rb +11 -2
  158. data/lib/carray/record_order.rb +208 -0
  159. data/lib/carray/runtime.rb +14 -11
  160. data/lib/carray/segment_iterator.rb +990 -0
  161. data/lib/carray/serialize.rb +197 -27
  162. data/lib/carray/slab_iterator.rb +51 -27
  163. data/lib/carray/stack.rb +10 -4
  164. data/lib/carray/string_operation_extension.rb +4 -3
  165. data/lib/carray/struct.rb +142 -51
  166. data/lib/carray/struct_builder.rb +110 -14
  167. data/lib/carray/table.rb +2 -2
  168. data/lib/carray/time.rb +517 -103
  169. data/lib/carray/window_iterator.rb +127 -72
  170. data/lib/carray.rb +2 -2
  171. data/yard-stubs/ca_obj_array.rb +4 -4
  172. data/yard-stubs/ca_obj_bitarray.rb +7 -0
  173. data/yard-stubs/ca_obj_bitfield.rb +23 -12
  174. data/yard-stubs/ca_obj_fake.rb +1 -1
  175. data/yard-stubs/ca_obj_field.rb +1 -1
  176. data/yard-stubs/ca_obj_refer.rb +3 -3
  177. data/yard-stubs/ca_obj_roll.rb +0 -1
  178. data/yard-stubs/ca_obj_stride.rb +10 -7
  179. data/yard-stubs/ca_obj_transpose.rb +1 -1
  180. data/yard-stubs/ca_obj_window.rb +1 -2
  181. data/yard-stubs/carray_access.rb +5 -4
  182. data/yard-stubs/carray_broadcast.rb +1 -1
  183. data/yard-stubs/carray_cast.rb +78 -43
  184. data/yard-stubs/carray_conversion.rb +13 -1
  185. data/yard-stubs/carray_copy.rb +8 -3
  186. data/yard-stubs/carray_core.rb +3 -3
  187. data/yard-stubs/carray_count.rb +9 -0
  188. data/yard-stubs/carray_lazy.rb +5 -1
  189. data/yard-stubs/carray_loop.rb +4 -1
  190. data/yard-stubs/carray_mask.rb +34 -10
  191. data/yard-stubs/carray_math.rb +25 -13
  192. data/yard-stubs/carray_median_percentile.rb +10 -6
  193. data/yard-stubs/carray_order.rb +34 -13
  194. data/yard-stubs/carray_scatter.rb +10 -3
  195. data/yard-stubs/carray_segment.rb +72 -0
  196. data/yard-stubs/carray_slab.rb +2 -1
  197. data/yard-stubs/carray_sort.rb +43 -3
  198. data/yard-stubs/carray_test.rb +10 -0
  199. data/yard-stubs/carray_utils.rb +0 -26
  200. metadata +11 -19
  201. data/ext/ca_for_each_element.h +0 -239
  202. data/ext/carray_build_flags.h +0 -3
  203. data/ext/carray_config.h +0 -28
  204. data/ext/carray_kernels_bincmp.c +0 -4446
  205. data/ext/carray_kernels_binop.c +0 -11001
  206. data/ext/carray_kernels_init.c +0 -1131
  207. data/ext/carray_kernels_map.c +0 -3467
  208. data/ext/carray_kernels_moncmp.c +0 -2097
  209. data/ext/carray_kernels_monop.c +0 -18313
  210. data/ext/carray_kernels_reduce_aggregate.c +0 -26005
  211. data/ext/carray_kernels_reduce_boolean.c +0 -330
  212. data/ext/carray_kernels_reduce_cumulative.c +0 -14862
  213. data/ext/carray_kernels_reduce_extreme.c +0 -17494
  214. data/ext/carray_kernels_reduce_variance.c +0 -3910
  215. data/ext/carray_kernels_scan.c +0 -3693
  216. data/ext/carray_kernels_search.c +0 -32218
  217. data/ext/carray_kernels_sort.c +0 -10626
  218. data/ext/carray_kernels_triop.c +0 -1392
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 728236f069adb2a801da715596ac5ab87d8cf7c460e70de8667d3afa23e9b19f
4
- data.tar.gz: fdf25919377fe2d90919712ecf8c50a0ed183c15a57ec925cefedc4a7066fb90
3
+ metadata.gz: 76ca6c2983cbd1e07acc64daeedc7328bee9239369f56f3af661ed1cc41e0bfb
4
+ data.tar.gz: 1506fa95c68148469451631fb375826e2599a979b4593db8c7508cf16d3c7545
5
5
  SHA512:
6
- metadata.gz: 26f8ae296774c82b5bba4f9521cf2b53ec0b1804115c0a4a2f39e29d67419932487a4ff892cd115ab3eb34242c942711504c05945b5e95d44653e546e7c70d66
7
- data.tar.gz: e8109043b73d2f71d395ee0cc5711e395f6964ba0cef7c2aea2d35759dadedca5aa649ec3ebe14de66b6034a788213fb0c0fa1a1ef2499eef03d8b0b021d4bc4
6
+ metadata.gz: 66a3078a1bdd6459ab0c7526a588cc81191a4e043a17028b769b87b5d32874a9fd20c2b4828217dfa573457871f476155d005db518da600bbef38baca5551eb8
7
+ data.tar.gz: 23479b0a9b229489ff5ed1afa7fdc0a30150b9d3ba32803b089f1f9e5d7a955cd556c478c7cb5346e516830632a4ba35cb54d186f54a783b589d7cabec9498d5
data/CHANGELOG.md CHANGED
@@ -34,6 +34,310 @@ 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
+
37
341
  ## 3.0.2
38
342
 
39
343
  - New: `CArray::AddressBasis`, for a C extension whose code addresses cells
data/README.md CHANGED
@@ -4,17 +4,16 @@ Ruby/CArray is an extension library for the multi-dimensional array class. It pr
4
4
 
5
5
  ## Status
6
6
 
7
- 3.0.x still moves: behavior can change between releases — see [CHANGELOG.md](CHANGELOG.md). 3.1 is the first release meant to be depended on. Until then, treat it as a place to try things out.
7
+ 3.0.x still moves: behavior can change between releases. 3.1 is the first release meant to be depended on. Until then, treat it as a place to try things out.
8
8
 
9
9
  ## Features
10
10
 
11
11
  * Every array carries a per-element mask for missing values, respected by reductions and statistics
12
12
  * Views compose without copying — a write through the outermost view reaches the source data
13
13
  * MemoryView protocol on both sides: share buffers with other numerical libraries without copying
14
- * Kernel-style iteration: run a Ruby block over each sub-array spanning the axes you choose
15
- * Attach domain meaning (time, angle, quantity with units…) without changing storage — a Face
16
14
  * Define your own array class in pure Ruby while keeping the full CArray interface
17
- * Pack multiple values into one element as a record type
15
+ * Kernel-style iteration: run a Ruby block over each sub-array spanning the axes you choose
16
+ * Faces: array types for time, strings and categories, with methods of their own on top of everything CArray does
18
17
  * Comes with a DataFrame (`CAFrame`) whose columns are plain CArrays, so masks and views keep working on them
19
18
 
20
19
  Each of these is shown, briefly and with a runnable example, in [A tour of the features](docs/FeatureTour.md).
data/carray.gemspec CHANGED
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "carray"
3
- s.version = "3.0.2"
3
+ s.version = "3.0.3"
4
4
  s.authors = ["himotoyoshi"]
5
5
  s.email = ["himotoyoshi@users.noreply.github.com"]
6
6
  s.summary = "Multi-dimensional numerical array class for Ruby"
@@ -42,6 +42,7 @@
42
42
  #include "carray.h"
43
43
  #include "ca_iter_substrate.h"
44
44
  #include "ca_composite_dispatch.h"
45
+ #include "carray_internal.h" /* ca_parent_lends_no_memory */
45
46
 
46
47
  #include <string.h>
47
48
 
@@ -393,6 +394,100 @@ ca_axis_dispatch_fill_value_cb (ca_size_t off, int oob, ca_size_t n, void *vctx)
393
394
  }
394
395
  }
395
396
 
397
+ /* Rows whose innermost axis is a step-1 SHIFT axis. View cell i of the row
398
+ reads parent cell start + i, so the cells with 0 <= start + i < size0 form
399
+ one run and go to cb together. The cells before and after it are out of
400
+ range: under FILL / MASK every one of them is (cb gets each side as one
401
+ oob slab), under the other policies each maps to a cell of its own. */
402
+ static void
403
+ ca_axis_dispatch_shift_cell (const ca_axis_desc_t *ax, ca_size_t pstride,
404
+ ca_size_t row_off, ca_size_t i, ca_size_t bytes,
405
+ ca_slab_cb_t cb, void *ctx)
406
+ {
407
+ ca_size_t pi = ca_bounds_normalize_index(ax->policy, ax->size0,
408
+ ax->start + i);
409
+ if ( pi < 0 || pi >= ax->size0 ) {
410
+ cb(row_off, 1, bytes, ctx);
411
+ }
412
+ else {
413
+ cb(row_off + pi * pstride, 0, bytes, ctx);
414
+ }
415
+ }
416
+
417
+ static void
418
+ ca_axis_dispatch_for_each_shift_row (ca_axis_desc_t *laxes,
419
+ const ca_size_t *pstrides,
420
+ int8_t lndim,
421
+ ca_size_t bytes,
422
+ ca_size_t total_elements,
423
+ ca_slab_cb_t cb,
424
+ void *ctx)
425
+ {
426
+ int8_t inner = lndim - 1;
427
+ const ca_axis_desc_t *ax = &laxes[inner];
428
+ ca_size_t pstride = pstrides[inner];
429
+ ca_size_t count = ax->count;
430
+ int coalesce = ( ax->policy == CA_BOUNDS_FILL
431
+ || ax->policy == CA_BOUNDS_MASK );
432
+ ca_op_prefix_axis_t prefix[CA_RANK_MAX];
433
+ ca_size_t idx[CA_RANK_MAX];
434
+ ca_size_t lo, hi, n_rows, r, i;
435
+ int8_t k;
436
+
437
+ /* In-range view cells: lo <= i < hi. */
438
+ lo = -ax->start;
439
+ if ( lo < 0 ) lo = 0;
440
+ if ( lo > count ) lo = count;
441
+ hi = ax->size0 - ax->start;
442
+ if ( hi > count ) hi = count;
443
+ if ( hi < lo ) hi = lo;
444
+
445
+ ca_axis_dispatch_classify_prefix(laxes, pstrides, inner, prefix);
446
+ for ( k = 0; k < inner; k++ ) idx[k] = 0;
447
+
448
+ n_rows = total_elements / count;
449
+ for ( r = 0; r < n_rows; r++ ) {
450
+ int oob;
451
+ ca_size_t row_off = ca_axis_dispatch_prefix_offset(prefix, idx, inner,
452
+ &oob);
453
+ if ( oob ) {
454
+ cb(row_off, 1, count * bytes, ctx);
455
+ }
456
+ else {
457
+ if ( lo > 0 ) {
458
+ if ( coalesce ) {
459
+ cb(row_off, 1, lo * bytes, ctx);
460
+ }
461
+ else {
462
+ for ( i = 0; i < lo; i++ ) {
463
+ ca_axis_dispatch_shift_cell(ax, pstride, row_off, i, bytes,
464
+ cb, ctx);
465
+ }
466
+ }
467
+ }
468
+ if ( hi > lo ) {
469
+ cb(row_off + (ax->start + lo) * pstride, 0, (hi - lo) * bytes, ctx);
470
+ }
471
+ if ( hi < count ) {
472
+ if ( coalesce ) {
473
+ cb(row_off, 1, (count - hi) * bytes, ctx);
474
+ }
475
+ else {
476
+ for ( i = hi; i < count; i++ ) {
477
+ ca_axis_dispatch_shift_cell(ax, pstride, row_off, i, bytes,
478
+ cb, ctx);
479
+ }
480
+ }
481
+ }
482
+ }
483
+
484
+ for ( k = inner - 1; k >= 0; k-- ) {
485
+ if ( ++idx[k] < prefix[k].count ) break;
486
+ idx[k] = 0;
487
+ }
488
+ }
489
+ }
490
+
396
491
  /* Generic per-slab driver. Walks the (post-merge / post-layout)
397
492
  prefix axes row-major and invokes `cb` once per slab iteration.
398
493
  The all-slab case degenerates to a single `cb` invocation with
@@ -437,6 +532,17 @@ ca_axis_dispatch_for_each_slab (CArray *parent,
437
532
  return;
438
533
  }
439
534
 
535
+ /* Innermost SHIFT axis with step 1: the in-range part of each row is one
536
+ contiguous run of parent cells, so it goes to cb as one slab instead of
537
+ one cell at a time. */
538
+ if ( slab_start == lndim
539
+ && laxes[lndim - 1].kind == CA_AXIS_KIND_SHIFT
540
+ && laxes[lndim - 1].step == 1 ) {
541
+ ca_axis_dispatch_for_each_shift_row(laxes, pstrides, lndim, bytes,
542
+ total_elements, cb, ctx);
543
+ return;
544
+ }
545
+
440
546
  /* Pre-classify prefix axes once. Inner loop becomes branchless
441
547
  modulo a per-axis kind tag whose value is constant across
442
548
  iters. */
@@ -951,3 +1057,122 @@ ca_axis_dispatch_outer_has_shift (const ca_axis_desc_t *descs,
951
1057
  }
952
1058
  return 0;
953
1059
  }
1060
+
1061
+ /* ==========================================================================
1062
+ Transfers of a descriptor view (CAGrid, CASelect, CASelectAxis, CAWindow)
1063
+ whose parent holds no ptr.
1064
+
1065
+ The engine above walks parent->ptr. A cold parent is handled by what it
1066
+ is:
1067
+
1068
+ - one ca_attach_is_alias accepts already has the memory: borrow it;
1069
+ - one with no memory to lend (ca_parent_lends_no_memory) is asked for
1070
+ the selected cells only, through the view's own xfer_stride over the
1071
+ whole view -- producing every cell of the parent to serve a selection
1072
+ would make the cost follow the parent's size;
1073
+ - anything else is gathered into a parent-shaped scratch that stands in
1074
+ for parent->ptr, and pushed back for PUT.
1075
+ ========================================================================== */
1076
+
1077
+ /* The whole view as one region request to its own xfer_stride slot. */
1078
+ static void
1079
+ ca_axis_view_xfer_region (CArray *ca, void *data, int dir)
1080
+ {
1081
+ ca_size_t starts[CA_RANK_MAX], strides[CA_RANK_MAX], s;
1082
+ int8_t k;
1083
+
1084
+ /* The slot is called directly, past ca_xfer_stride's empty-region check. */
1085
+ if ( ca->elements == 0 ) return;
1086
+
1087
+ s = ca->bytes;
1088
+ for ( k = ca->ndim - 1; k >= 0; k-- ) {
1089
+ starts[k] = 0;
1090
+ strides[k] = s;
1091
+ s *= ca->dim[k];
1092
+ }
1093
+ ca_func[ca->obj_type].xfer_stride(ca, starts, ca->dim, strides, data, dir);
1094
+ }
1095
+
1096
+ void
1097
+ ca_axis_view_xfer_all (void *ap, ca_axis_view_fast_t fast, int region_ok,
1098
+ void *data, int dir)
1099
+ {
1100
+ CArray *ca = (CArray *) ap;
1101
+ CArray *parent = ((CAView *) ca)->parent;
1102
+
1103
+ if ( parent->ptr ) {
1104
+ fast(ca, (char *) data, dir);
1105
+ return;
1106
+ }
1107
+
1108
+ if ( ca_attach_is_alias(parent) ) {
1109
+ ca_attach(parent); /* window: an alias attach, cannot raise */
1110
+ fast(ca, (char *) data, dir);
1111
+ ca_detach(parent);
1112
+ return;
1113
+ }
1114
+
1115
+ if ( region_ok && ca_parent_lends_no_memory(parent) ) {
1116
+ ca_axis_view_xfer_region(ca, data, dir);
1117
+ return;
1118
+ }
1119
+
1120
+ {
1121
+ volatile VALUE holder;
1122
+ ca_size_t plen = parent->elements * parent->bytes;
1123
+ char *parent_scratch = ALLOCV_N(char, holder, plen);
1124
+ ca_xfer_all(parent, parent_scratch, CA_XFER_GET);
1125
+ parent->ptr = parent_scratch;
1126
+ fast(ca, (char *) data, dir);
1127
+ /* Put parent->ptr back before the PUT: the PUT can raise (a lazy
1128
+ backing's sync failing), and the scratch is gone once this frame
1129
+ is. */
1130
+ parent->ptr = NULL;
1131
+ if ( dir == CA_XFER_PUT ) {
1132
+ ca_xfer_all(parent, parent_scratch, CA_XFER_PUT);
1133
+ }
1134
+ ALLOCV_END(holder);
1135
+ }
1136
+ }
1137
+
1138
+ /* The attach of a view that owns its buffer and fills it through its own
1139
+ transfers (a descriptor view whose parent has no memory to lend, CARemap,
1140
+ CAStack, CAMeld). The buffer is published only once filled -- a view
1141
+ with a live ptr makes the per-cell dispatchers bypass the transfer slots,
1142
+ and for CA_OBJECT the garbage cells of an unfilled buffer would be marked
1143
+ -- and a raise while filling frees it. The fill goes through ca_xfer_all,
1144
+ which holds the object cells against a collection while the transfers
1145
+ call Ruby. */
1146
+
1147
+ static VALUE
1148
+ ca_axis_view_fill_owned (VALUE arg)
1149
+ {
1150
+ void **args = (void **) arg;
1151
+ ca_xfer_all((CArray *) args[0], args[1], CA_XFER_GET);
1152
+ return Qnil;
1153
+ }
1154
+
1155
+ void
1156
+ ca_axis_view_attach_owned (void *ap)
1157
+ {
1158
+ CArray *ca = (CArray *) ap;
1159
+ char *buf = xmalloc(ca_length(ca) > 0 ? ca_length(ca) : 1);
1160
+ void *args[2];
1161
+ int tag = 0;
1162
+
1163
+ args[0] = ca;
1164
+ args[1] = buf;
1165
+ rb_protect(ca_axis_view_fill_owned, (VALUE) args, &tag);
1166
+ if ( tag ) {
1167
+ xfree(buf);
1168
+ rb_jump_tag(tag);
1169
+ }
1170
+ ca->ptr = buf;
1171
+ }
1172
+
1173
+ void
1174
+ ca_axis_view_sync_owned (void *ap)
1175
+ {
1176
+ CArray *ca = (CArray *) ap;
1177
+ ca_func[ca->obj_type].xfer_all(ca, ca->ptr, CA_XFER_PUT);
1178
+ }