carray 3.0.2 → 3.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +568 -0
- data/README.md +6 -6
- data/carray.gemspec +1 -1
- data/ext/ca_axis_descriptor.h +27 -24
- data/ext/ca_axis_dispatch.c +220 -0
- data/ext/ca_axis_group.c +201 -123
- data/ext/ca_binop_dispatch.c +46 -52
- data/ext/ca_binop_dispatch.h +1 -1
- data/ext/ca_categorical_iterator.c +249 -183
- data/ext/ca_compare.c +7 -12
- data/ext/ca_for_buffer.h +21 -68
- data/ext/ca_group_iter.c +38 -23
- data/ext/ca_kernel_iterator.c +1765 -2076
- data/ext/ca_kernel_iterator.h +584 -566
- data/ext/ca_monop_dispatch.c +86 -63
- data/ext/ca_monop_dispatch.h +5 -0
- data/ext/ca_obj_array.c +223 -56
- data/ext/ca_obj_bincmp.c +172 -138
- data/ext/ca_obj_binop.c +116 -230
- data/ext/ca_obj_bitarray.c +40 -26
- data/ext/ca_obj_bitfield.c +100 -68
- data/ext/ca_obj_block.c +19 -9
- data/ext/ca_obj_byte_swap.c +45 -46
- data/ext/ca_obj_const_string.c +269 -44
- data/ext/ca_obj_face.c +612 -89
- data/ext/ca_obj_face.h +111 -47
- data/ext/ca_obj_fake.c +36 -79
- data/ext/ca_obj_farray.c +7 -3
- data/ext/ca_obj_field.c +33 -8
- data/ext/ca_obj_fixlen_string.c +23 -24
- data/ext/ca_obj_grid.c +541 -210
- data/ext/ca_obj_meld.c +331 -111
- data/ext/ca_obj_moncmp.c +80 -65
- data/ext/ca_obj_monop.c +177 -149
- data/ext/ca_obj_object.c +664 -248
- data/ext/ca_obj_record.c +63 -23
- data/ext/ca_obj_reduce.c +18 -16
- data/ext/ca_obj_refer.c +140 -58
- data/ext/ca_obj_remap.c +85 -24
- data/ext/ca_obj_repeat.c +24 -9
- data/ext/ca_obj_roll.c +121 -16
- data/ext/ca_obj_select.c +61 -58
- data/ext/ca_obj_select_axis.c +73 -107
- data/ext/ca_obj_shift.c +32 -102
- data/ext/ca_obj_stack.c +372 -122
- data/ext/ca_obj_stride.c +348 -140
- data/ext/ca_obj_string.c +88 -22
- data/ext/ca_obj_tile.c +38 -21
- data/ext/ca_obj_time.c +45 -31
- data/ext/ca_obj_timedelta.c +43 -29
- data/ext/ca_obj_transpose.c +17 -6
- data/ext/ca_obj_triop.c +125 -141
- data/ext/ca_obj_window.c +330 -44
- data/ext/ca_op_ipower.c +24 -11
- data/ext/ca_op_powi.h +57 -30
- data/ext/ca_pow5_table.h +656 -0
- data/ext/ca_sort_kernels.h +70 -0
- data/ext/ca_sweep_engine.c +104 -81
- data/ext/ca_sweep_engine.h +7 -20
- data/ext/ca_triop_dispatch.c +4 -1
- data/ext/ca_triop_dispatch.h +5 -2
- data/ext/caframe_csv_reader.c +542 -0
- data/ext/carray.h +241 -92
- data/ext/carray_access.c +608 -276
- data/ext/carray_address_basis.c +494 -97
- data/ext/carray_attribute.c +129 -4
- data/ext/carray_bincount.c +39 -7
- data/ext/carray_broadcast.c +56 -19
- data/ext/carray_build_flags.h +1 -1
- data/ext/carray_call_cfunc.c +108 -28
- data/ext/carray_cast.c +1576 -223
- data/ext/carray_cast_func.rb +77 -24
- data/ext/carray_class.c +4 -3
- data/ext/carray_conversion.c +208 -75
- data/ext/carray_copy.c +82 -24
- data/ext/carray_core.c +646 -162
- data/ext/carray_count.c +171 -88
- data/ext/carray_element.c +94 -60
- data/ext/carray_factorize.c +670 -324
- data/ext/carray_generate.c +260 -83
- data/ext/carray_histogram.c +209 -106
- data/ext/carray_hold.c +14 -16
- data/ext/carray_index_classifier.c +422 -243
- data/ext/carray_internal.h +692 -3
- data/ext/carray_kernels_bincmp.c +7 -0
- data/ext/carray_kernels_binop.c +487 -120
- data/ext/carray_kernels_init.c +653 -50
- data/ext/carray_kernels_moncmp.c +15 -8
- data/ext/carray_kernels_monop.c +99 -43
- data/ext/carray_kernels_reduce_aggregate.c +3925 -6112
- data/ext/carray_kernels_reduce_boolean.c +112 -36
- data/ext/carray_kernels_reduce_cumulative.c +2127 -6903
- data/ext/carray_kernels_reduce_extreme.c +3032 -2892
- data/ext/carray_kernels_reduce_variance.c +439 -500
- data/ext/carray_kernels_scan.c +1097 -488
- data/ext/carray_kernels_search.c +3787 -2176
- data/ext/carray_kernels_sort.c +1200 -920
- data/ext/carray_kernels_triop.c +479 -7
- data/ext/carray_key_windows.c +216 -0
- data/ext/carray_lazy.c +967 -153
- data/ext/carray_loop.c +81 -65
- data/ext/carray_mask.c +910 -324
- data/ext/carray_math_kernel.h +1 -0
- data/ext/carray_median_percentile.c +299 -100
- data/ext/carray_memory_view.c +872 -758
- data/ext/carray_operator.c +1171 -364
- data/ext/carray_order.c +505 -133
- data/ext/carray_partition.c +112 -65
- data/ext/carray_random.c +327 -239
- data/ext/carray_round.c +273 -0
- data/ext/carray_scatter.c +346 -78
- data/ext/carray_segment.c +257 -0
- data/ext/carray_slab.c +249 -86
- data/ext/carray_sort.c +224 -227
- data/ext/carray_sort_kernel.c +8 -1
- data/ext/carray_struct.c +118 -39
- data/ext/carray_test.c +193 -69
- data/ext/carray_time_text.c +1182 -0
- data/ext/carray_undef.c +4 -0
- data/ext/carray_utils.c +579 -113
- data/ext/extconf.rb +27 -2
- data/ext/mk_call_cfunc.rb +19 -4
- data/ext/mk_pow5_table.rb +35 -0
- data/ext/mkkernel.rb +1202 -1014
- data/ext/ruby_carray.c +17 -5
- data/ext/version.h +4 -4
- data/ext/version.rb +4 -2
- data/lib/carray/attribute.rb +160 -17
- data/lib/carray/autoload_carray.rb +23 -27
- data/lib/carray/autoload_method_extension.rb +20 -5
- data/lib/carray/axis_group.rb +360 -63
- data/lib/carray/basics.rb +162 -28
- data/lib/carray/bincount_nd.rb +18 -2
- data/lib/carray/block_iterator.rb +231 -58
- data/lib/carray/boolean_reduce.rb +34 -42
- data/lib/carray/categorical.rb +242 -52
- data/lib/carray/categorical_iterator.rb +437 -827
- data/lib/carray/complex.rb +41 -34
- data/lib/carray/conditional.rb +150 -14
- data/lib/carray/const_string.rb +59 -4
- data/lib/carray/construct.rb +15 -11
- data/lib/carray/data_type_extension.rb +1 -0
- data/lib/carray/distance_limit.rb +77 -0
- data/lib/carray/fixlen_string.rb +7 -1
- data/lib/carray/frame/column_verbs.rb +70 -0
- data/lib/carray/frame/concat.rb +157 -6
- data/lib/carray/frame/convert.rb +82 -7
- data/lib/carray/frame/csv_parser.rb +688 -87
- data/lib/carray/frame/describe.rb +80 -0
- data/lib/carray/frame/frame.rb +369 -45
- data/lib/carray/frame/group.rb +631 -90
- data/lib/carray/frame/io.rb +506 -79
- data/lib/carray/frame/join.rb +411 -62
- data/lib/carray/frame/records.rb +116 -19
- data/lib/carray/frame/reshape.rb +455 -0
- data/lib/carray/frame/sort.rb +30 -18
- data/lib/carray/frame/verbs.rb +868 -61
- data/lib/carray/frame/windows.rb +117 -0
- data/lib/carray/frame.rb +4 -0
- data/lib/carray/fuse_source.rb +118 -2
- data/lib/carray/fusion.rb +257 -42
- data/lib/carray/histogram.rb +6 -1
- data/lib/carray/inspect.rb +54 -13
- data/lib/carray/iterator.rb +99 -9
- data/lib/carray/key_windows.rb +620 -0
- data/lib/carray/lazy.rb +216 -193
- data/lib/carray/mask_gap_fill.rb +112 -92
- data/lib/carray/math.rb +32 -19
- data/lib/carray/meld_reduce.rb +52 -21
- data/lib/carray/methods/align_addr.rb +8 -4
- data/lib/carray/methods/bin.rb +26 -18
- data/lib/carray/methods/bincount.rb +18 -2
- data/lib/carray/methods/bit_string.rb +15 -4
- data/lib/carray/methods/choose.rb +16 -3
- data/lib/carray/methods/composition.rb +18 -10
- data/lib/carray/methods/factorize.rb +2 -2
- data/lib/carray/methods/gather_nd.rb +25 -5
- data/lib/carray/methods/insert_block.rb +9 -14
- data/lib/carray/methods/is_between.rb +59 -0
- data/lib/carray/methods/is_in.rb +140 -10
- data/lib/carray/methods/join.rb +2 -7
- data/lib/carray/methods/lag.rb +49 -0
- data/lib/carray/methods/locate_addr.rb +182 -45
- data/lib/carray/methods/mode.rb +18 -1
- data/lib/carray/methods/nunique.rb +11 -6
- data/lib/carray/methods/repeat.rb +1 -20
- data/lib/carray/methods/resize.rb +21 -23
- data/lib/carray/methods/snap.rb +34 -12
- data/lib/carray/methods/unique.rb +11 -2
- data/lib/carray/methods/value_counts.rb +4 -11
- data/lib/carray/record_order.rb +208 -0
- data/lib/carray/rng.rb +3 -0
- data/lib/carray/runtime.rb +65 -17
- data/lib/carray/segment_iterator.rb +1221 -0
- data/lib/carray/serialize.rb +214 -30
- data/lib/carray/slab_iterator.rb +101 -27
- data/lib/carray/stack.rb +10 -4
- data/lib/carray/string.rb +105 -5
- data/lib/carray/string_operation_extension.rb +12 -4
- data/lib/carray/struct.rb +137 -58
- data/lib/carray/struct_builder.rb +124 -17
- data/lib/carray/time.rb +1222 -186
- data/lib/carray/window_iterator.rb +242 -81
- data/lib/carray.rb +9 -2
- data/yard-stubs/ca_obj_array.rb +4 -4
- data/yard-stubs/ca_obj_bitarray.rb +7 -0
- data/yard-stubs/ca_obj_bitfield.rb +23 -12
- data/yard-stubs/ca_obj_fake.rb +1 -1
- data/yard-stubs/ca_obj_field.rb +5 -1
- data/yard-stubs/ca_obj_grid.rb +21 -10
- data/yard-stubs/ca_obj_refer.rb +5 -4
- data/yard-stubs/ca_obj_roll.rb +2 -3
- data/yard-stubs/ca_obj_shift.rb +5 -1
- data/yard-stubs/ca_obj_stride.rb +10 -7
- data/yard-stubs/ca_obj_tile.rb +1 -1
- data/yard-stubs/ca_obj_transpose.rb +1 -1
- data/yard-stubs/ca_obj_window.rb +9 -5
- data/yard-stubs/carray_access.rb +11 -5
- data/yard-stubs/carray_attribute.rb +0 -5
- data/yard-stubs/carray_broadcast.rb +1 -1
- data/yard-stubs/carray_cast.rb +88 -43
- data/yard-stubs/carray_conversion.rb +15 -2
- data/yard-stubs/carray_copy.rb +12 -5
- data/yard-stubs/carray_core.rb +3 -3
- data/yard-stubs/carray_count.rb +9 -0
- data/yard-stubs/carray_element.rb +10 -4
- data/yard-stubs/carray_generate.rb +21 -1
- data/yard-stubs/carray_lazy.rb +5 -1
- data/yard-stubs/carray_loop.rb +4 -1
- data/yard-stubs/carray_mask.rb +60 -12
- data/yard-stubs/carray_math.rb +52 -14
- data/yard-stubs/carray_median_percentile.rb +15 -8
- data/yard-stubs/carray_memory_view.rb +40 -23
- data/yard-stubs/carray_order.rb +61 -29
- data/yard-stubs/carray_random.rb +13 -4
- data/yard-stubs/carray_scatter.rb +21 -9
- data/yard-stubs/carray_segment.rb +72 -0
- data/yard-stubs/carray_slab.rb +10 -2
- data/yard-stubs/carray_sort.rb +49 -8
- data/yard-stubs/carray_test.rb +15 -3
- data/yard-stubs/carray_utils.rb +0 -26
- metadata +19 -5
- data/ext/ca_for_each_element.h +0 -239
- data/ext/carray_kernels_map.c +0 -3467
- data/lib/carray/arrow_tensor.rb +0 -401
- data/lib/carray/table.rb +0 -108
|
@@ -71,9 +71,11 @@ class CAWindowIterator < CAIterator
|
|
|
71
71
|
# `:nearest` / `:truncate`). `fill_value:` is a constant margin value
|
|
72
72
|
# (an escape for `:constant` padding); when given it overrides `:skip`.
|
|
73
73
|
#
|
|
74
|
-
# For backward compatibility `initialize(window_view)`
|
|
75
|
-
# view (the old `CAWindowIterator.new(a.window(...))`
|
|
76
|
-
# (offset ranges, bounds, fill value) is read back
|
|
74
|
+
# For backward compatibility `initialize(window_view)` with no ranges
|
|
75
|
+
# accepts a CAWindow view (the old `CAWindowIterator.new(a.window(...))`
|
|
76
|
+
# form): the geometry (offset ranges, bounds, fill value) is read back
|
|
77
|
+
# from the view. Given ranges, a CAWindow view is rolled over like any
|
|
78
|
+
# other array.
|
|
77
79
|
#
|
|
78
80
|
# @param source [CArray, CAWindow] the array to roll over, or a CAWindow
|
|
79
81
|
# view to read the geometry from.
|
|
@@ -81,7 +83,7 @@ class CAWindowIterator < CAIterator
|
|
|
81
83
|
# @param bounds [Symbol] `:skip` / `:nearest` / `:truncate`.
|
|
82
84
|
# @param fill_value [Object, nil] constant margin value, overriding :skip.
|
|
83
85
|
def initialize (source, *ranges, bounds: :skip, fill_value: nil)
|
|
84
|
-
if source.is_a?(CArray) && source.obj_type == CA_OBJ_WINDOW
|
|
86
|
+
if ranges.empty? && source.is_a?(CArray) && source.obj_type == CA_OBJ_WINDOW
|
|
85
87
|
# Backward-compat: read geometry from a CAWindow view built by #window.
|
|
86
88
|
# start[i] = lo, shape[i] (window width) = w, so hi = lo + w - 1.
|
|
87
89
|
win = source
|
|
@@ -104,6 +106,7 @@ class CAWindowIterator < CAIterator
|
|
|
104
106
|
"windows: expected #{@source.ndim} ranges (one per axis), " \
|
|
105
107
|
"got #{@ranges.size}"
|
|
106
108
|
end
|
|
109
|
+
@ranges = @ranges.each_with_index.map { |r, k| window_offsets(r, k) }
|
|
107
110
|
|
|
108
111
|
@sndim = @source.ndim
|
|
109
112
|
@widths = @ranges.map { |r| r.end - r.begin + 1 }
|
|
@@ -158,11 +161,38 @@ class CAWindowIterator < CAIterator
|
|
|
158
161
|
# the padded entity.
|
|
159
162
|
# @return [CArray]
|
|
160
163
|
def sliding_view
|
|
161
|
-
@sliding_view ||= anchored_buffer.sliding_windows(*@widths)
|
|
164
|
+
@sliding_view ||= @shape.include?(0) ? no_windows : anchored_buffer.sliding_windows(*@widths)
|
|
162
165
|
end
|
|
163
166
|
|
|
164
167
|
private
|
|
165
168
|
|
|
169
|
+
# An axis's offsets as an inclusive Integer range, as Ruby reads the range
|
|
170
|
+
# given (`-1...1` is the offsets -1 and 0).
|
|
171
|
+
def window_offsets (r, k)
|
|
172
|
+
unless r.is_a?(Range) && r.begin.is_a?(Integer) && r.end.is_a?(Integer)
|
|
173
|
+
raise TypeError,
|
|
174
|
+
"windows: axis #{k} takes a Range of Integer offsets, got #{r.inspect}"
|
|
175
|
+
end
|
|
176
|
+
last = r.exclude_end? ? r.end - 1 : r.end
|
|
177
|
+
if last < r.begin
|
|
178
|
+
raise ArgumentError, "windows: axis #{k} offsets #{r.inspect} are empty"
|
|
179
|
+
end
|
|
180
|
+
r.begin..last
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# With no anchors (an empty source, or a :truncate window wider than the
|
|
184
|
+
# source) there is nothing to slide over, and sliding_windows refuses a
|
|
185
|
+
# window wider than its buffer. The view is then an empty one of the same
|
|
186
|
+
# shape: the source's unit tiles cut to the output and stretched along the
|
|
187
|
+
# window axes, so every reduction answers with an empty array of its type.
|
|
188
|
+
def no_windows
|
|
189
|
+
unit = @source.block_view(*[1] * @sndim)
|
|
190
|
+
cut = @shape.each_with_index.map { |n, k| n == unit.shape[k] ? nil : 0...n }
|
|
191
|
+
unit = unit[*cut, *[nil] * @sndim] unless cut.all?(&:nil?)
|
|
192
|
+
view = unit.broadcast_to(*(@shape + @widths))
|
|
193
|
+
@source.face? ? view.face_lift(@source) : view
|
|
194
|
+
end
|
|
195
|
+
|
|
166
196
|
# The stretch of the padded buffer the windows are taken from: it begins at
|
|
167
197
|
# the first window and holds one window per anchor. For a window that covers
|
|
168
198
|
# its own anchor and a boundary policy that pads, that is the whole buffer.
|
|
@@ -170,7 +200,7 @@ class CAWindowIterator < CAIterator
|
|
|
170
200
|
buffer = padded_entity
|
|
171
201
|
wanted = @ndim.times.map { |k| @origins[k]...(@origins[k] + @shape[k] + @widths[k] - 1) }
|
|
172
202
|
return buffer if wanted.each_with_index.all? { |range, k|
|
|
173
|
-
range.begin.zero? && range.end == buffer.
|
|
203
|
+
range.begin.zero? && range.end == buffer.shape[k]
|
|
174
204
|
}
|
|
175
205
|
buffer[*wanted]
|
|
176
206
|
end
|
|
@@ -210,10 +240,11 @@ class CAWindowIterator < CAIterator
|
|
|
210
240
|
# One allocation, the source copied into the interior region, then the
|
|
211
241
|
# margins filled per mode.
|
|
212
242
|
def pad_source (src, lefts, rights, mode, value)
|
|
243
|
+
return pad_face(src, lefts, rights, mode, value) if src.face?
|
|
213
244
|
nd = src.ndim
|
|
214
245
|
shape = src.shape
|
|
215
246
|
pshape = nd.times.map { |i| shape[i] + lefts[i] + rights[i] }
|
|
216
|
-
pad = CArray.new(src.data_type, pshape)
|
|
247
|
+
pad = CArray.new(src.data_type, pshape, bytes: src.bytes)
|
|
217
248
|
|
|
218
249
|
# Fill the whole buffer first, then overwrite the interior with the source.
|
|
219
250
|
case mode
|
|
@@ -222,8 +253,8 @@ class CAWindowIterator < CAIterator
|
|
|
222
253
|
when :masked
|
|
223
254
|
pad[] = UNDEF
|
|
224
255
|
when :edge
|
|
225
|
-
#
|
|
226
|
-
|
|
256
|
+
# Nothing to fill: every margin cell is written from the source below
|
|
257
|
+
# (a provisional 0 cannot be stored in a fixlen buffer).
|
|
227
258
|
end
|
|
228
259
|
|
|
229
260
|
interior = nd.times.map { |i| lefts[i]...(lefts[i] + shape[i]) }
|
|
@@ -234,6 +265,23 @@ class CAWindowIterator < CAIterator
|
|
|
234
265
|
pad
|
|
235
266
|
end
|
|
236
267
|
|
|
268
|
+
# A Face is padded on its storage and put back on at the end, so the margins
|
|
269
|
+
# hold storage -- UNDEF, a copy of an edge cell, or a `:constant` value the
|
|
270
|
+
# Face has turned into storage -- and the reductions over the windows are
|
|
271
|
+
# the Face's own. A buffer of the Face's surface type could not take the
|
|
272
|
+
# margin values at all.
|
|
273
|
+
def pad_face (src, lefts, rights, mode, value)
|
|
274
|
+
storage = src.__send__(:face_storage)
|
|
275
|
+
if mode == :constant && !value.nil?
|
|
276
|
+
cell = src.__send__(:blank_like, [1])
|
|
277
|
+
cell[0] = value
|
|
278
|
+
padded = pad_source(storage, lefts, rights, :constant, cell.parent[0])
|
|
279
|
+
else
|
|
280
|
+
padded = pad_source(storage, lefts, rights, mode, value)
|
|
281
|
+
end
|
|
282
|
+
padded.face_lift(src)
|
|
283
|
+
end
|
|
284
|
+
|
|
237
285
|
# Edge-replicate the margins of a padded buffer whose interior already holds
|
|
238
286
|
# the source. Per axis, the left margin rows copy the first interior row and
|
|
239
287
|
# the right margin rows copy the last; done axis by axis over the whole
|
|
@@ -356,19 +404,13 @@ class CAWindowIterator < CAIterator
|
|
|
356
404
|
# @param fill_value [Object, nil] value to put in place of a masked
|
|
357
405
|
# result instead of leaving it masked.
|
|
358
406
|
# @return [CArray] reference-shaped (or shrunk, for :truncate)
|
|
359
|
-
# @!method all
|
|
407
|
+
# @!method all
|
|
360
408
|
# Whether every cell of each rolling is true.
|
|
361
|
-
#
|
|
362
|
-
# for a result; a piece with fewer comes back masked.
|
|
363
|
-
# @param fill_value [Object, nil] value to put in place of a masked
|
|
364
|
-
# result instead of leaving it masked.
|
|
409
|
+
# Takes no `min_count:` or `fill_value:`, as the core `all` does not.
|
|
365
410
|
# @return [CArray] reference-shaped (or shrunk, for :truncate)
|
|
366
|
-
# @!method any
|
|
411
|
+
# @!method any
|
|
367
412
|
# Whether any cell of each rolling is true.
|
|
368
|
-
#
|
|
369
|
-
# for a result; a piece with fewer comes back masked.
|
|
370
|
-
# @param fill_value [Object, nil] value to put in place of a masked
|
|
371
|
-
# result instead of leaving it masked.
|
|
413
|
+
# Takes no `min_count:` or `fill_value:`, as the core `any` does not.
|
|
372
414
|
# @return [CArray] reference-shaped (or shrunk, for :truncate)
|
|
373
415
|
# @!method variancep(min_count: nil, fill_value: nil)
|
|
374
416
|
# Rolling population variance (divisor `n`).
|
|
@@ -405,11 +447,11 @@ class CAWindowIterator < CAIterator
|
|
|
405
447
|
# @param fill_value [Object, nil] value to put in place of a masked
|
|
406
448
|
# result instead of leaving it masked.
|
|
407
449
|
# @return [CArray] reference-shaped (or shrunk, for :truncate)
|
|
408
|
-
[:sum, :accumulate, :prod, :mean, :min, :max, :variance, :stddev,
|
|
450
|
+
[:sum, :accumulate, :prod, :mean, :min, :max, :variance, :stddev,
|
|
409
451
|
:variancep, :stddevp, :minmax, :min_index, :max_index].each do |op|
|
|
410
452
|
class_eval <<~RUBY, __FILE__, __LINE__ + 1
|
|
411
453
|
def #{op} (min_count: nil, fill_value: nil)
|
|
412
|
-
folded = fold_by_offset(:#{op}, min_count, fill_value)
|
|
454
|
+
folded = @shape.include?(0) ? nil : fold_by_offset(:#{op}, min_count, fill_value)
|
|
413
455
|
return folded unless folded.nil?
|
|
414
456
|
kw = {}
|
|
415
457
|
kw[:min_count] = min_count unless min_count.nil?
|
|
@@ -419,6 +461,41 @@ class CAWindowIterator < CAIterator
|
|
|
419
461
|
RUBY
|
|
420
462
|
end
|
|
421
463
|
|
|
464
|
+
# all / any take no min_count: or fill_value:, as the core ones do not.
|
|
465
|
+
[:all, :any].each do |op|
|
|
466
|
+
class_eval <<~RUBY, __FILE__, __LINE__ + 1
|
|
467
|
+
def #{op} (**kw)
|
|
468
|
+
if kw.empty?
|
|
469
|
+
folded = @shape.include?(0) ? nil : fold_by_offset(:#{op}, nil)
|
|
470
|
+
return folded unless folded.nil?
|
|
471
|
+
end
|
|
472
|
+
sliding_view.#{op}(axis: @window_axes, **CAIterator.reduce_keywords(:#{op}, kw))
|
|
473
|
+
end
|
|
474
|
+
RUBY
|
|
475
|
+
end
|
|
476
|
+
|
|
477
|
+
# @!method first
|
|
478
|
+
# Rolling first present value, in row-major order within the window;
|
|
479
|
+
# UNDEF for a window with none (the UNDEF margin of `:skip` is not
|
|
480
|
+
# present, so a window over the edge starts at its first source cell).
|
|
481
|
+
# @return [CArray] reference-shaped (or shrunk, for :truncate)
|
|
482
|
+
# @!method last
|
|
483
|
+
# Rolling last present value; UNDEF for a window with none.
|
|
484
|
+
# @return [CArray] reference-shaped (or shrunk, for :truncate)
|
|
485
|
+
# @!method nunique
|
|
486
|
+
# Rolling number of distinct present values (int64); 0 for a window with
|
|
487
|
+
# none.
|
|
488
|
+
# @return [CArray] reference-shaped (or shrunk, for :truncate)
|
|
489
|
+
#
|
|
490
|
+
# These take no `min_count:` or `fill_value:`, as the core ones do not.
|
|
491
|
+
[:first, :last, :nunique].each do |op|
|
|
492
|
+
class_eval <<~RUBY, __FILE__, __LINE__ + 1
|
|
493
|
+
def #{op} (**kw)
|
|
494
|
+
sliding_view.#{op}(axis: @window_axes, **CAIterator.reduce_keywords(:#{op}, kw))
|
|
495
|
+
end
|
|
496
|
+
RUBY
|
|
497
|
+
end
|
|
498
|
+
|
|
422
499
|
# @overload min_addr
|
|
423
500
|
# Rolling flat SOURCE address of the window minimum — which source cell holds
|
|
424
501
|
# it, so `source.reshape(source.elements)[sw.min_addr]` are the window minima.
|
|
@@ -428,12 +505,12 @@ class CAWindowIterator < CAIterator
|
|
|
428
505
|
# source cell it replicates, and with `bounds: :constant` (or `fill_value:`)
|
|
429
506
|
# a winning margin cell has no source address and is a masked result.
|
|
430
507
|
# @return [CArray] reference-shaped (or shrunk, for :truncate)
|
|
431
|
-
def min_addr; window_winner_addr(:min_index); end
|
|
508
|
+
def min_addr (**kw); window_winner_addr(:min_index, kw); end
|
|
432
509
|
|
|
433
510
|
# @overload max_addr
|
|
434
511
|
# Rolling flat source address of the window maximum. See {#min_addr}.
|
|
435
512
|
# @return [CArray] reference-shaped (or shrunk, for :truncate)
|
|
436
|
-
def max_addr; window_winner_addr(:max_index); end
|
|
513
|
+
def max_addr (**kw); window_winner_addr(:max_index, kw); end
|
|
437
514
|
|
|
438
515
|
private
|
|
439
516
|
|
|
@@ -464,22 +541,34 @@ class CAWindowIterator < CAIterator
|
|
|
464
541
|
:min => :pmin!, :max => :pmax!,
|
|
465
542
|
:all => :and!, :any => :or! }.freeze
|
|
466
543
|
|
|
544
|
+
# A boolean accumulates as the core's does, by parity, so its kernel is xor.
|
|
545
|
+
def offset_fold_kernel (op)
|
|
546
|
+
return :xor! if op == :accumulate && @source.data_type == CA_BOOLEAN
|
|
547
|
+
OFFSET_FOLD.fetch(op)
|
|
548
|
+
end
|
|
549
|
+
|
|
467
550
|
# Widest window this path takes on any one axis.
|
|
468
551
|
OFFSET_FOLD_MAX_WIDTH = 5
|
|
469
552
|
|
|
470
553
|
# Runs the fold by offset, or returns nil when this window is not one it can
|
|
471
554
|
# answer for -- in which case the caller delegates as before.
|
|
472
555
|
def fold_by_offset (op, min_count, fill_value = nil)
|
|
556
|
+
return nil unless CArray.__fast_paths_enabled__
|
|
557
|
+
need = CArray.__present_cells_needed_per_result_cell__(op, min_count, 0)
|
|
473
558
|
return nil unless op == :mean || OFFSET_FOLD.key?(op)
|
|
559
|
+
# The elementwise kernels act on a Face's storage; its reductions are its own.
|
|
560
|
+
return nil if @source.face?
|
|
474
561
|
return nil if @widths.any? { |width| width > OFFSET_FOLD_MAX_WIDTH }
|
|
475
562
|
return nil if neutral_value(op).nil?
|
|
563
|
+
# pmin / pmax let NaN lose, so a window holding only NaN would fold to the
|
|
564
|
+
# neutral value; the core answers NaN there.
|
|
565
|
+
return nil if (op == :min || op == :max) && source_has_nan?
|
|
476
566
|
|
|
477
567
|
folded = op == :mean ? fold_mean : accumulate_offsets(op)
|
|
478
568
|
folded = mask_empty_windows(folded) if EMPTY_WINDOW_IS_UNDEFINED.include?(op)
|
|
479
|
-
folded = apply_min_count(folded,
|
|
569
|
+
folded = apply_min_count(folded, need)
|
|
480
570
|
# A `fill_value:` on the call replaces a result that came out undefined.
|
|
481
|
-
folded
|
|
482
|
-
folded
|
|
571
|
+
CArray.__fill_undefined_result_cells__(folded, fill_value)
|
|
483
572
|
end
|
|
484
573
|
|
|
485
574
|
# The mean is the sum over the same offsets, divided by the number of cells
|
|
@@ -492,13 +581,14 @@ class CAWindowIterator < CAIterator
|
|
|
492
581
|
divisor = counts.copy
|
|
493
582
|
divisor[counts.eq(0)] = 1
|
|
494
583
|
end
|
|
584
|
+
# coerce: the mean's fixed compute type
|
|
495
585
|
accumulate_offsets(:sum).to_type(offset_fold_data_type(:mean)) / divisor
|
|
496
586
|
end
|
|
497
587
|
|
|
498
588
|
# Walks the window offsets, accumulating each shifted plane into the result.
|
|
499
589
|
def accumulate_offsets (op, base = nil, kernel = nil, data_type = nil)
|
|
500
590
|
base ||= offset_fold_base(op)
|
|
501
|
-
kernel ||=
|
|
591
|
+
kernel ||= offset_fold_kernel(op)
|
|
502
592
|
data_type ||= offset_fold_data_type(op)
|
|
503
593
|
accumulator = nil
|
|
504
594
|
offset_grid.each do |offset|
|
|
@@ -540,14 +630,19 @@ class CAWindowIterator < CAIterator
|
|
|
540
630
|
end
|
|
541
631
|
end
|
|
542
632
|
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
633
|
+
def source_has_nan?
|
|
634
|
+
return false unless @source.float? || @source.data_type == CA_OBJECT
|
|
635
|
+
@source.is_nan.any
|
|
636
|
+
end
|
|
637
|
+
|
|
638
|
+
# A result only where the window held `need` cells (CArray.__present_cells_needed_per_result_cell__).
|
|
639
|
+
def apply_min_count (folded, need)
|
|
640
|
+
return folded if need == 0
|
|
546
641
|
counts = window_cell_counts
|
|
547
642
|
if counts.is_a?(Integer)
|
|
548
|
-
folded[] = UNDEF if counts <
|
|
643
|
+
folded[] = UNDEF if counts < need
|
|
549
644
|
else
|
|
550
|
-
folded[counts.lt(
|
|
645
|
+
folded[counts.lt(need)] = UNDEF
|
|
551
646
|
end
|
|
552
647
|
folded
|
|
553
648
|
end
|
|
@@ -644,7 +739,7 @@ class CAWindowIterator < CAIterator
|
|
|
644
739
|
def offset_fold_data_type (op)
|
|
645
740
|
@offset_fold_data_type ||= {}
|
|
646
741
|
@offset_fold_data_type[op] ||=
|
|
647
|
-
|
|
742
|
+
CAIterator.core_probe(@source).public_send(op, axis: [1]).data_type
|
|
648
743
|
end
|
|
649
744
|
|
|
650
745
|
# Every offset within the window, as a list of per-axis positions into the
|
|
@@ -662,8 +757,9 @@ class CAWindowIterator < CAIterator
|
|
|
662
757
|
# offset is the range's begin for a padded margin, 0 for :truncate). A margin
|
|
663
758
|
# winner (coordinate out of bounds) resolves per the boundary policy:
|
|
664
759
|
# :nearest clamps to the edge source cell, :constant / :skip mask the result.
|
|
665
|
-
def window_winner_addr (idx_op)
|
|
666
|
-
|
|
760
|
+
def window_winner_addr (idx_op, kw = {})
|
|
761
|
+
kw = CAIterator.reduce_keywords(idx_op, kw)
|
|
762
|
+
mi = send(idx_op, **kw.slice(:min_count)) # window-local flat index
|
|
667
763
|
n = @source.shape
|
|
668
764
|
wstride = Array.new(@sndim); acc = 1
|
|
669
765
|
(@sndim - 1).downto(0) { |i| wstride[i] = acc; acc *= @widths[i] }
|
|
@@ -687,7 +783,7 @@ class CAWindowIterator < CAIterator
|
|
|
687
783
|
end
|
|
688
784
|
addr[oob] = UNDEF unless @bounds == :nearest
|
|
689
785
|
addr[mi.is_masked.eq(1)] = UNDEF if mi.has_mask?
|
|
690
|
-
addr
|
|
786
|
+
CArray.__fill_undefined_result_cells__(addr, kw[:fill_value])
|
|
691
787
|
end
|
|
692
788
|
|
|
693
789
|
public
|
|
@@ -697,26 +793,29 @@ class CAWindowIterator < CAIterator
|
|
|
697
793
|
# cells (the effective tap count, which drops near a :skip edge);
|
|
698
794
|
# `count(UNDEF)` counts masked cells; `count(v)` counts cells equal to `v`.
|
|
699
795
|
# @return [CArray]
|
|
700
|
-
def count (*args)
|
|
701
|
-
return count_not_masked if args.empty?
|
|
796
|
+
def count (*args, **kw)
|
|
797
|
+
return count_not_masked(**kw) if args.empty?
|
|
702
798
|
# The sliding_windows view is a CAStride, so its #count is not shadowed;
|
|
703
799
|
# dispatch CArray#count explicitly anyway, matching the family regularity.
|
|
704
|
-
CArray.instance_method(:count).bind_call(sliding_view, *args, axis: @window_axes
|
|
800
|
+
CArray.instance_method(:count).bind_call(sliding_view, *args, axis: @window_axes,
|
|
801
|
+
**CAIterator.reduce_keywords(:count, kw))
|
|
705
802
|
end
|
|
706
803
|
|
|
707
804
|
# @overload count_not_masked
|
|
708
805
|
# Rolling count of present (non-masked) cells -- the denominator of a
|
|
709
806
|
# renormalizing convolution.
|
|
710
807
|
# @return [CArray]
|
|
711
|
-
def count_not_masked
|
|
712
|
-
sliding_view.count_not_masked(axis: @window_axes
|
|
808
|
+
def count_not_masked (**kw)
|
|
809
|
+
sliding_view.count_not_masked(axis: @window_axes,
|
|
810
|
+
**CAIterator.reduce_keywords(:count_not_masked, kw))
|
|
713
811
|
end
|
|
714
812
|
|
|
715
813
|
# @overload count_masked
|
|
716
814
|
# Rolling count of masked cells.
|
|
717
815
|
# @return [CArray]
|
|
718
|
-
def count_masked
|
|
719
|
-
sliding_view.count_masked(axis: @window_axes
|
|
816
|
+
def count_masked (**kw)
|
|
817
|
+
sliding_view.count_masked(axis: @window_axes,
|
|
818
|
+
**CAIterator.reduce_keywords(:count_masked, kw))
|
|
720
819
|
end
|
|
721
820
|
|
|
722
821
|
# @overload elements
|
|
@@ -788,40 +887,31 @@ class CAWindowIterator < CAIterator
|
|
|
788
887
|
|
|
789
888
|
# ---- order statistics (median / percentile / quantile) ----------------
|
|
790
889
|
#
|
|
791
|
-
# Core per-axis order statistics take a single axis
|
|
792
|
-
#
|
|
793
|
-
#
|
|
794
|
-
#
|
|
795
|
-
# multi window axes + unmasked margin -> materialize the windows, flatten
|
|
796
|
-
# the window axes into one, single-
|
|
797
|
-
# axis core order-stat
|
|
798
|
-
# :skip (UNDEF margin) -> raise (core has no masked per-axis
|
|
799
|
-
# order-stat); guide to :nearest /
|
|
800
|
-
# :truncate
|
|
801
|
-
#
|
|
802
|
-
# When core gains masked per-axis order statistics (a tracked refactor), the
|
|
803
|
-
# :skip guard can be dropped and :skip served directly.
|
|
890
|
+
# Core per-axis order statistics take a single axis, so a single window
|
|
891
|
+
# axis goes straight to `sliding_view.op(axis: window_axis)`, and several
|
|
892
|
+
# are materialized with the window axes flattened into one. The UNDEF
|
|
893
|
+
# margin of :skip is left out of each window as any masked cell is.
|
|
804
894
|
|
|
805
|
-
# @overload median
|
|
806
|
-
# Rolling median.
|
|
807
|
-
# `:truncate`); with the default `:skip` it raises.
|
|
895
|
+
# @overload median(min_count: nil, fill_value: nil)
|
|
896
|
+
# Rolling median.
|
|
808
897
|
# @return [CArray]
|
|
809
|
-
def median
|
|
810
|
-
|
|
898
|
+
def median (**kw)
|
|
899
|
+
kw = CAIterator.reduce_keywords(:median, kw)
|
|
900
|
+
order_stat { |view, axis| view.median(axis: axis, **kw) }
|
|
811
901
|
end
|
|
812
902
|
|
|
813
|
-
# @overload percentile(*pers)
|
|
903
|
+
# @overload percentile(*pers, min_count: nil, fill_value: nil)
|
|
814
904
|
# Rolling percentile(s). One argument returns one CArray, several return
|
|
815
|
-
# an array of CArrays (as `CArray#percentile`).
|
|
816
|
-
# margin.
|
|
905
|
+
# an array of CArrays (as `CArray#percentile`).
|
|
817
906
|
# @return [CArray, Array<CArray>]
|
|
818
|
-
def percentile (*pers)
|
|
819
|
-
|
|
907
|
+
def percentile (*pers, **kw)
|
|
908
|
+
kw = CAIterator.reduce_keywords(:percentile, kw)
|
|
909
|
+
order_stat { |view, axis| view.percentile(*pers, axis: axis, **kw) }
|
|
820
910
|
end
|
|
821
911
|
|
|
822
912
|
# @overload quantile
|
|
823
913
|
# Rolling five-number summary `[min, Q1, median, Q3, max]` (five CArrays),
|
|
824
|
-
# as `CArray#quantile`.
|
|
914
|
+
# as `CArray#quantile`.
|
|
825
915
|
# @return [Array<CArray>]
|
|
826
916
|
def quantile
|
|
827
917
|
order_stat { |view, axis| view.quantile(axis: axis) }
|
|
@@ -830,14 +920,8 @@ class CAWindowIterator < CAIterator
|
|
|
830
920
|
private
|
|
831
921
|
|
|
832
922
|
# Drive an order statistic (yielded as `block.call(view, axis)`) through the
|
|
833
|
-
# single-axis / multi-axis
|
|
923
|
+
# single-axis / multi-axis dispatch above.
|
|
834
924
|
def order_stat
|
|
835
|
-
if @bounds == :skip
|
|
836
|
-
raise ArgumentError,
|
|
837
|
-
"windowed order statistics need an unmasked margin; " \
|
|
838
|
-
"use bounds: :nearest (edge-extend) or bounds: :truncate (valid). " \
|
|
839
|
-
"For an UNDEF-margin median use reduce { |w| w.median } (slower)."
|
|
840
|
-
end
|
|
841
925
|
sv = sliding_view
|
|
842
926
|
if @window_axes.size == 1
|
|
843
927
|
yield sv, @window_axes[0]
|
|
@@ -858,15 +942,17 @@ class CAWindowIterator < CAIterator
|
|
|
858
942
|
# @overload wsum(weights)
|
|
859
943
|
# Rolling weighted sum, `weights` shaped like a single window.
|
|
860
944
|
# @return [CArray]
|
|
861
|
-
def wsum (weights)
|
|
862
|
-
|
|
945
|
+
def wsum (weights, **kw)
|
|
946
|
+
kw = CAIterator.reduce_keywords(:wsum, kw)
|
|
947
|
+
weighted(weights) { |sv, w, axis| sv.wsum(w, axis: axis, **kw) }
|
|
863
948
|
end
|
|
864
949
|
|
|
865
950
|
# @overload wmean(weights)
|
|
866
951
|
# Rolling weighted mean, `weights` shaped like a single window.
|
|
867
952
|
# @return [CArray]
|
|
868
|
-
def wmean (weights)
|
|
869
|
-
|
|
953
|
+
def wmean (weights, **kw)
|
|
954
|
+
kw = CAIterator.reduce_keywords(:wmean, kw)
|
|
955
|
+
weighted(weights) { |sv, w, axis| sv.wmean(w, axis: axis, **kw) }
|
|
870
956
|
end
|
|
871
957
|
|
|
872
958
|
private
|
|
@@ -1008,13 +1094,51 @@ class CAWindowIterator < CAIterator
|
|
|
1008
1094
|
# winner is well-defined).
|
|
1009
1095
|
# @raise [NotImplementedError]
|
|
1010
1096
|
[:cumsum, :cumprod, :cummax, :cummin, :cumcount].each do |op|
|
|
1011
|
-
define_method
|
|
1097
|
+
# define_method: a refusal that names itself in its own message
|
|
1098
|
+
define_method(op, &Ractor.make_shareable(proc do |*, **|
|
|
1012
1099
|
raise NotImplementedError,
|
|
1013
1100
|
"#{self.class} has no #{op}: a segment scan needs each cell to " \
|
|
1014
1101
|
"belong to exactly one piece, but overlapping windows share cells, " \
|
|
1015
1102
|
"so a per-cell running value is ill-defined; use reduce for a " \
|
|
1016
1103
|
"custom per-window fold."
|
|
1017
|
-
end
|
|
1104
|
+
end))
|
|
1105
|
+
end
|
|
1106
|
+
|
|
1107
|
+
# @!method lag(n = 1)
|
|
1108
|
+
# Not supported for a window iterator. A lag writes each cell the value
|
|
1109
|
+
# some steps earlier in its piece, which needs each cell to have one
|
|
1110
|
+
# position in one piece; overlapping windows put a cell in many windows.
|
|
1111
|
+
# Use `source.lag(n, axis:)` for the lag along an axis.
|
|
1112
|
+
# @raise [NotImplementedError]
|
|
1113
|
+
def lag (*)
|
|
1114
|
+
raise NotImplementedError,
|
|
1115
|
+
"#{self.class} has no lag: a cell belongs to many overlapping windows, " \
|
|
1116
|
+
"so it has no single position to step back from; use " \
|
|
1117
|
+
"source.lag(n, axis:) for the lag along an axis."
|
|
1118
|
+
end
|
|
1119
|
+
|
|
1120
|
+
# @!method strip_mask
|
|
1121
|
+
# Not supported for a window iterator: a cell belongs to many overlapping
|
|
1122
|
+
# windows, so it has no single value to be filled with. Use
|
|
1123
|
+
# `source.strip_mask(method:, axis:)` for a fill along an axis.
|
|
1124
|
+
# @raise [NotImplementedError]
|
|
1125
|
+
def strip_mask (*, **)
|
|
1126
|
+
raise NotImplementedError,
|
|
1127
|
+
"#{self.class} has no strip_mask: a cell belongs to many overlapping " \
|
|
1128
|
+
"windows, so it has no single fill; use source.strip_mask(method:, axis:) " \
|
|
1129
|
+
"for a fill along an axis."
|
|
1130
|
+
end
|
|
1131
|
+
|
|
1132
|
+
# @!method order
|
|
1133
|
+
# Not supported for a window iterator: a cell belongs to many overlapping
|
|
1134
|
+
# windows, so it has no single rank. Use `source.order(axis:)` for the
|
|
1135
|
+
# rank along an axis.
|
|
1136
|
+
# @raise [NotImplementedError]
|
|
1137
|
+
def order (*, **)
|
|
1138
|
+
raise NotImplementedError,
|
|
1139
|
+
"#{self.class} has no order: a cell belongs to many overlapping windows, " \
|
|
1140
|
+
"so it has no single rank; use source.order(axis:) for the rank along " \
|
|
1141
|
+
"an axis."
|
|
1018
1142
|
end
|
|
1019
1143
|
|
|
1020
1144
|
private
|
|
@@ -1040,7 +1164,44 @@ class CArray
|
|
|
1040
1164
|
# @param bounds [Symbol] `:skip` / `:nearest` / `:truncate`.
|
|
1041
1165
|
# @param fill_value [Object, nil] constant margin value.
|
|
1042
1166
|
# @return [CAWindowIterator]
|
|
1043
|
-
|
|
1167
|
+
# @overload windows(on:, before: nil, after: nil, exclude: :lower, by: nil, interval: nil, at: nil)
|
|
1168
|
+
# Returns a {CAKeyWindowIterator}: a window per value bounded by a width
|
|
1169
|
+
# of the key `on` (a time, a duration or a number) instead of a count of
|
|
1170
|
+
# cells. The window of a value whose key is t holds the values whose
|
|
1171
|
+
# key lies between t - before and t + after; `exclude:` leaves out the
|
|
1172
|
+
# lower end (default), the upper end, both or neither. `by:` keeps
|
|
1173
|
+
# each window within the values with the same by keys, and `interval:`
|
|
1174
|
+
# gives the key's grid step for {CAKeyWindowIterator#is_complete}. A
|
|
1175
|
+
# value whose key is masked, NaN or infinite is in no window and has no
|
|
1176
|
+
# window. `at:` places the windows at those points of the key instead
|
|
1177
|
+
# of at the values, one answer per point (a grid of points gives
|
|
1178
|
+
# windows on a grid); it cannot be given with `by:`.
|
|
1179
|
+
#
|
|
1180
|
+
# temp.windows(on: time, before: "1 hour").mean
|
|
1181
|
+
# temp.windows(on: time, at: hours, after: "3 hours", exclude: :upper).mean
|
|
1182
|
+
# @return [CAKeyWindowIterator]
|
|
1183
|
+
def windows (*ranges, on: nil, **key_window)
|
|
1184
|
+
unless on.nil?
|
|
1185
|
+
unless ranges.empty?
|
|
1186
|
+
raise ArgumentError, "windows: give offset ranges or on:, not both"
|
|
1187
|
+
end
|
|
1188
|
+
given = [:bounds, :fill_value] & key_window.keys
|
|
1189
|
+
unless given.empty?
|
|
1190
|
+
raise ArgumentError,
|
|
1191
|
+
"windows: #{given.map { |k| "#{k}:" }.join(', ')} " \
|
|
1192
|
+
"belong to offset windows; a window on a key has no padding " \
|
|
1193
|
+
"(a row with no window answers UNDEF)"
|
|
1194
|
+
end
|
|
1195
|
+
require "carray/key_windows"
|
|
1196
|
+
return CAKeyWindowIterator.new(self, on: on, **key_window)
|
|
1197
|
+
end
|
|
1198
|
+
bounds = key_window.delete(:bounds) { :skip }
|
|
1199
|
+
fill_value = key_window.delete(:fill_value)
|
|
1200
|
+
unless key_window.empty?
|
|
1201
|
+
raise ArgumentError,
|
|
1202
|
+
"windows: #{key_window.keys.map { |k| "#{k}:" }.join(', ')} " \
|
|
1203
|
+
"need on:, the key the widths are measured on"
|
|
1204
|
+
end
|
|
1044
1205
|
if ranges.size == 1 && ranges[0].is_a?(CArray) && ranges[0].obj_type == CA_OBJ_WINDOW
|
|
1045
1206
|
return CAWindowIterator.new(ranges[0])
|
|
1046
1207
|
end
|
data/lib/carray.rb
CHANGED
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
require 'carray_ext'
|
|
4
4
|
|
|
5
|
+
# Errors name the method the user called: the outermost frame of the library
|
|
6
|
+
# before the user's code, and the library's Ruby frames are the ones here.
|
|
7
|
+
CArray.send(:library_root=, __dir__)
|
|
8
|
+
|
|
5
9
|
# Top-level CAMath module -- math namespace for CArrays.
|
|
6
10
|
# `CAMath.sin(ca)` works because module functions inherited
|
|
7
11
|
# from Math (via include) accept any object that responds to
|
|
@@ -24,7 +28,7 @@ require 'carray/runtime' # load-bearing Ruby support the core depends on
|
|
|
24
28
|
require 'carray/basics' # frequently-used convenience methods kept eager
|
|
25
29
|
require 'carray/conditional' # then_else / replace_where / conditional
|
|
26
30
|
require 'carray/mask_gap_fill' # unmask/strip_mask method: keyword (hold / linear gap-fill)
|
|
27
|
-
require 'carray/attribute' # per-instance metadata Hash (#
|
|
31
|
+
require 'carray/attribute' # per-instance metadata Hash (#attr / #set_attr / #attrs / #has_attr?)
|
|
28
32
|
require 'carray/boolean_reduce' # all/any/none skip_masked: keyword (Kleene fold)
|
|
29
33
|
require 'carray/meld_reduce' # CAMeld per-parent reduce fast path (sum/mean/min/max along meld_axis)
|
|
30
34
|
# carray/ordering CIFY (2026-06-21): translated to ext/carray_order.c
|
|
@@ -37,7 +41,7 @@ require 'carray/complex' # real / imag accessors; MUST precede carray/lazy
|
|
|
37
41
|
require 'carray/lazy' # the lazy elementwise view layer
|
|
38
42
|
# carray/face.rb was deleted (the Phase 1 skeleton CArray::Face module
|
|
39
43
|
# became dead weight, replaced by C-layer macro deploy +
|
|
40
|
-
# copy_state/
|
|
44
|
+
# copy_state/storage_to_element convention).
|
|
41
45
|
require 'carray/time' # CATime / CATimedelta
|
|
42
46
|
# carray/methods/* (bincount / broadcast / gather_nd+put_nd) are small
|
|
43
47
|
# single-feature method files, loaded lazily via autoload_carray.
|
|
@@ -49,6 +53,9 @@ require 'carray/string_operation_extension' # shared StringOperationMixin (must
|
|
|
49
53
|
require 'carray/const_string' # CAConstString construction surface
|
|
50
54
|
require 'carray/string' # CAString construction surface
|
|
51
55
|
require 'carray/fixlen_string' # CAFixlenString construction surface
|
|
56
|
+
# Loaded here, not on first use: Ruby's own error messages call #inspect, and
|
|
57
|
+
# a Ractor other than the main one cannot load a file.
|
|
58
|
+
require 'carray/inspect'
|
|
52
59
|
# CArray.format lives in carray/methods/string_format.rb, autoloaded on first call
|
|
53
60
|
|
|
54
61
|
# carray/frame is loaded lazily via autoload_carray (CAFrame / GroupedFrame
|
data/yard-stubs/ca_obj_array.rb
CHANGED
|
@@ -43,9 +43,9 @@ class CArray
|
|
|
43
43
|
# `data_type`, shape, mask, and element data into `self`. Result
|
|
44
44
|
# is an independent entity.
|
|
45
45
|
#
|
|
46
|
-
#
|
|
47
|
-
#
|
|
48
|
-
#
|
|
46
|
+
# For views, `dup` / `clone` produce a new view sharing the parent
|
|
47
|
+
# (Ruby shallow-copy semantics), so they do not copy. Don't use
|
|
48
|
+
# them to copy a CArray; use {#copy}.
|
|
49
49
|
# @param other [CArray]
|
|
50
50
|
# @return [self]
|
|
51
51
|
# @api private
|
|
@@ -66,7 +66,7 @@ class CArray
|
|
|
66
66
|
# @return [Class]
|
|
67
67
|
# @overload fixlen(*shape, bytes:) { ... }
|
|
68
68
|
# Equivalent to `CArray.new(:fixlen, shape, bytes: bytes) { ... }`.
|
|
69
|
-
# The `bytes:` keyword is required for `:fixlen
|
|
69
|
+
# The `bytes:` keyword is required for `:fixlen`, and is 1 or more.
|
|
70
70
|
# @param shape [Array<Integer>]
|
|
71
71
|
# @param bytes [Integer]
|
|
72
72
|
# @return [CArray]
|
|
@@ -11,6 +11,11 @@
|
|
|
11
11
|
# On big-endian hosts the bit axis of a non-fixlen parent reflects
|
|
12
12
|
# network byte order (parent bytes are walked in reverse within each
|
|
13
13
|
# element); single-byte parents are linear on either endian.
|
|
14
|
+
#
|
|
15
|
+
# A bit view has no mask. The bits of one byte would share that byte's
|
|
16
|
+
# one mask cell, so the view refuses a masked parent and refuses to be
|
|
17
|
+
# masked (`mask=`, storing `UNDEF`, storing a masked array). Read the
|
|
18
|
+
# bits of a masked array through `value.bitarray`.
|
|
14
19
|
class CABitarray < CAView
|
|
15
20
|
end
|
|
16
21
|
|
|
@@ -27,6 +32,8 @@ class CArray
|
|
|
27
32
|
# @return [CABitarray]
|
|
28
33
|
# @raise [CADataTypeError] when `self.data_type` is a complex or
|
|
29
34
|
# object type (bit-level access is not defined for those).
|
|
35
|
+
# @raise [ArgumentError] when `self` has a mask; use
|
|
36
|
+
# `value.bitarray` to read the bits without it.
|
|
30
37
|
def bitarray; end
|
|
31
38
|
|
|
32
39
|
# Alias for {#bitarray}.
|