carray 3.0.3 → 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 +264 -0
- data/README.md +3 -2
- data/carray.gemspec +1 -1
- data/ext/ca_axis_descriptor.h +27 -24
- data/ext/ca_axis_dispatch.c +1 -6
- data/ext/ca_axis_group.c +27 -27
- data/ext/ca_binop_dispatch.c +1 -29
- data/ext/ca_binop_dispatch.h +0 -1
- data/ext/ca_categorical_iterator.c +205 -134
- data/ext/ca_compare.c +7 -18
- data/ext/ca_group_iter.c +16 -17
- data/ext/ca_kernel_iterator.c +1234 -1994
- data/ext/ca_kernel_iterator.h +391 -437
- data/ext/ca_monop_dispatch.c +82 -59
- data/ext/ca_monop_dispatch.h +1 -0
- data/ext/ca_obj_array.c +96 -39
- data/ext/ca_obj_bincmp.c +24 -21
- data/ext/ca_obj_binop.c +15 -26
- data/ext/ca_obj_bitarray.c +3 -2
- data/ext/ca_obj_bitfield.c +5 -6
- data/ext/ca_obj_block.c +18 -9
- data/ext/ca_obj_byte_swap.c +5 -6
- data/ext/ca_obj_const_string.c +173 -19
- data/ext/ca_obj_face.c +238 -57
- data/ext/ca_obj_face.h +19 -0
- data/ext/ca_obj_fake.c +15 -29
- data/ext/ca_obj_farray.c +6 -3
- data/ext/ca_obj_field.c +17 -5
- data/ext/ca_obj_fixlen_string.c +2 -3
- data/ext/ca_obj_grid.c +406 -132
- data/ext/ca_obj_meld.c +172 -34
- data/ext/ca_obj_moncmp.c +23 -8
- data/ext/ca_obj_monop.c +67 -57
- data/ext/ca_obj_object.c +157 -51
- data/ext/ca_obj_record.c +12 -7
- data/ext/ca_obj_reduce.c +13 -8
- data/ext/ca_obj_refer.c +25 -27
- data/ext/ca_obj_remap.c +30 -9
- data/ext/ca_obj_repeat.c +9 -10
- data/ext/ca_obj_roll.c +6 -5
- data/ext/ca_obj_select.c +14 -9
- data/ext/ca_obj_select_axis.c +15 -47
- data/ext/ca_obj_shift.c +11 -1
- data/ext/ca_obj_stack.c +179 -34
- data/ext/ca_obj_stride.c +162 -115
- data/ext/ca_obj_string.c +71 -4
- data/ext/ca_obj_tile.c +6 -5
- data/ext/ca_obj_time.c +22 -6
- data/ext/ca_obj_timedelta.c +22 -6
- data/ext/ca_obj_transpose.c +5 -3
- data/ext/ca_obj_triop.c +23 -32
- data/ext/ca_obj_window.c +190 -26
- data/ext/ca_sweep_engine.c +2 -2
- data/ext/caframe_csv_reader.c +80 -1
- data/ext/carray.h +181 -75
- data/ext/carray_access.c +184 -96
- data/ext/carray_address_basis.c +343 -87
- data/ext/carray_attribute.c +89 -6
- data/ext/carray_bincount.c +10 -8
- data/ext/carray_broadcast.c +13 -8
- data/ext/carray_build_flags.h +3 -0
- data/ext/carray_call_cfunc.c +108 -0
- data/ext/carray_cast.c +142 -24
- data/ext/carray_cast_func.rb +46 -6
- data/ext/carray_class.c +4 -3
- data/ext/carray_config.h +28 -0
- data/ext/carray_conversion.c +16 -7
- data/ext/carray_copy.c +51 -14
- data/ext/carray_core.c +147 -55
- data/ext/carray_count.c +50 -20
- data/ext/carray_element.c +76 -33
- data/ext/carray_factorize.c +118 -64
- data/ext/carray_generate.c +191 -50
- data/ext/carray_histogram.c +62 -17
- data/ext/carray_hold.c +12 -15
- data/ext/carray_index_classifier.c +388 -176
- data/ext/carray_internal.h +336 -29
- data/ext/carray_kernels_bincmp.c +4453 -0
- data/ext/carray_kernels_binop.c +11368 -0
- data/ext/carray_kernels_init.c +1734 -0
- data/ext/carray_kernels_moncmp.c +2104 -0
- data/ext/carray_kernels_monop.c +18369 -0
- data/ext/carray_kernels_reduce_aggregate.c +23818 -0
- data/ext/carray_kernels_reduce_boolean.c +406 -0
- data/ext/carray_kernels_reduce_cumulative.c +10086 -0
- data/ext/carray_kernels_reduce_extreme.c +17634 -0
- data/ext/carray_kernels_reduce_variance.c +3849 -0
- data/ext/carray_kernels_scan.c +4302 -0
- data/ext/carray_kernels_search.c +33829 -0
- data/ext/carray_kernels_sort.c +10906 -0
- data/ext/carray_kernels_triop.c +1864 -0
- data/ext/carray_key_windows.c +216 -0
- data/ext/carray_lazy.c +170 -111
- data/ext/carray_mask.c +292 -72
- data/ext/carray_median_percentile.c +82 -28
- data/ext/carray_memory_view.c +669 -561
- data/ext/carray_operator.c +124 -47
- data/ext/carray_order.c +384 -76
- data/ext/carray_partition.c +28 -27
- data/ext/carray_random.c +67 -13
- data/ext/carray_round.c +273 -0
- data/ext/carray_scatter.c +63 -38
- data/ext/carray_segment.c +3 -3
- data/ext/carray_slab.c +91 -28
- data/ext/carray_sort.c +35 -35
- data/ext/carray_struct.c +19 -18
- data/ext/carray_test.c +26 -5
- data/ext/carray_undef.c +4 -0
- data/ext/carray_utils.c +246 -45
- data/ext/mk_call_cfunc.rb +19 -2
- data/ext/mkkernel.rb +198 -240
- data/ext/ruby_carray.c +10 -4
- data/ext/version.h +4 -4
- data/lib/carray/attribute.rb +126 -22
- data/lib/carray/autoload_carray.rb +8 -31
- data/lib/carray/autoload_method_extension.rb +14 -5
- data/lib/carray/axis_group.rb +255 -106
- data/lib/carray/basics.rb +78 -10
- data/lib/carray/bincount_nd.rb +12 -2
- data/lib/carray/block_iterator.rb +126 -23
- data/lib/carray/boolean_reduce.rb +17 -17
- data/lib/carray/categorical.rb +165 -43
- data/lib/carray/categorical_iterator.rb +130 -27
- data/lib/carray/complex.rb +5 -5
- data/lib/carray/conditional.rb +67 -18
- data/lib/carray/const_string.rb +14 -5
- 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/convert.rb +69 -0
- data/lib/carray/frame/csv_parser.rb +111 -17
- data/lib/carray/frame/describe.rb +3 -2
- data/lib/carray/frame/frame.rb +246 -21
- data/lib/carray/frame/group.rb +510 -95
- data/lib/carray/frame/io.rb +115 -28
- data/lib/carray/frame/join.rb +312 -51
- data/lib/carray/frame/records.rb +83 -7
- data/lib/carray/frame/reshape.rb +42 -35
- data/lib/carray/frame/sort.rb +7 -19
- data/lib/carray/frame/verbs.rb +188 -48
- data/lib/carray/frame/windows.rb +117 -0
- data/lib/carray/frame.rb +2 -0
- data/lib/carray/fuse_source.rb +11 -4
- data/lib/carray/fusion.rb +40 -16
- data/lib/carray/histogram.rb +6 -1
- data/lib/carray/inspect.rb +31 -8
- data/lib/carray/iterator.rb +85 -7
- data/lib/carray/key_windows.rb +620 -0
- data/lib/carray/lazy.rb +182 -183
- data/lib/carray/mask_gap_fill.rb +63 -57
- data/lib/carray/meld_reduce.rb +12 -14
- data/lib/carray/methods/bin.rb +26 -18
- data/lib/carray/methods/bincount.rb +16 -0
- data/lib/carray/methods/bit_string.rb +1 -1
- data/lib/carray/methods/choose.rb +16 -3
- data/lib/carray/methods/composition.rb +3 -1
- data/lib/carray/methods/gather_nd.rb +14 -5
- data/lib/carray/methods/insert_block.rb +2 -2
- data/lib/carray/methods/is_between.rb +59 -0
- data/lib/carray/methods/is_in.rb +53 -36
- data/lib/carray/methods/join.rb +1 -6
- data/lib/carray/methods/lag.rb +49 -0
- data/lib/carray/methods/locate_addr.rb +47 -51
- data/lib/carray/methods/mode.rb +1 -1
- data/lib/carray/methods/nunique.rb +11 -6
- data/lib/carray/methods/resize.rb +3 -3
- data/lib/carray/methods/value_counts.rb +4 -11
- data/lib/carray/record_order.rb +3 -3
- data/lib/carray/rng.rb +3 -0
- data/lib/carray/runtime.rb +53 -8
- data/lib/carray/segment_iterator.rb +313 -82
- data/lib/carray/serialize.rb +24 -10
- data/lib/carray/slab_iterator.rb +50 -0
- data/lib/carray/string.rb +105 -5
- data/lib/carray/string_operation_extension.rb +8 -1
- data/lib/carray/struct.rb +9 -21
- data/lib/carray/struct_builder.rb +19 -8
- data/lib/carray/time.rb +726 -104
- data/lib/carray/window_iterator.rb +122 -16
- data/lib/carray.rb +7 -0
- data/yard-stubs/ca_obj_bitfield.rb +1 -1
- data/yard-stubs/ca_obj_field.rb +4 -0
- data/yard-stubs/ca_obj_grid.rb +21 -10
- data/yard-stubs/ca_obj_refer.rb +2 -1
- data/yard-stubs/ca_obj_roll.rb +2 -2
- data/yard-stubs/ca_obj_shift.rb +5 -1
- data/yard-stubs/ca_obj_tile.rb +1 -1
- data/yard-stubs/ca_obj_window.rb +8 -3
- data/yard-stubs/carray_access.rb +6 -1
- data/yard-stubs/carray_attribute.rb +0 -5
- data/yard-stubs/carray_cast.rb +10 -0
- data/yard-stubs/carray_conversion.rb +2 -1
- data/yard-stubs/carray_copy.rb +4 -2
- data/yard-stubs/carray_count.rb +4 -4
- data/yard-stubs/carray_element.rb +10 -4
- data/yard-stubs/carray_generate.rb +21 -1
- data/yard-stubs/carray_mask.rb +26 -2
- data/yard-stubs/carray_math.rb +27 -1
- data/yard-stubs/carray_median_percentile.rb +5 -2
- data/yard-stubs/carray_memory_view.rb +40 -23
- data/yard-stubs/carray_order.rb +29 -18
- data/yard-stubs/carray_random.rb +13 -4
- data/yard-stubs/carray_scatter.rb +14 -9
- data/yard-stubs/carray_slab.rb +9 -2
- data/yard-stubs/carray_sort.rb +6 -5
- data/yard-stubs/carray_test.rb +5 -3
- metadata +25 -3
- data/lib/carray/arrow_tensor.rb +0 -401
- data/lib/carray/table.rb +0 -108
data/lib/carray/methods/is_in.rb
CHANGED
|
@@ -5,10 +5,11 @@ class CArray
|
|
|
5
5
|
# each cell whose value appears in the set `values`.
|
|
6
6
|
#
|
|
7
7
|
# `values` is treated as a set, not as an operand to broadcast: it
|
|
8
|
-
# may be any shape (or an Array
|
|
8
|
+
# may be any shape (or an Array) and is flattened to a
|
|
9
9
|
# single seen-set, so its shape need not match `self`. Only one
|
|
10
10
|
# argument is accepted; to test a few immediate values pass an
|
|
11
|
-
# Array (`a.is_in([0, -1])`).
|
|
11
|
+
# Array (`a.is_in([0, -1])`). A single value is a set of one, as
|
|
12
|
+
# `[value]` is (`a.is_in(2)` is `a.is_in([2])`).
|
|
12
13
|
#
|
|
13
14
|
# When `self` and `values` have different numeric data types they are
|
|
14
15
|
# promoted to a common type first (the same promotion binops use,
|
|
@@ -46,8 +47,10 @@ class CArray
|
|
|
46
47
|
# For a per-fiber "does this fiber contain any of these values"
|
|
47
48
|
# reduction, compose with {#any}: `a.is_in(values).any(axis: k)`.
|
|
48
49
|
#
|
|
49
|
-
# @param values [CArray, Array
|
|
50
|
+
# @param values [CArray, Array] the set to test membership
|
|
50
51
|
# against. Promoted with `self` to a common data type.
|
|
52
|
+
# @raise [ArgumentError] for a Range, which reads as an interval rather
|
|
53
|
+
# than the set of its steps.
|
|
51
54
|
# Between two time arrays the question is about instants, not ticks:
|
|
52
55
|
# `values` is reconciled into `self`'s unit first, so a `:D` array and an
|
|
53
56
|
# `:h` array match on the instants they share. The same holds for the set
|
|
@@ -55,6 +58,7 @@ class CArray
|
|
|
55
58
|
#
|
|
56
59
|
# @return [CArray] boolean CArray of the same shape as `self`.
|
|
57
60
|
def is_in (values)
|
|
61
|
+
refuse_range_set(values, "is_in")
|
|
58
62
|
a, b = promote_value_set(values)
|
|
59
63
|
a.__send__(:__is_in__, b)
|
|
60
64
|
end
|
|
@@ -69,11 +73,12 @@ class CArray
|
|
|
69
73
|
# always flat, like {#unique}, because the distinct values of a
|
|
70
74
|
# fiber vary in number.
|
|
71
75
|
#
|
|
72
|
-
# @param other [CArray, Array
|
|
76
|
+
# @param other [CArray, Array] promoted with `self` (a Range is refused, as by {#is_in}).
|
|
73
77
|
# @param sort [Boolean] when true, return the values sorted
|
|
74
78
|
# ascending instead of in first-appearance order.
|
|
75
79
|
# @return [CArray] 1-D CArray of the common distinct values.
|
|
76
80
|
def intersection (other, sort: false)
|
|
81
|
+
refuse_range_set(other, "intersection")
|
|
77
82
|
a, b = promote_value_set(other)
|
|
78
83
|
r = a.__send__(:__intersection__, b)
|
|
79
84
|
r = r.to_unit(unit) if !a.equal?(self) && respond_to?(:in_common_unit_with)
|
|
@@ -85,10 +90,11 @@ class CArray
|
|
|
85
90
|
# absent from `other`, in `self`'s first-appearance order.
|
|
86
91
|
# See {#intersection} for the shared semantics and options.
|
|
87
92
|
#
|
|
88
|
-
# @param other [CArray, Array
|
|
93
|
+
# @param other [CArray, Array] promoted with `self` (a Range is refused, as by {#is_in}).
|
|
89
94
|
# @param sort [Boolean] when true, return the values sorted ascending.
|
|
90
95
|
# @return [CArray] 1-D CArray of the self-only distinct values.
|
|
91
96
|
def difference (other, sort: false)
|
|
97
|
+
refuse_range_set(other, "difference")
|
|
92
98
|
a, b = promote_value_set(other)
|
|
93
99
|
r = a.__send__(:__difference__, b)
|
|
94
100
|
r = r.to_unit(unit) if !a.equal?(self) && respond_to?(:in_common_unit_with)
|
|
@@ -103,11 +109,12 @@ class CArray
|
|
|
103
109
|
# holds the values of both ({#intersection} and {#difference} answer in
|
|
104
110
|
# `self`'s unit, since their values are `self`'s).
|
|
105
111
|
#
|
|
106
|
-
# @param other [CArray, Array
|
|
112
|
+
# @param other [CArray, Array] promoted with `self` (a Range is refused, as by {#is_in}).
|
|
107
113
|
# @param sort [Boolean] when true, return the values sorted ascending
|
|
108
114
|
# (a merged, ordered set — e.g. a common time axis).
|
|
109
115
|
# @return [CArray] 1-D CArray of the combined distinct values.
|
|
110
116
|
def union (other, sort: false)
|
|
117
|
+
refuse_range_set(other, "union")
|
|
111
118
|
a, b = promote_value_set(other)
|
|
112
119
|
r = a.__send__(:__union__, b)
|
|
113
120
|
sort ? r.sort : r
|
|
@@ -115,6 +122,16 @@ class CArray
|
|
|
115
122
|
|
|
116
123
|
private
|
|
117
124
|
|
|
125
|
+
# A Range reads as an interval in Ruby, which a set of its steps is not
|
|
126
|
+
# (2.5 is not in [2, 3, 4]), so the set-valued verbs refuse one rather
|
|
127
|
+
# than answer for the steps.
|
|
128
|
+
def refuse_range_set (values, verb)
|
|
129
|
+
return unless values.is_a?(Range)
|
|
130
|
+
raise ArgumentError,
|
|
131
|
+
"#{verb}: a Range is not taken as a set; pass its values as an Array " \
|
|
132
|
+
"((#{values.inspect}).to_a), or test an interval with is_between(#{values.inspect})"
|
|
133
|
+
end
|
|
134
|
+
|
|
118
135
|
# Reconcile a set-valued argument with self to a common data_type via
|
|
119
136
|
# CArray.result_type (the single-source promotion rule the eager binop and
|
|
120
137
|
# lazy CABinOp share), returning [self', set']. Only data types are reconciled,
|
|
@@ -141,36 +158,38 @@ class CArray
|
|
|
141
158
|
return fixlen_widened_with(values)
|
|
142
159
|
end
|
|
143
160
|
# A Face that reconciles operands (CATime) reads a bare Array as values
|
|
144
|
-
# of its own kind -- instants written as Time / String / elements
|
|
145
|
-
|
|
146
|
-
|
|
161
|
+
# of its own kind -- instants written as Time / String / elements -- and
|
|
162
|
+
# meets them as a comparison does (comparable_pair).
|
|
163
|
+
if values.is_a?(Array) && face? && respond_to?(:comparable_pair)
|
|
164
|
+
return comparable_pair(values)
|
|
147
165
|
end
|
|
148
166
|
# A single value (a Time, an element, a bare number) is a set of one,
|
|
149
167
|
# read the same way; a bare number is refused like any other operand
|
|
150
168
|
# that is not of its kind.
|
|
151
169
|
if !values.is_a?(CArray) && !values.is_a?(Range) && face? &&
|
|
152
|
-
respond_to?(:
|
|
153
|
-
return
|
|
170
|
+
respond_to?(:comparable_pair)
|
|
171
|
+
return comparable_pair([values])
|
|
154
172
|
end
|
|
155
173
|
# ...and so does an array of values that are not of its kind (an object
|
|
156
174
|
# column of Time values, a string column).
|
|
157
|
-
if values.is_a?(CArray) && respond_to?(:
|
|
158
|
-
!values.respond_to?(:
|
|
159
|
-
return
|
|
175
|
+
if values.is_a?(CArray) && respond_to?(:comparable_pair) &&
|
|
176
|
+
!values.respond_to?(:comparable_pair)
|
|
177
|
+
return comparable_pair(values)
|
|
160
178
|
end
|
|
161
179
|
# The same from the other side: values of a time array looked for in an
|
|
162
180
|
# array that is not one.
|
|
163
|
-
if values.is_a?(CArray) && values.respond_to?(:
|
|
164
|
-
!respond_to?(:
|
|
165
|
-
|
|
181
|
+
if values.is_a?(CArray) && values.respond_to?(:comparable_pair) &&
|
|
182
|
+
!respond_to?(:comparable_pair)
|
|
183
|
+
set, own = values.comparable_pair(self)
|
|
184
|
+
return [own, set]
|
|
166
185
|
end
|
|
167
186
|
# A Face whose storage is not of its surface type. When its cells are
|
|
168
187
|
# its values, the set meets it as it would meet an array of the storage
|
|
169
188
|
# type, and the Face stays while that type does not change. Any other
|
|
170
189
|
# Face leaves the set to the lookup, which brings it into the Face's
|
|
171
190
|
# space or refuses it.
|
|
172
|
-
if face? && (storage =
|
|
173
|
-
unless
|
|
191
|
+
if face? && (storage = face_storage).data_type != data_type
|
|
192
|
+
unless comparable_storage?
|
|
174
193
|
set = values.is_a?(Range) ? values.to_a : values
|
|
175
194
|
return [self, set.is_a?(CArray) ? set : CArray.wrap_readonly(set)]
|
|
176
195
|
end
|
|
@@ -181,9 +200,16 @@ class CArray
|
|
|
181
200
|
when Array then return promote_elements(values)
|
|
182
201
|
when Range then return promote_elements(values.to_a)
|
|
183
202
|
end
|
|
203
|
+
# A single value is a set of one, as [value] is. wrap_readonly would make
|
|
204
|
+
# it an object array, which takes self into the object lane, where 2.0 is
|
|
205
|
+
# not eql? 2.
|
|
206
|
+
if !values.is_a?(CArray) && !values.respond_to?(:to_ca) &&
|
|
207
|
+
!CArray.memory_view_available?(values)
|
|
208
|
+
return promote_elements([values])
|
|
209
|
+
end
|
|
184
210
|
set = values.is_a?(CArray) ? values : CArray.wrap_readonly(values)
|
|
185
211
|
return [self, set] if set.data_type == data_type # common fast path
|
|
186
|
-
t =
|
|
212
|
+
t = query_match_type(set)
|
|
187
213
|
[coerce_self(t), set.to_type(t)]
|
|
188
214
|
end
|
|
189
215
|
|
|
@@ -200,12 +226,17 @@ class CArray
|
|
|
200
226
|
# set for not matching self. Same rule the scalar operand of a
|
|
201
227
|
# comparison follows: a String standing in for a cell is padded to the
|
|
202
228
|
# cell's width.
|
|
229
|
+
# coerce: a fixlen or object set holds values of self's kind (see above)
|
|
203
230
|
[self, elems.to_ca.to_type(data_type, bytes: bytes)]
|
|
204
231
|
elsif data_type == CA_OBJECT
|
|
232
|
+
# coerce: an object set holds values of self's kind
|
|
205
233
|
[self, elems.to_ca.to_type(data_type)]
|
|
234
|
+
elsif elems.any? { |v| v.is_a?(String) || v.is_a?(Symbol) }
|
|
235
|
+
# A String is a value, which result_type would read as the name of a
|
|
236
|
+
# data type; a numeric self meets it in the object lane.
|
|
237
|
+
[to_type(CA_OBJECT), elems.to_ca.to_type(CA_OBJECT)]
|
|
206
238
|
else
|
|
207
239
|
t = CArray.result_type(self, *elems)
|
|
208
|
-
t = value_match_type(t, CA_INT64) if elems.any? { |v| v.is_a?(Integer) && v < 0 }
|
|
209
240
|
[coerce_self(t), elems.to_ca.to_type(t)]
|
|
210
241
|
end
|
|
211
242
|
end
|
|
@@ -216,7 +247,7 @@ class CArray
|
|
|
216
247
|
# their values; anything else, or the same kind of array as self, is
|
|
217
248
|
# taken as it is. A record is left to be refused.
|
|
218
249
|
def values_for_lookup (other)
|
|
219
|
-
return other if other.send(:
|
|
250
|
+
return other if other.send(:orderable_storage?)
|
|
220
251
|
return other if face? && other.class == self.class
|
|
221
252
|
if other.respond_to?(:to_string)
|
|
222
253
|
other.to_string
|
|
@@ -244,20 +275,6 @@ class CArray
|
|
|
244
275
|
x.bytes == w ? x : x.to_fixlen_string(bytes: w)
|
|
245
276
|
end
|
|
246
277
|
|
|
247
|
-
# The values of +other+, which has no to_comparable, read as values of +face+'s
|
|
248
|
-
# kind (instants written as Time / Date / String for a time key), in an
|
|
249
|
-
# array of +other+'s shape; a masked cell stays masked. nil when every
|
|
250
|
-
# cell is masked: there is nothing to read.
|
|
251
|
-
private def values_in_face(face, other)
|
|
252
|
-
return face.to_comparable(other) if other.is_a?(Array)
|
|
253
|
-
vals = other.flatten.to_a
|
|
254
|
-
filler = vals.find { |v| !UNDEF.equal?(v) }
|
|
255
|
-
return nil if filler.equal?(nil) && vals.all? { |v| UNDEF.equal?(v) }
|
|
256
|
-
out = face.to_comparable(vals.map { |v| UNDEF.equal?(v) ? filler : v })
|
|
257
|
-
out[other.is_masked.flatten] = UNDEF if other.has_mask?
|
|
258
|
-
out.reshape(*other.shape)
|
|
259
|
-
end
|
|
260
|
-
|
|
261
278
|
def coerce_self (t)
|
|
262
279
|
CArray.result_type(self) == t ? self : to_type(t)
|
|
263
280
|
end
|
data/lib/carray/methods/join.rb
CHANGED
|
@@ -46,12 +46,7 @@ class CArray
|
|
|
46
46
|
return sep.nil? ? to_a.join : to_a.join(sep)
|
|
47
47
|
end
|
|
48
48
|
|
|
49
|
-
ax =
|
|
50
|
-
ax += ndim if ax < 0
|
|
51
|
-
if ax < 0 || ax >= ndim
|
|
52
|
-
raise ArgumentError,
|
|
53
|
-
"axis #{axis.inspect} out of range for ndim=#{ndim}"
|
|
54
|
-
end
|
|
49
|
+
ax = normalize_axis(axis, "join")
|
|
55
50
|
|
|
56
51
|
sep_str = sep || ""
|
|
57
52
|
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
class CArray
|
|
2
|
+
|
|
3
|
+
# @overload lag(n = 1, axis: nil)
|
|
4
|
+
# Returns the value `n` steps earlier along `axis`, cell by cell: cell
|
|
5
|
+
# `i` of the result is cell `i - n` of `self`. A cell with no value `n`
|
|
6
|
+
# steps back is UNDEF, so a difference against it is UNDEF rather than
|
|
7
|
+
# a difference against a stand-in value:
|
|
8
|
+
#
|
|
9
|
+
# ```
|
|
10
|
+
# x = CA_FLOAT64([1.0, 4.0, 9.0])
|
|
11
|
+
# x.lag # => [UNDEF, 1.0, 4.0]
|
|
12
|
+
# x - x.lag # => [UNDEF, 3.0, 5.0]
|
|
13
|
+
# x.lag(-1) # => [4.0, 9.0, UNDEF] (the value one step later)
|
|
14
|
+
# ```
|
|
15
|
+
#
|
|
16
|
+
# The cell `n` steps back is taken by position: when it is masked, the
|
|
17
|
+
# result is UNDEF there too. It is `shift` with `UNDEF` as the fill
|
|
18
|
+
# value, and returns the same kind of view: it shares storage with
|
|
19
|
+
# `self`. Use `copy` for an independent array.
|
|
20
|
+
#
|
|
21
|
+
# The steps run along one axis. A 1-D array has only one, so `axis:`
|
|
22
|
+
# may be left out; an array of more dimensions must name it, since
|
|
23
|
+
# stepping back through the cells in flatten order would carry a value
|
|
24
|
+
# across from the end of one row to the start of the next.
|
|
25
|
+
#
|
|
26
|
+
# @param n [Integer] steps back; a negative `n` looks ahead.
|
|
27
|
+
# @param axis [Integer, nil] the axis the steps run along.
|
|
28
|
+
# @return [CArray] a view, shaped like `self`.
|
|
29
|
+
# @raise [TypeError] when `n` or `axis` is not an Integer.
|
|
30
|
+
# @raise [ArgumentError] when `axis` is out of range, or left out for an
|
|
31
|
+
# array of more than one dimension.
|
|
32
|
+
def lag (n = 1, axis: nil)
|
|
33
|
+
unless n.is_a?(Integer)
|
|
34
|
+
raise TypeError, "lag: n must be an Integer (got #{n.class})"
|
|
35
|
+
end
|
|
36
|
+
if axis.nil?
|
|
37
|
+
unless ndim == 1
|
|
38
|
+
raise ArgumentError,
|
|
39
|
+
"lag: name the axis to step along (axis:) for an array of " \
|
|
40
|
+
"#{ndim} dimensions"
|
|
41
|
+
end
|
|
42
|
+
axis = 0
|
|
43
|
+
end
|
|
44
|
+
steps = Array.new(ndim, 0)
|
|
45
|
+
steps[normalize_axis(axis, "lag")] = n
|
|
46
|
+
shift(*steps, fill_value: UNDEF)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
end
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
require "carray/methods/is_in" # fixlen_widened_with,
|
|
1
|
+
require "carray/methods/is_in" # fixlen_widened_with, values_for_lookup
|
|
2
|
+
require "carray/distance_limit"
|
|
2
3
|
|
|
3
4
|
class CArray
|
|
4
5
|
|
|
@@ -42,10 +43,9 @@ class CArray
|
|
|
42
43
|
# variables without repeating the lookup.
|
|
43
44
|
#
|
|
44
45
|
# @param ref [CArray, Array, Range] reference values to match against;
|
|
45
|
-
# any shape (used as flat). An Array or Range is
|
|
46
|
-
#
|
|
47
|
-
# `
|
|
48
|
-
# numeric lane.
|
|
46
|
+
# any shape (used as flat). An Array or Range is the list of its
|
|
47
|
+
# values, met at a common type with `self` as {#is_in} meets one, so
|
|
48
|
+
# `2` finds `2.0`.
|
|
49
49
|
# @return [CArray] `:int64` flat addresses into `ref`, same shape as
|
|
50
50
|
# `self`; unmatched cells are masked.
|
|
51
51
|
# @raise [CArray::DataTypeError] when `self` and `ref` have no common data type.
|
|
@@ -58,30 +58,35 @@ class CArray
|
|
|
58
58
|
a, b = in_common_unit_with(ref)
|
|
59
59
|
return a.locate_addr(b)
|
|
60
60
|
end
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
return
|
|
61
|
+
# They meet as a comparison does (comparable_pair).
|
|
62
|
+
if respond_to?(:comparable_pair) && !ref.respond_to?(:comparable_pair)
|
|
63
|
+
key, r = comparable_pair(ref)
|
|
64
|
+
return key.locate_addr(r)
|
|
65
|
+
elsif ref.respond_to?(:comparable_pair) && !respond_to?(:comparable_pair)
|
|
66
|
+
r, key = ref.comparable_pair(self)
|
|
67
|
+
return key.locate_addr(r)
|
|
68
68
|
end
|
|
69
69
|
unless ref.is_a?(CArray) || ref.respond_to?(:to_ca)
|
|
70
70
|
raise TypeError, "locate_addr: the reference is an array of values (got #{ref.class})"
|
|
71
71
|
end
|
|
72
|
+
# An Array or Range of values meets self as is_in's does: the common type
|
|
73
|
+
# comes from self and the elements, so 2 finds 2.0 in a float array. The
|
|
74
|
+
# conversion keeps the order, so the addresses stay positions in ref.
|
|
75
|
+
if ref.is_a?(Array) || ref.is_a?(Range)
|
|
76
|
+
q, r = promote_elements(ref.is_a?(Range) ? ref.to_a : ref)
|
|
77
|
+
return q.send(:__locate_addr__, r)
|
|
78
|
+
end
|
|
72
79
|
ref = ref.to_ca unless ref.is_a?(CArray)
|
|
73
80
|
ref = values_for_lookup(ref)
|
|
74
81
|
if data_type == CA_FIXLEN && ref.data_type == CA_FIXLEN && bytes != ref.bytes
|
|
75
82
|
a, b = fixlen_widened_with(ref)
|
|
76
83
|
return a.locate_addr(b) unless a.equal?(self) && b.equal?(ref)
|
|
77
84
|
end
|
|
78
|
-
# Put self and ref in
|
|
79
|
-
#
|
|
80
|
-
#
|
|
81
|
-
#
|
|
82
|
-
|
|
83
|
-
# pair of mixed sign is matched by value (see value_match_type).
|
|
84
|
-
t = value_match_type(CArray.result_type(self, ref), ref.data_type)
|
|
85
|
+
# Put self and ref in the type they match in (query_match_type), so a
|
|
86
|
+
# fractional query against an int ref is compared as a float (1.5 does
|
|
87
|
+
# not match 1) and a pair of mixed sign by value. to_type is elementwise
|
|
88
|
+
# and order-preserving, so the addresses stay valid indices into ref.
|
|
89
|
+
t = ref.send(:query_match_type, self)
|
|
85
90
|
q = (data_type == t) ? self : to_type(t)
|
|
86
91
|
r = (ref.data_type == t) ? ref : ref.to_type(t)
|
|
87
92
|
q.send(:__locate_addr__, r)
|
|
@@ -137,6 +142,8 @@ class CArray
|
|
|
137
142
|
"locate_nearest_addr: direction must be :round / :floor / " \
|
|
138
143
|
":ceil (got #{direction.inspect})"
|
|
139
144
|
end
|
|
145
|
+
[self, ref].each { |key| nearest_refuse_bytes(key) }
|
|
146
|
+
limit = tolerance && DistanceLimit.read(tolerance, self, "tolerance:")
|
|
140
147
|
return CArray.int64(*shape) if elements == 0
|
|
141
148
|
# A masked reference value matches nothing, as in locate_addr: sorting
|
|
142
149
|
# puts the masked ones last, and only the others are searched. A NaN
|
|
@@ -169,13 +176,29 @@ class CArray
|
|
|
169
176
|
si = si.reshape(*shape)
|
|
170
177
|
si[is_masked] = UNDEF if has_mask?
|
|
171
178
|
idx = ri.project(si)
|
|
172
|
-
if
|
|
179
|
+
if limit
|
|
173
180
|
dist = (ref.project(idx) - self).abs
|
|
174
|
-
idx[dist >
|
|
181
|
+
idx[dist > limit] = UNDEF
|
|
175
182
|
end
|
|
176
183
|
idx
|
|
177
184
|
end
|
|
178
185
|
|
|
186
|
+
# A key held as fixed-length bytes (raw fixlen, or a fixlen string) has
|
|
187
|
+
# no nearest: the search counts keys as numbers. A Face whose storage is
|
|
188
|
+
# a number (a time) is searched by that number.
|
|
189
|
+
# +subject+ and +remedy+ let a caller (CAFrame#join_asof) say it in its
|
|
190
|
+
# own words.
|
|
191
|
+
private def nearest_refuse_bytes(key, subject = nil, remedy = "match exact values with locate_addr")
|
|
192
|
+
# A Face whose cells encode the value (a const string, a categorical)
|
|
193
|
+
# is refused later by its own reconcile, which says why.
|
|
194
|
+
return unless key.is_a?(CArray) && key.__send__(:orderable_storage?)
|
|
195
|
+
storage = key.face? ? key.__send__(:face_storage) : key
|
|
196
|
+
return unless storage.data_type == CA_FIXLEN
|
|
197
|
+
subject ||= "locate_nearest_addr: a #{CArray::Inspector.type_label(key)} key"
|
|
198
|
+
raise CArray::DataTypeError,
|
|
199
|
+
"#{subject} is held as fixed-length bytes, which have no nearest; #{remedy}"
|
|
200
|
+
end
|
|
201
|
+
|
|
179
202
|
# The keys locate_nearest_addr orders by: the storage of the sorted
|
|
180
203
|
# reference and of the query, in one numeric type. A Face key is first
|
|
181
204
|
# brought into the reference's space (two time keys of different units
|
|
@@ -189,10 +212,10 @@ class CArray
|
|
|
189
212
|
rs = rs.to_unit(u)
|
|
190
213
|
q = q.to_unit(u)
|
|
191
214
|
end
|
|
192
|
-
q = rs.__send__(:
|
|
193
|
-
rs = rs.__send__(:
|
|
215
|
+
q = rs.__send__(:reconcile_operand, q, "locate_nearest_addr")
|
|
216
|
+
rs = rs.__send__(:face_storage)
|
|
194
217
|
end
|
|
195
|
-
t =
|
|
218
|
+
t = rs.send(:query_match_type, q)
|
|
196
219
|
rk = (rs.data_type == t) ? rs : rs.to_type(t)
|
|
197
220
|
qk = (q.data_type == t) ? q : q.to_type(t)
|
|
198
221
|
[rk, qk.flatten]
|
|
@@ -234,33 +257,6 @@ class CArray
|
|
|
234
257
|
si
|
|
235
258
|
end
|
|
236
259
|
|
|
237
|
-
# The tolerance in the units of the distance. A time key measures its
|
|
238
|
-
# distance as a CATimedelta, so its tolerance is a duration: a String in
|
|
239
|
-
# the spelling CArray.time takes for a unit ("10 minutes"), or a
|
|
240
|
-
# CATimedelta value. A bare number would mean a different length in each
|
|
241
|
-
# unit, so it is refused there; any other key takes a number.
|
|
242
|
-
private def nearest_tolerance(tolerance, dist)
|
|
243
|
-
unless dist.is_a?(CATimedelta)
|
|
244
|
-
if tolerance.is_a?(String)
|
|
245
|
-
raise ArgumentError,
|
|
246
|
-
"tolerance: #{tolerance.inspect} is a duration, but the key is " \
|
|
247
|
-
"#{data_type_name}; give a number"
|
|
248
|
-
end
|
|
249
|
-
return tolerance
|
|
250
|
-
end
|
|
251
|
-
case tolerance
|
|
252
|
-
when String
|
|
253
|
-
res = CATime::Resolution.parse(tolerance)
|
|
254
|
-
CATimedelta::Element.new(res.count, res.base)
|
|
255
|
-
when CATimedelta::Element
|
|
256
|
-
tolerance
|
|
257
|
-
else
|
|
258
|
-
raise ArgumentError,
|
|
259
|
-
"tolerance: a time key needs a duration such as \"10 minutes\" " \
|
|
260
|
-
"or a CATimedelta value (got #{tolerance.inspect})"
|
|
261
|
-
end
|
|
262
|
-
end
|
|
263
|
-
|
|
264
260
|
# @!endgroup
|
|
265
261
|
|
|
266
262
|
end
|
data/lib/carray/methods/mode.rb
CHANGED
|
@@ -74,7 +74,7 @@ class CArray
|
|
|
74
74
|
# reduced CArrays, one per mode rank (per-axis).
|
|
75
75
|
def mode (axis: nil)
|
|
76
76
|
# An error from the methods mode is made of (sort, ...) names mode.
|
|
77
|
-
CArray.
|
|
77
|
+
CArray.__name_errors_after_method__(:mode) { mode_answered(axis) }
|
|
78
78
|
end
|
|
79
79
|
|
|
80
80
|
private def mode_answered (axis)
|
|
@@ -12,7 +12,9 @@ class CArray
|
|
|
12
12
|
# whole array and returns an Integer. With `axis: k` it counts per
|
|
13
13
|
# fiber along axis `k`, returning a reduced `CA_INT64` CArray
|
|
14
14
|
# (shape = self.shape with axis `k` removed; `keep_axis: true` keeps
|
|
15
|
-
# it as a length-1 axis).
|
|
15
|
+
# it as a length-1 axis). An Array of axes counts over all of them
|
|
16
|
+
# together, one count per remaining position; naming every axis
|
|
17
|
+
# returns an Integer. The shape rule matches other per-axis
|
|
16
18
|
# reductions such as `sum(axis:)`.
|
|
17
19
|
#
|
|
18
20
|
# The distinct count has identity 0: an empty array, an all-masked
|
|
@@ -25,12 +27,12 @@ class CArray
|
|
|
25
27
|
# distinctness follows Ruby `eql?` / `hash` (which, unlike numeric,
|
|
26
28
|
# does not collapse distinct NaN objects).
|
|
27
29
|
#
|
|
28
|
-
# @param axis [Integer, nil] axis to count
|
|
29
|
-
# the whole array.
|
|
30
|
+
# @param axis [Integer, Array<Integer>, nil] axis or axes to count
|
|
31
|
+
# over; `nil` counts over the whole array.
|
|
30
32
|
# @param keep_axis [Boolean] when `axis` is given, keep the reduced
|
|
31
33
|
# axis as a length-1 axis instead of dropping it.
|
|
32
|
-
# @return [Integer, CArray] Integer for `axis: nil
|
|
33
|
-
# reduced `CA_INT64` CArray.
|
|
34
|
+
# @return [Integer, CArray] Integer for `axis: nil` or every axis,
|
|
35
|
+
# otherwise a reduced `CA_INT64` CArray.
|
|
34
36
|
# @overload nunique(along: k)
|
|
35
37
|
# Returns how many distinct **sub-arrays** `self` holds, comparing
|
|
36
38
|
# whole sub-arrays rather than cells: `along: k` names the axis
|
|
@@ -60,7 +62,10 @@ class CArray
|
|
|
60
62
|
# then read the single reduced cell as an Integer.
|
|
61
63
|
flatten.send(:__nunique__, 0, false)[0]
|
|
62
64
|
else
|
|
63
|
-
|
|
65
|
+
axes = Array(axis).map { |a| normalize_axis(a, "nunique") }
|
|
66
|
+
counts = __nunique__(axes, keep_axis)
|
|
67
|
+
# naming every axis reduces to one count, an Integer as sum(axis:) gives
|
|
68
|
+
(axes.size == ndim && ! keep_axis) ? counts[0] : counts
|
|
64
69
|
end
|
|
65
70
|
end
|
|
66
71
|
|
|
@@ -43,11 +43,11 @@ class CArray
|
|
|
43
43
|
# const string, a categorical) resizes too. The new area starts as zero
|
|
44
44
|
# storage; a given fill_value is written as a value of the array, which a
|
|
45
45
|
# read-only Face refuses.
|
|
46
|
-
out =
|
|
47
|
-
out_storage = out.__send__(:
|
|
46
|
+
out = blank_like(newdim)
|
|
47
|
+
out_storage = out.__send__(:face_storage)
|
|
48
48
|
out[] = fill_value unless fill_value.equal?(NO_FILL_VALUE)
|
|
49
49
|
out_storage.mask.paste(offset, self.false) if out_storage.has_mask?
|
|
50
|
-
out_storage.paste(offset,
|
|
50
|
+
out_storage.paste(offset, face_storage)
|
|
51
51
|
out
|
|
52
52
|
end
|
|
53
53
|
|
|
@@ -52,17 +52,10 @@ class CArray
|
|
|
52
52
|
[ values[CArray.int64(order.size) { |i| order[i] }],
|
|
53
53
|
counts[CArray.int64(order.size) { |i| order[i] }] ]
|
|
54
54
|
when :value
|
|
55
|
-
# Ascending value
|
|
56
|
-
#
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
order = (0...v.size).sort_by do |i|
|
|
60
|
-
x = v[i]
|
|
61
|
-
nan = x.is_a?(Float) && x.nan?
|
|
62
|
-
[nan ? 1 : 0, nan ? 0 : x, i]
|
|
63
|
-
end
|
|
64
|
-
[ values[CArray.int64(order.size) { |i| order[i] }],
|
|
65
|
-
counts[CArray.int64(order.size) { |i| order[i] }] ]
|
|
55
|
+
# Ascending value in the order sort uses (NaN after every number),
|
|
56
|
+
# then gather both arrays through it.
|
|
57
|
+
order = values.sort_index
|
|
58
|
+
[values[order], counts[order]]
|
|
66
59
|
else
|
|
67
60
|
[values, counts]
|
|
68
61
|
end
|
data/lib/carray/record_order.rb
CHANGED
|
@@ -49,7 +49,7 @@ class CARecord
|
|
|
49
49
|
# @return [CArray] the indices that partition about the `kth` record.
|
|
50
50
|
[:sort_addr, :sort_index, :rank_index, :order,
|
|
51
51
|
:min_index, :max_index, :partition_index].each do |op|
|
|
52
|
-
define_method(op
|
|
52
|
+
define_method(op, &Ractor.make_shareable(proc { |*args, **kw| record_rank.public_send(op, *args, **kw) }))
|
|
53
53
|
end
|
|
54
54
|
|
|
55
55
|
# @overload min(axis: nil, keep_axis: false)
|
|
@@ -93,10 +93,10 @@ class CARecord
|
|
|
93
93
|
# @!method gt(other)
|
|
94
94
|
# @!method ge(other)
|
|
95
95
|
[:lt, :le, :gt, :ge].each do |op|
|
|
96
|
-
define_method(op
|
|
96
|
+
define_method(op, &Ractor.make_shareable(proc do |other|
|
|
97
97
|
mine, theirs = record_rank_with(other)
|
|
98
98
|
mine.public_send(op, theirs)
|
|
99
|
-
end
|
|
99
|
+
end))
|
|
100
100
|
end
|
|
101
101
|
|
|
102
102
|
alias < lt
|
data/lib/carray/rng.rb
CHANGED
|
@@ -29,6 +29,7 @@ class CArray
|
|
|
29
29
|
:xoshiro256pp => File.expand_path("../../ext/ca_rng_xoshiro256pp.h",
|
|
30
30
|
__dir__),
|
|
31
31
|
}.freeze
|
|
32
|
+
Ractor.make_shareable(SOURCE_FILES)
|
|
32
33
|
|
|
33
34
|
# The C each generator is, as text.
|
|
34
35
|
#
|
|
@@ -50,6 +51,7 @@ class CArray
|
|
|
50
51
|
SOURCE = SOURCE_FILES.transform_values { |path|
|
|
51
52
|
File.read(path, :encoding => "UTF-8")
|
|
52
53
|
}.freeze
|
|
54
|
+
Ractor.make_shareable(SOURCE)
|
|
53
55
|
|
|
54
56
|
# What every generator's text needs beside it, and what none of them
|
|
55
57
|
# owns: turning uniforms into a normal is the same arithmetic whichever
|
|
@@ -80,6 +82,7 @@ class CArray
|
|
|
80
82
|
:bits => "ca_xoshiro256pp_next",
|
|
81
83
|
}.freeze,
|
|
82
84
|
}.freeze
|
|
85
|
+
Ractor.make_shareable(DRAW_FUNCTIONS)
|
|
83
86
|
|
|
84
87
|
end
|
|
85
88
|
|