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
data/lib/carray/methods/bin.rb
CHANGED
|
@@ -18,6 +18,11 @@ class CArray
|
|
|
18
18
|
# that side. NaN / masked input cells are always masked in the
|
|
19
19
|
# output, independently of `lfill` / `ufill`.
|
|
20
20
|
#
|
|
21
|
+
# When `vmin == vmax` the range is one point: a value equal to it is
|
|
22
|
+
# in bin 0 whatever `bins:` or `step` says, and a value below or above
|
|
23
|
+
# it is out of range. An array whose values are all the same is
|
|
24
|
+
# binned this way by `bin(a.min, a.max, ...)`.
|
|
25
|
+
#
|
|
21
26
|
# Use `snap(step, offset:)` when the desired output is the snapped
|
|
22
27
|
# value; use `bin_to(edges)` when the edges are non-uniform.
|
|
23
28
|
#
|
|
@@ -46,23 +51,21 @@ class CArray
|
|
|
46
51
|
end
|
|
47
52
|
raise ArgumentError, "bin: vmin > vmax" if vmin > vmax
|
|
48
53
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
54
|
+
unless bins.nil? || bins.is_a?(Integer)
|
|
55
|
+
raise TypeError, "bin: bins: must be an Integer (got #{bins.inspect})"
|
|
56
|
+
end
|
|
52
57
|
if vmin == vmax
|
|
53
|
-
#
|
|
54
|
-
#
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
if inv.count(true) > 0
|
|
60
|
-
out.mask = out.has_mask? ? (out.mask | inv) : inv
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
return out
|
|
58
|
+
# A range of one point: a value equal to it is in bin 0 whatever the
|
|
59
|
+
# count of bins (or the step), a value below or above it is out of
|
|
60
|
+
# range.
|
|
61
|
+
raise ArgumentError, "bin: n must be >= 1" if bins && bins < 1
|
|
62
|
+
return bin_to(CArray.float64(2) { vmin.to_f },
|
|
63
|
+
lfill: lfill, ufill: ufill, include_max: true)
|
|
64
64
|
end
|
|
65
65
|
|
|
66
|
+
n = bins || ((vmax - vmin).to_f / step).round
|
|
67
|
+
raise ArgumentError, "bin: n must be >= 1" if n < 1
|
|
68
|
+
|
|
66
69
|
# Delegate to `bin_to` with generated uniform edges — same kernel
|
|
67
70
|
# (`histbin_ki`) as `histogram`, so semantics are identical.
|
|
68
71
|
edges = CArray.float64(n + 1).span(vmin..vmax)
|
|
@@ -99,8 +102,9 @@ class CArray
|
|
|
99
102
|
# @return [CArray] `CA_INT64` array with the same shape as `self`
|
|
100
103
|
# holding bin indices in `[0, N-1]` (or the fill values / mask
|
|
101
104
|
# for out-of-range and masked cells).
|
|
102
|
-
# @raise [ArgumentError] when `edges` is not 1-D
|
|
103
|
-
#
|
|
105
|
+
# @raise [ArgumentError] when `edges` is not 1-D, has fewer than 2
|
|
106
|
+
# values, has a masked value or NaN, or decreases anywhere. Equal
|
|
107
|
+
# edges (a bin of zero width) and infinite ones are accepted.
|
|
104
108
|
# @example
|
|
105
109
|
# e = CA_FLOAT64([0, 1, 10, 100])
|
|
106
110
|
# v = CA_FLOAT64([0.5, 5.0, 50.0, -1.0, 200.0])
|
|
@@ -120,8 +124,12 @@ class CArray
|
|
|
120
124
|
# under -> -1, in-range -> 0..N-1, over -> N.
|
|
121
125
|
out = src.send(:histbin_ki, e, include_max) - 1
|
|
122
126
|
|
|
123
|
-
|
|
124
|
-
|
|
127
|
+
# Both sides are found before either is written, so a fill that equals
|
|
128
|
+
# the other side's index (lfill: n) is not taken for it.
|
|
129
|
+
under = out.eq(-1)
|
|
130
|
+
over = out.eq(n)
|
|
131
|
+
out[under] = lfill.nil? ? UNDEF : lfill
|
|
132
|
+
out[over] = ufill.nil? ? UNDEF : ufill
|
|
125
133
|
out
|
|
126
134
|
end
|
|
127
135
|
|
|
@@ -35,10 +35,26 @@ class CArray
|
|
|
35
35
|
raise CArray::DataTypeError,
|
|
36
36
|
"bincount requires an integer label array (got #{data_type_name})"
|
|
37
37
|
end
|
|
38
|
+
unless length.is_a?(Integer)
|
|
39
|
+
raise TypeError, "bincount: length: must be an Integer (got #{length.inspect})"
|
|
40
|
+
end
|
|
41
|
+
if length < 0
|
|
42
|
+
raise ArgumentError, "bincount: length: must not be negative (got #{length})"
|
|
43
|
+
end
|
|
44
|
+
unless weights.nil?
|
|
45
|
+
unless weights.is_a?(CArray)
|
|
46
|
+
raise ArgumentError, "bincount: weights must be a CArray"
|
|
47
|
+
end
|
|
48
|
+
unless weights.integer? || weights.float?
|
|
49
|
+
raise CArray::DataTypeError,
|
|
50
|
+
"bincount: weights must be numeric (got " \
|
|
51
|
+
"#{CArray::Inspector.type_label(weights)})"
|
|
52
|
+
end
|
|
53
|
+
end
|
|
38
54
|
|
|
39
55
|
if elements.zero?
|
|
40
56
|
if weights
|
|
41
|
-
out = CArray.new(weights.data_type, [length])
|
|
57
|
+
out = CArray.new(weights.data_type, [length], bytes: weights.bytes)
|
|
42
58
|
else
|
|
43
59
|
out_type = (length > 0xFFFFFFFF) ? CA_UINT64 : CA_UINT32
|
|
44
60
|
out = CArray.new(out_type, [length])
|
|
@@ -54,7 +70,7 @@ class CArray
|
|
|
54
70
|
# Every cell is masked: no labels to count, same result as an empty
|
|
55
71
|
# input (all-zero output of the requested minimum length).
|
|
56
72
|
if weights
|
|
57
|
-
out = CArray.new(weights.data_type, [length])
|
|
73
|
+
out = CArray.new(weights.data_type, [length], bytes: weights.bytes)
|
|
58
74
|
else
|
|
59
75
|
out_type = (length > 0xFFFFFFFF) ? CA_UINT64 : CA_UINT32
|
|
60
76
|
out = CArray.new(out_type, [length])
|
|
@@ -58,20 +58,31 @@ class CArray
|
|
|
58
58
|
# multiple of 8) are zero-filled. The byte order matches the packed-bit
|
|
59
59
|
# convention used by Apache Arrow validity bitmaps and PEP 3118 `?` /
|
|
60
60
|
# `_Bool` at the bit level.
|
|
61
|
+
# A masked cell has no bit to pack, so a receiver with masked cells is
|
|
62
|
+
# refused; pack the mask separately with {#validity_bits}.
|
|
61
63
|
# @return [CArray] uint8 CArray of shape `[ceil(n / 8)]`.
|
|
62
|
-
# @raise [ArgumentError] when the receiver is not 1-D
|
|
63
|
-
#
|
|
64
|
+
# @raise [ArgumentError] when the receiver is not 1-D, its `data_type`
|
|
65
|
+
# is not one of `CA_BOOLEAN` / `CA_UINT8` / `CA_INT8`, or it has
|
|
66
|
+
# masked cells.
|
|
64
67
|
def pack_bits
|
|
65
68
|
unless data_type == CA_BOOLEAN || data_type == CA_UINT8 || data_type == CA_INT8
|
|
66
69
|
raise ArgumentError,
|
|
67
70
|
"pack_bits: expected CA_BOOLEAN / CA_UINT8 / CA_INT8 (got #{data_type_name})"
|
|
68
71
|
end
|
|
69
|
-
raise ArgumentError, "pack_bits: 1-D CArray expected (got
|
|
72
|
+
raise ArgumentError, "pack_bits: 1-D CArray expected (got ndim #{ndim})" unless ndim == 1
|
|
70
73
|
n = elements
|
|
71
74
|
n_bytes = (n + 7) / 8
|
|
72
75
|
packed = CArray.uint8(n_bytes) { 0 }
|
|
73
76
|
return packed if n == 0
|
|
74
|
-
|
|
77
|
+
source = self
|
|
78
|
+
if has_mask?
|
|
79
|
+
if count_masked > 0
|
|
80
|
+
raise ArgumentError,
|
|
81
|
+
"pack_bits: masked cells have no bit to pack; pack the mask with validity_bits"
|
|
82
|
+
end
|
|
83
|
+
source = value
|
|
84
|
+
end
|
|
85
|
+
packed.bitarray.reshape(-1)[0..n-1] = source
|
|
75
86
|
packed
|
|
76
87
|
end
|
|
77
88
|
|
|
@@ -6,7 +6,8 @@ class CArray
|
|
|
6
6
|
#
|
|
7
7
|
# Where `self == i`, the result takes `choices[i]` -- a scalar
|
|
8
8
|
# fills those cells, a CArray contributes its corresponding
|
|
9
|
-
# cells. The result has the same shape as `self`.
|
|
9
|
+
# cells. The result has the same shape as `self`. Where a label
|
|
10
|
+
# is masked, the result is masked.
|
|
10
11
|
#
|
|
11
12
|
# @param choices [Array<CArray, Object>] values indexed by the
|
|
12
13
|
# labels in `self`; each entry is either a same-shape CArray
|
|
@@ -15,7 +16,10 @@ class CArray
|
|
|
15
16
|
# When `nil` it is inferred: `CArray.result_type` of the CArray
|
|
16
17
|
# choices, or `CA_OBJECT` when every choice is a scalar.
|
|
17
18
|
# @return [CArray] new CArray with the shape of `self` holding
|
|
18
|
-
# the chosen values
|
|
19
|
+
# the chosen values; a {CAString} when every choice is a String
|
|
20
|
+
# or a {CAString} and `data_type` is not given.
|
|
21
|
+
# @raise [IndexError] if a label that is not masked is negative or
|
|
22
|
+
# not less than the number of choices.
|
|
19
23
|
# @example
|
|
20
24
|
# ref = CA_INT([[0, 1, 2], [1, 2, 0], [2, 0, 1]])
|
|
21
25
|
# a = CArray.int(3, 3).seq(1)
|
|
@@ -24,6 +28,14 @@ class CArray
|
|
|
24
28
|
# ref.choose([a, b, c]) # per-cell pick from a / b / c
|
|
25
29
|
# ref.choose(["a", "b", "c"]) # recode labels to values
|
|
26
30
|
def choose (choices, data_type: nil)
|
|
31
|
+
outside = (self.lt(0) | self.ge(choices.size)).where
|
|
32
|
+
if outside.elements > 0
|
|
33
|
+
addr = outside[0]
|
|
34
|
+
raise IndexError,
|
|
35
|
+
"choose: label #{self[addr]} at address #{addr} is out of range " \
|
|
36
|
+
"for #{choices.size} choices"
|
|
37
|
+
end
|
|
38
|
+
text = !data_type && CArray.send(:selection_text?, choices)
|
|
27
39
|
unless data_type
|
|
28
40
|
ca = choices.select { |v| v.is_a?(CArray) }
|
|
29
41
|
data_type = ca.empty? ? CA_OBJECT : CArray.result_type(*ca)
|
|
@@ -33,7 +45,8 @@ class CArray
|
|
|
33
45
|
s = self.eq(i)
|
|
34
46
|
out[s] = v.is_a?(CArray) ? v[s] : v
|
|
35
47
|
end
|
|
36
|
-
out
|
|
48
|
+
out[self.is_masked] = UNDEF if has_mask?
|
|
49
|
+
text ? CAString.wrap(out) : out
|
|
37
50
|
end
|
|
38
51
|
|
|
39
52
|
end
|
|
@@ -75,7 +75,8 @@ class CArray
|
|
|
75
75
|
# Use `CArray.meld` for the uniform-shape view-default counterpart.
|
|
76
76
|
#
|
|
77
77
|
# @param list [Array<CArray>] pieces to concatenate.
|
|
78
|
-
# @param axis [Integer] axis to concatenate along
|
|
78
|
+
# @param axis [Integer, nil] axis to concatenate along; `nil` is the
|
|
79
|
+
# same as `0`.
|
|
79
80
|
# @param data_type [Symbol, Integer, nil] result `data_type`;
|
|
80
81
|
# inferred via `result_type` when `nil`.
|
|
81
82
|
# @return [CArray] fresh CArray with per-piece `axis` sizes summed.
|
|
@@ -83,7 +84,8 @@ class CArray
|
|
|
83
84
|
# inconsistent.
|
|
84
85
|
def self.concatenate (list, axis: 0, data_type: nil)
|
|
85
86
|
raise ArgumentError, "concatenate: list must not be empty" if list.empty?
|
|
86
|
-
|
|
87
|
+
axis = 0 if axis.nil?
|
|
88
|
+
__ragged_paste(list, [list.size], axis, data_type, "concatenate")
|
|
87
89
|
end
|
|
88
90
|
|
|
89
91
|
# @overload concatenate(*others, axis: 0, data_type: nil)
|
|
@@ -128,7 +130,7 @@ class CArray
|
|
|
128
130
|
raise ArgumentError,
|
|
129
131
|
"mosaic: tdim product (#{expected}) must equal list size (#{list.size})"
|
|
130
132
|
end
|
|
131
|
-
__ragged_paste(list, tdim, axis, data_type)
|
|
133
|
+
__ragged_paste(list, tdim, axis, data_type, "mosaic")
|
|
132
134
|
end
|
|
133
135
|
|
|
134
136
|
# Shared eager paste-loop helper (= ex-`combine` paste implementation,
|
|
@@ -136,7 +138,8 @@ class CArray
|
|
|
136
138
|
# shape obtained by tiling `list` over `tdim` starting at `axis`, with
|
|
137
139
|
# per-piece dim sizes summed along each tile axis (= block-matrix
|
|
138
140
|
# consistency required across rows / columns).
|
|
139
|
-
def self.__ragged_paste (list, tdim, axis, data_type)
|
|
141
|
+
def self.__ragged_paste (list, tdim, axis, data_type, name)
|
|
142
|
+
shared_attrs = attrs_shared_by(list)
|
|
140
143
|
list = CArray.promote_list(list, data_type: data_type)
|
|
141
144
|
# promote_list has already enforced homogeneity (= common data_type, and
|
|
142
145
|
# for Face elements: same Face class + portable + state-compatible). So,
|
|
@@ -144,12 +147,16 @@ class CArray
|
|
|
144
147
|
# result with face_lift, rather than special-casing data_class / DATA_SIZE
|
|
145
148
|
# here (= which only ever handled CARecord and silently corrupted plain
|
|
146
149
|
# fixlen / numeric Faces by leaving bytes nil).
|
|
147
|
-
|
|
150
|
+
# The Face is re-wrapped from the widest piece: a fixlen Face carries
|
|
151
|
+
# its cell width, and the result is as wide as its widest piece.
|
|
152
|
+
face_parent = list[0].face? ? list.max_by(&:bytes) : nil
|
|
148
153
|
if face_parent
|
|
149
|
-
list = list.map { |x|
|
|
154
|
+
list = list.map { |x| x.__send__(:face_storage) }
|
|
150
155
|
end
|
|
151
156
|
data_type = list[0].data_type
|
|
152
|
-
|
|
157
|
+
# Fixlen pieces of different widths join at the widest, so no cell is
|
|
158
|
+
# cut; each narrower piece is padded with NUL as it is pasted.
|
|
159
|
+
bytes = (data_type == :fixlen) ? list.map(&:bytes).max : nil
|
|
153
160
|
# promote_list guarantees a non-empty list of CArrays, so the reference
|
|
154
161
|
# shape is simply the first element (a CScalar carries shape [1], so a
|
|
155
162
|
# leading scalar is fine; the scalar-expansion pass below broadcasts it).
|
|
@@ -157,7 +164,7 @@ class CArray
|
|
|
157
164
|
dim = ref.shape
|
|
158
165
|
ndim = ref.ndim
|
|
159
166
|
tndim = tdim.size
|
|
160
|
-
axis = CArray.normalize_axis(axis, ndim - tndim + 1,
|
|
167
|
+
axis = CArray.normalize_axis(axis, ndim - tndim + 1, name)
|
|
161
168
|
|
|
162
169
|
list = list.map do |x|
|
|
163
170
|
if x.scalar?
|
|
@@ -190,7 +197,7 @@ class CArray
|
|
|
190
197
|
block.each_with_index do |item, *tidx|
|
|
191
198
|
unless item.ndim == ndim
|
|
192
199
|
raise ArgumentError,
|
|
193
|
-
"
|
|
200
|
+
"#{name}: piece at tile #{tidx.inspect} has ndim " \
|
|
194
201
|
"#{item.ndim} (expected #{ndim})"
|
|
195
202
|
end
|
|
196
203
|
ndim.times do |d|
|
|
@@ -199,7 +206,7 @@ class CArray
|
|
|
199
206
|
expected = on_tile ? tile_sizes[i][tidx[i]] : dim[d]
|
|
200
207
|
unless item.shape[d] == expected
|
|
201
208
|
raise ArgumentError,
|
|
202
|
-
"
|
|
209
|
+
"#{name}: piece at tile #{tidx.inspect} has " \
|
|
203
210
|
"#{on_tile ? 'tile' : 'non-tile'} axis #{d} size " \
|
|
204
211
|
"#{item.shape[d]} (expected #{expected}); pieces must agree " \
|
|
205
212
|
"on non-tile axes and be block-matrix consistent along tile axes"
|
|
@@ -217,6 +224,7 @@ class CArray
|
|
|
217
224
|
obj.paste(idx, item)
|
|
218
225
|
end
|
|
219
226
|
obj = obj.face_lift(face_parent) if face_parent
|
|
227
|
+
obj.set_attrs(shared_attrs) unless shared_attrs.empty?
|
|
220
228
|
obj
|
|
221
229
|
end
|
|
222
230
|
private_class_method :__ragged_paste
|
|
@@ -23,8 +23,8 @@ class CArray
|
|
|
23
23
|
# Distinctness is the family's hash-key judgement (see {#unique}):
|
|
24
24
|
# `==` for numeric with all NaN collapsed to one value and
|
|
25
25
|
# -0.0 / +0.0 the same value; `eql?` / `hash` for `CA_OBJECT` and
|
|
26
|
-
# `CA_FIXLEN`. Complex
|
|
27
|
-
#
|
|
26
|
+
# `CA_FIXLEN`. Complex values compare by value, with NaN and -0.0
|
|
27
|
+
# treated the same way in each component.
|
|
28
28
|
#
|
|
29
29
|
# There is no `sort:` here, unlike {#unique} and {#value_counts}:
|
|
30
30
|
# the codes index the levels, so reordering the vocabulary would
|
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
# Treats the trailing K axes of `indices.shape = (..., K)` as coordinate tuples
|
|
6
6
|
# and consumes the first K axes of params while carrying the remaining axes (= rest).
|
|
7
7
|
#
|
|
8
|
-
# Implementation goes through
|
|
8
|
+
# Implementation goes through an index view (CAGrid) of the flattened array:
|
|
9
9
|
# `params.flatten[flat_addr]`. Pure Ruby, no additional framework, composed
|
|
10
|
-
# entirely from existing indexers as in `take_along_axis`. Since
|
|
10
|
+
# entirely from existing indexers as in `take_along_axis`. Since the view is
|
|
11
11
|
# writable, write-through via `put_nd` uses last-write-wins semantics (= last
|
|
12
12
|
# value wins on duplicate indices, same contract as `put_along_axis`). For
|
|
13
13
|
# accumulate semantics, route to the `scatter_*!` family instead.
|
|
@@ -63,8 +63,9 @@ class CArray
|
|
|
63
63
|
def gather_nd (indices)
|
|
64
64
|
flat_addr, outer, rest = gather_nd_flat_addr(indices, "gather_nd")
|
|
65
65
|
out_shape = outer + rest
|
|
66
|
-
# flatten + 1-D fancy indexing ->
|
|
67
|
-
|
|
66
|
+
# flatten + 1-D fancy indexing -> CAGrid view -> materialise via .copy.
|
|
67
|
+
# A masked coordinate reads as UNDEF: project carries the address mask.
|
|
68
|
+
result = flat_addr.has_mask? ? flatten.project(flat_addr) : self.flatten[flat_addr].copy
|
|
68
69
|
out_shape.empty? ? result : result.reshape(*out_shape)
|
|
69
70
|
end
|
|
70
71
|
|
|
@@ -90,8 +91,22 @@ class CArray
|
|
|
90
91
|
# @raise [ArgumentError] on the same conditions as {#gather_nd}.
|
|
91
92
|
# @raise [IndexError] when a coordinate is out of range on any axis.
|
|
92
93
|
def put_nd (indices, values)
|
|
94
|
+
# The write goes through a flattened view, which carries a frozen
|
|
95
|
+
# array's read-only flag but not its frozenness; say what the array is.
|
|
96
|
+
raise FrozenError.new("can't modify frozen #{self.class}", receiver: self) if frozen?
|
|
93
97
|
flat_addr, _outer, _rest = gather_nd_flat_addr(indices, "put_nd")
|
|
94
|
-
|
|
98
|
+
if flat_addr.has_mask?
|
|
99
|
+
# A masked coordinate writes nothing. The values are laid out over
|
|
100
|
+
# the coordinates by the store itself, so they mean what they mean
|
|
101
|
+
# unmasked, and only the cells at unmasked coordinates are written.
|
|
102
|
+
flat = flatten
|
|
103
|
+
vals = flat.project(flat_addr)
|
|
104
|
+
vals[] = values
|
|
105
|
+
sel = flat_addr.is_not_masked
|
|
106
|
+
flat[flat_addr[sel]] = vals[sel]
|
|
107
|
+
else
|
|
108
|
+
self.flatten[flat_addr] = values
|
|
109
|
+
end
|
|
95
110
|
self
|
|
96
111
|
end
|
|
97
112
|
|
|
@@ -150,6 +165,11 @@ class CArray
|
|
|
150
165
|
flat_idx = (flat_idx_raw + dims) % dims
|
|
151
166
|
|
|
152
167
|
base = (flat_idx * strides).sum(axis: 1).int64.reshape(m) # (M,)
|
|
168
|
+
# sum skips a masked coordinate, which would leave its row pointing at
|
|
169
|
+
# the wrong cell: a row with any masked coordinate has no address.
|
|
170
|
+
if flat_idx_raw.has_mask?
|
|
171
|
+
base[flat_idx_raw.is_masked.any(axis: 1)] = UNDEF
|
|
172
|
+
end
|
|
153
173
|
|
|
154
174
|
flat_addr =
|
|
155
175
|
if rest.empty?
|
|
@@ -11,8 +11,8 @@ class CArray
|
|
|
11
11
|
# appends at the end) and a negative value counts from the end;
|
|
12
12
|
# `bsize[i]` must be non-negative. Inserted cells are filled by
|
|
13
13
|
# the block's return value, or left at the type's default when
|
|
14
|
-
# no block is given. Preserves fixlen bytes and Face identity
|
|
15
|
-
#
|
|
14
|
+
# no block is given. Preserves fixlen bytes and Face identity; the
|
|
15
|
+
# block returns a value of the array (a Time for a time array).
|
|
16
16
|
# The `offset` array is not mutated.
|
|
17
17
|
#
|
|
18
18
|
# @param offset [Array<Integer>] insertion offset per axis.
|
|
@@ -40,21 +40,16 @@ class CArray
|
|
|
40
40
|
# offset == dim (append) leaves nothing on the upper side to shift.
|
|
41
41
|
grids[i][offset[i]..-1].seq!(offset[i]+bsize[i]) if offset[i] < shape[i]
|
|
42
42
|
end
|
|
43
|
-
#
|
|
44
|
-
#
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
src = src.parent while src.face?
|
|
48
|
-
dt = src.data_type
|
|
49
|
-
bytes = (dt == :fixlen) ? src.bytes : nil
|
|
50
|
-
out = CArray.new(dt, newdim, bytes: bytes)
|
|
43
|
+
# The original cells are copied as storage, so a read-only Face inserts
|
|
44
|
+
# too; the block's value is written as a value of the array, which a
|
|
45
|
+
# read-only Face refuses.
|
|
46
|
+
out = blank_like(newdim)
|
|
51
47
|
if block_given?
|
|
52
|
-
sel =
|
|
53
|
-
sel[*grids] =
|
|
48
|
+
sel = CArray.boolean(*newdim) { true }
|
|
49
|
+
sel[*grids] = false
|
|
54
50
|
out[sel] = block.call
|
|
55
51
|
end
|
|
56
|
-
out[*grids] =
|
|
57
|
-
out = out.face_lift(face_parent) if face_parent
|
|
52
|
+
out.__send__(:face_storage)[*grids] = face_storage
|
|
58
53
|
return out
|
|
59
54
|
end
|
|
60
55
|
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
class CArray
|
|
2
|
+
|
|
3
|
+
# @!group Elementwise math
|
|
4
|
+
|
|
5
|
+
# @overload is_between(lo, hi, exclude: nil)
|
|
6
|
+
# @overload is_between(range)
|
|
7
|
+
# Returns a boolean CArray of the same shape as `self`, `true` at each
|
|
8
|
+
# cell whose value lies between `lo` and `hi`. Both bounds are included
|
|
9
|
+
# unless `exclude:` names the one to leave out:
|
|
10
|
+
#
|
|
11
|
+
# a.is_between(2, 4) # 2 <= a <= 4
|
|
12
|
+
# a.is_between(2, 4, exclude: :upper) # 2 <= a < 4
|
|
13
|
+
# a.is_between(2, 4, exclude: :lower) # 2 < a <= 4
|
|
14
|
+
# a.is_between(2, 4, exclude: :both) # 2 < a < 4
|
|
15
|
+
#
|
|
16
|
+
# A Range says the same as the two bounds, its excluded end as
|
|
17
|
+
# `exclude: :upper`: `a.is_between(2...4)`. A bound given as `nil` (or
|
|
18
|
+
# the open end of `..4` / `2..`) leaves that side unbounded.
|
|
19
|
+
#
|
|
20
|
+
# It is the two comparisons joined by `&`, so it takes what they take --
|
|
21
|
+
# a time array is bounded by Strings, `Time` or elements -- and answers as
|
|
22
|
+
# they do: a masked cell is masked, and NaN is outside every interval.
|
|
23
|
+
# Bounds the wrong way round (`lo > hi`) contain nothing.
|
|
24
|
+
# @param lo [Object, nil] the lower bound.
|
|
25
|
+
# @param hi [Object, nil] the upper bound.
|
|
26
|
+
# @param exclude [nil, :lower, :upper, :both] the bounds left out.
|
|
27
|
+
# @return [CArray] boolean, the shape of `self`.
|
|
28
|
+
# @raise [ArgumentError] when neither bound is given, `exclude:` is not one
|
|
29
|
+
# of the three, or a Range comes with a second bound or `exclude:`.
|
|
30
|
+
def is_between (*bounds, exclude: nil)
|
|
31
|
+
if bounds.size == 1 && bounds[0].is_a?(Range)
|
|
32
|
+
unless exclude.nil?
|
|
33
|
+
raise ArgumentError,
|
|
34
|
+
"is_between: a Range says which ends it includes; give two bounds to use exclude:"
|
|
35
|
+
end
|
|
36
|
+
range = bounds[0]
|
|
37
|
+
lo, hi = range.begin, range.end
|
|
38
|
+
exclude = :upper if range.exclude_end? && !hi.nil?
|
|
39
|
+
elsif bounds.size == 2
|
|
40
|
+
lo, hi = bounds
|
|
41
|
+
else
|
|
42
|
+
raise ArgumentError,
|
|
43
|
+
"is_between: give a lower and an upper bound, or a Range (got #{bounds.size} arguments)"
|
|
44
|
+
end
|
|
45
|
+
unless [nil, :lower, :upper, :both].include?(exclude)
|
|
46
|
+
raise ArgumentError,
|
|
47
|
+
"is_between: exclude: must be :lower, :upper or :both (got #{exclude.inspect})"
|
|
48
|
+
end
|
|
49
|
+
if lo.nil? && hi.nil?
|
|
50
|
+
raise ArgumentError, "is_between: give at least one bound"
|
|
51
|
+
end
|
|
52
|
+
lower = lo.nil? ? nil : (%i[lower both].include?(exclude) ? gt(lo) : ge(lo))
|
|
53
|
+
upper = hi.nil? ? nil : (%i[upper both].include?(exclude) ? lt(hi) : le(hi))
|
|
54
|
+
lower && upper ? lower & upper : (lower || upper)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# @!endgroup
|
|
58
|
+
|
|
59
|
+
end
|