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/is_in.rb
CHANGED
|
@@ -5,18 +5,22 @@ 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,
|
|
15
16
|
# {CArray.result_type}), so membership is value-correct across
|
|
16
17
|
# data types (e.g. an int cell equals a float set element of the same
|
|
17
18
|
# value, and a fractional set element never truncates onto an int
|
|
18
|
-
# cell).
|
|
19
|
-
#
|
|
19
|
+
# cell). Integers of mixed sign are matched by value, as {#eq}
|
|
20
|
+
# compares them: `-1` in an int8 array never matches `255` in a uint8
|
|
21
|
+
# one, and the set operations answer such a pair in a type that holds
|
|
22
|
+
# both (int16 for uint8 with int8, object for uint64 with int64).
|
|
23
|
+
# Genuinely incompatible data types (e.g. numeric vs fixlen) raise.
|
|
20
24
|
#
|
|
21
25
|
# Membership is value-based and shares the distinctness of the
|
|
22
26
|
# value-hash discovery family ({#unique} / {#value_counts}):
|
|
@@ -27,6 +31,15 @@ class CArray
|
|
|
27
31
|
# (so a 5-byte array written from `"be"` is `is_in(["be"])`). A
|
|
28
32
|
# `CA_FIXLEN` set given as a CArray must already be of that width.
|
|
29
33
|
#
|
|
34
|
+
# An object array keeps Ruby's own split between its two kinds of
|
|
35
|
+
# equality. A lookup through a hash -- `is_in`, the set operations,
|
|
36
|
+
# {#locate_addr}, {#unique} and the rest of the discovery family --
|
|
37
|
+
# uses `eql?`, as `Array#uniq`, `Array#&` and `Set` do, so `1` and
|
|
38
|
+
# `1.0` are different values there. A cell-by-cell comparison --
|
|
39
|
+
# {#eq}, {#count}, {#search}, {#bsearch} -- uses `==`, as
|
|
40
|
+
# `Array#include?` does, so they are equal there. To match by value
|
|
41
|
+
# across Integer and Float, convert the array to a numeric type first.
|
|
42
|
+
#
|
|
30
43
|
# Masked cells of `values` do not enter the set. Masked cells of
|
|
31
44
|
# `self` stay masked in the result (membership is unknown), so
|
|
32
45
|
# `is_in` propagates `self`'s mask like an element-wise comparison.
|
|
@@ -34,8 +47,10 @@ class CArray
|
|
|
34
47
|
# For a per-fiber "does this fiber contain any of these values"
|
|
35
48
|
# reduction, compose with {#any}: `a.is_in(values).any(axis: k)`.
|
|
36
49
|
#
|
|
37
|
-
# @param values [CArray, Array
|
|
50
|
+
# @param values [CArray, Array] the set to test membership
|
|
38
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.
|
|
39
54
|
# Between two time arrays the question is about instants, not ticks:
|
|
40
55
|
# `values` is reconciled into `self`'s unit first, so a `:D` array and an
|
|
41
56
|
# `:h` array match on the instants they share. The same holds for the set
|
|
@@ -43,6 +58,7 @@ class CArray
|
|
|
43
58
|
#
|
|
44
59
|
# @return [CArray] boolean CArray of the same shape as `self`.
|
|
45
60
|
def is_in (values)
|
|
61
|
+
refuse_range_set(values, "is_in")
|
|
46
62
|
a, b = promote_value_set(values)
|
|
47
63
|
a.__send__(:__is_in__, b)
|
|
48
64
|
end
|
|
@@ -57,13 +73,15 @@ class CArray
|
|
|
57
73
|
# always flat, like {#unique}, because the distinct values of a
|
|
58
74
|
# fiber vary in number.
|
|
59
75
|
#
|
|
60
|
-
# @param other [CArray, Array
|
|
76
|
+
# @param other [CArray, Array] promoted with `self` (a Range is refused, as by {#is_in}).
|
|
61
77
|
# @param sort [Boolean] when true, return the values sorted
|
|
62
78
|
# ascending instead of in first-appearance order.
|
|
63
79
|
# @return [CArray] 1-D CArray of the common distinct values.
|
|
64
80
|
def intersection (other, sort: false)
|
|
81
|
+
refuse_range_set(other, "intersection")
|
|
65
82
|
a, b = promote_value_set(other)
|
|
66
83
|
r = a.__send__(:__intersection__, b)
|
|
84
|
+
r = r.to_unit(unit) if !a.equal?(self) && respond_to?(:in_common_unit_with)
|
|
67
85
|
sort ? r.sort : r
|
|
68
86
|
end
|
|
69
87
|
|
|
@@ -72,25 +90,31 @@ class CArray
|
|
|
72
90
|
# absent from `other`, in `self`'s first-appearance order.
|
|
73
91
|
# See {#intersection} for the shared semantics and options.
|
|
74
92
|
#
|
|
75
|
-
# @param other [CArray, Array
|
|
93
|
+
# @param other [CArray, Array] promoted with `self` (a Range is refused, as by {#is_in}).
|
|
76
94
|
# @param sort [Boolean] when true, return the values sorted ascending.
|
|
77
95
|
# @return [CArray] 1-D CArray of the self-only distinct values.
|
|
78
96
|
def difference (other, sort: false)
|
|
97
|
+
refuse_range_set(other, "difference")
|
|
79
98
|
a, b = promote_value_set(other)
|
|
80
99
|
r = a.__send__(:__difference__, b)
|
|
100
|
+
r = r.to_unit(unit) if !a.equal?(self) && respond_to?(:in_common_unit_with)
|
|
81
101
|
sort ? r.sort : r
|
|
82
102
|
end
|
|
83
103
|
|
|
84
104
|
# @overload union(other, sort: false)
|
|
85
105
|
# Returns a 1-D CArray of the distinct values appearing in either
|
|
86
106
|
# `self` or `other`, in self-then-other first-appearance order.
|
|
87
|
-
# See {#intersection} for the shared semantics and options.
|
|
107
|
+
# See {#intersection} for the shared semantics and options. Two time
|
|
108
|
+
# arrays of different units give their union in the finer unit, which
|
|
109
|
+
# holds the values of both ({#intersection} and {#difference} answer in
|
|
110
|
+
# `self`'s unit, since their values are `self`'s).
|
|
88
111
|
#
|
|
89
|
-
# @param other [CArray, Array
|
|
112
|
+
# @param other [CArray, Array] promoted with `self` (a Range is refused, as by {#is_in}).
|
|
90
113
|
# @param sort [Boolean] when true, return the values sorted ascending
|
|
91
114
|
# (a merged, ordered set — e.g. a common time axis).
|
|
92
115
|
# @return [CArray] 1-D CArray of the combined distinct values.
|
|
93
116
|
def union (other, sort: false)
|
|
117
|
+
refuse_range_set(other, "union")
|
|
94
118
|
a, b = promote_value_set(other)
|
|
95
119
|
r = a.__send__(:__union__, b)
|
|
96
120
|
sort ? r.sort : r
|
|
@@ -98,6 +122,16 @@ class CArray
|
|
|
98
122
|
|
|
99
123
|
private
|
|
100
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
|
+
|
|
101
135
|
# Reconcile a set-valued argument with self to a common data_type via
|
|
102
136
|
# CArray.result_type (the single-source promotion rule the eager binop and
|
|
103
137
|
# lazy CABinOp share), returning [self', set']. Only data types are reconciled,
|
|
@@ -113,13 +147,69 @@ class CArray
|
|
|
113
147
|
# operand (Numo, a MemoryView producer, ...) comes in through wrap_readonly,
|
|
114
148
|
# the canonical type-coercion entry, so its format's data type drives the promote.
|
|
115
149
|
def promote_value_set (values)
|
|
150
|
+
# Two time arrays of different units meet in the finer one.
|
|
151
|
+
if values.is_a?(CArray) && values.class == self.class &&
|
|
152
|
+
respond_to?(:in_common_unit_with)
|
|
153
|
+
return in_common_unit_with(values)
|
|
154
|
+
end
|
|
155
|
+
values = values_for_lookup(values) if values.is_a?(CArray)
|
|
156
|
+
# Two fixed-width string arrays of different widths meet at the wider.
|
|
157
|
+
if values.is_a?(CArray) && data_type == CA_FIXLEN && values.data_type == CA_FIXLEN
|
|
158
|
+
return fixlen_widened_with(values)
|
|
159
|
+
end
|
|
160
|
+
# A Face that reconciles operands (CATime) reads a bare Array as values
|
|
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)
|
|
165
|
+
end
|
|
166
|
+
# A single value (a Time, an element, a bare number) is a set of one,
|
|
167
|
+
# read the same way; a bare number is refused like any other operand
|
|
168
|
+
# that is not of its kind.
|
|
169
|
+
if !values.is_a?(CArray) && !values.is_a?(Range) && face? &&
|
|
170
|
+
respond_to?(:comparable_pair)
|
|
171
|
+
return comparable_pair([values])
|
|
172
|
+
end
|
|
173
|
+
# ...and so does an array of values that are not of its kind (an object
|
|
174
|
+
# column of Time values, a string column).
|
|
175
|
+
if values.is_a?(CArray) && respond_to?(:comparable_pair) &&
|
|
176
|
+
!values.respond_to?(:comparable_pair)
|
|
177
|
+
return comparable_pair(values)
|
|
178
|
+
end
|
|
179
|
+
# The same from the other side: values of a time array looked for in an
|
|
180
|
+
# array that is not one.
|
|
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]
|
|
185
|
+
end
|
|
186
|
+
# A Face whose storage is not of its surface type. When its cells are
|
|
187
|
+
# its values, the set meets it as it would meet an array of the storage
|
|
188
|
+
# type, and the Face stays while that type does not change. Any other
|
|
189
|
+
# Face leaves the set to the lookup, which brings it into the Face's
|
|
190
|
+
# space or refuses it.
|
|
191
|
+
if face? && (storage = face_storage).data_type != data_type
|
|
192
|
+
unless comparable_storage?
|
|
193
|
+
set = values.is_a?(Range) ? values.to_a : values
|
|
194
|
+
return [self, set.is_a?(CArray) ? set : CArray.wrap_readonly(set)]
|
|
195
|
+
end
|
|
196
|
+
s, set = storage.send(:promote_value_set, values)
|
|
197
|
+
return [s.equal?(storage) ? self : s, set]
|
|
198
|
+
end
|
|
116
199
|
case values
|
|
117
200
|
when Array then return promote_elements(values)
|
|
118
201
|
when Range then return promote_elements(values.to_a)
|
|
119
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
|
|
120
210
|
set = values.is_a?(CArray) ? values : CArray.wrap_readonly(values)
|
|
121
211
|
return [self, set] if set.data_type == data_type # common fast path
|
|
122
|
-
t =
|
|
212
|
+
t = query_match_type(set)
|
|
123
213
|
[coerce_self(t), set.to_type(t)]
|
|
124
214
|
end
|
|
125
215
|
|
|
@@ -136,15 +226,55 @@ class CArray
|
|
|
136
226
|
# set for not matching self. Same rule the scalar operand of a
|
|
137
227
|
# comparison follows: a String standing in for a cell is padded to the
|
|
138
228
|
# cell's width.
|
|
229
|
+
# coerce: a fixlen or object set holds values of self's kind (see above)
|
|
139
230
|
[self, elems.to_ca.to_type(data_type, bytes: bytes)]
|
|
140
231
|
elsif data_type == CA_OBJECT
|
|
232
|
+
# coerce: an object set holds values of self's kind
|
|
141
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)]
|
|
142
238
|
else
|
|
143
239
|
t = CArray.result_type(self, *elems)
|
|
144
240
|
[coerce_self(t), elems.to_ca.to_type(t)]
|
|
145
241
|
end
|
|
146
242
|
end
|
|
147
243
|
|
|
244
|
+
# +other+ as a lookup against self reads it. A string array whose cells
|
|
245
|
+
# hold ranges into a pool of its own (CAConstString) is read by its
|
|
246
|
+
# strings, and a categorical by its labels, since their storage is not
|
|
247
|
+
# their values; anything else, or the same kind of array as self, is
|
|
248
|
+
# taken as it is. A record is left to be refused.
|
|
249
|
+
def values_for_lookup (other)
|
|
250
|
+
return other if other.send(:orderable_storage?)
|
|
251
|
+
return other if face? && other.class == self.class
|
|
252
|
+
if other.respond_to?(:to_string)
|
|
253
|
+
other.to_string
|
|
254
|
+
elsif other.is_a?(CACategorical)
|
|
255
|
+
other.to_type(:object)
|
|
256
|
+
else
|
|
257
|
+
other
|
|
258
|
+
end
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
# [self, other] at one cell width when both are fixed-width string arrays
|
|
262
|
+
# (CAFixlenString) of different widths: the narrower is padded with NUL to
|
|
263
|
+
# the wider, as concatenate joins them, so a string matches whatever width
|
|
264
|
+
# it is held at. Raw fixlen cells of different widths are values of
|
|
265
|
+
# different lengths, not padded strings, and are left as they are (the
|
|
266
|
+
# lookup refuses them, naming the widths).
|
|
267
|
+
def fixlen_widened_with (other)
|
|
268
|
+
return [self, other] if bytes == other.bytes
|
|
269
|
+
return [self, other] unless is_a?(CAFixlenString) && other.is_a?(CAFixlenString)
|
|
270
|
+
w = [bytes, other.bytes].max
|
|
271
|
+
[widen_fixlen_string(self, w), widen_fixlen_string(other, w)]
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
def widen_fixlen_string (x, w)
|
|
275
|
+
x.bytes == w ? x : x.to_fixlen_string(bytes: w)
|
|
276
|
+
end
|
|
277
|
+
|
|
148
278
|
def coerce_self (t)
|
|
149
279
|
CArray.result_type(self) == t ? self : to_type(t)
|
|
150
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
|
|
|
@@ -66,7 +61,7 @@ class CArray
|
|
|
66
61
|
t = transpose(*order)
|
|
67
62
|
end
|
|
68
63
|
inner = t.shape[-1]
|
|
69
|
-
outer_n = t.
|
|
64
|
+
outer_n = t.shape[0...-1].inject(1, :*) # 1 when ndim == 1
|
|
70
65
|
flat = t.reshape(outer_n, inner)
|
|
71
66
|
|
|
72
67
|
strings = Array.new(outer_n) { |i| flat[i, nil].to_a.join(sep_str) }
|
|
@@ -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,3 +1,6 @@
|
|
|
1
|
+
require "carray/methods/is_in" # fixlen_widened_with, values_for_lookup
|
|
2
|
+
require "carray/distance_limit"
|
|
3
|
+
|
|
1
4
|
class CArray
|
|
2
5
|
|
|
3
6
|
# @!group Sorting and searching
|
|
@@ -27,29 +30,63 @@ class CArray
|
|
|
27
30
|
# `self` and `ref` are compared at their common type, negotiated by
|
|
28
31
|
# `CArray.result_type` — a fractional query against an integer `ref`
|
|
29
32
|
# is compared at the promoted type rather than truncated, so `1.5` no
|
|
30
|
-
# longer matches `1
|
|
31
|
-
#
|
|
33
|
+
# longer matches `1`, and integers of mixed sign are matched by value
|
|
34
|
+
# (`-1` never matches `255`). Cross-family input (numeric against
|
|
35
|
+
# fixlen) raises.
|
|
36
|
+
#
|
|
37
|
+
# A time key (`CATime`) reads values that are not time arrays -- Time,
|
|
38
|
+
# Date or String objects, in an Array, an `:object` array or a string
|
|
39
|
+
# array -- as instants, on either side of the lookup, as {#is_in} does.
|
|
32
40
|
#
|
|
33
41
|
# Typical use is time-axis lookup: compute the address once against a
|
|
34
42
|
# reference axis, then reuse it to gather from many `ref`-shaped
|
|
35
43
|
# variables without repeating the lookup.
|
|
36
44
|
#
|
|
37
45
|
# @param ref [CArray, Array, Range] reference values to match against;
|
|
38
|
-
# any shape (used as flat). An Array or Range is
|
|
39
|
-
#
|
|
40
|
-
# `
|
|
41
|
-
# 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`.
|
|
42
49
|
# @return [CArray] `:int64` flat addresses into `ref`, same shape as
|
|
43
50
|
# `self`; unmatched cells are masked.
|
|
44
|
-
# @raise [
|
|
51
|
+
# @raise [CArray::DataTypeError] when `self` and `ref` have no common data type.
|
|
45
52
|
def locate_addr (ref)
|
|
53
|
+
# A time key (any Face that reconciles operands) against values that do
|
|
54
|
+
# not -- Time, Date or String objects, a string column -- reads them as
|
|
55
|
+
# values of its kind, whichever side they are on.
|
|
56
|
+
if ref.is_a?(CArray) && ref.class == self.class &&
|
|
57
|
+
respond_to?(:in_common_unit_with) && ref.unit != unit
|
|
58
|
+
a, b = in_common_unit_with(ref)
|
|
59
|
+
return a.locate_addr(b)
|
|
60
|
+
end
|
|
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
|
+
end
|
|
69
|
+
unless ref.is_a?(CArray) || ref.respond_to?(:to_ca)
|
|
70
|
+
raise TypeError, "locate_addr: the reference is an array of values (got #{ref.class})"
|
|
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
|
|
46
79
|
ref = ref.to_ca unless ref.is_a?(CArray)
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
80
|
+
ref = values_for_lookup(ref)
|
|
81
|
+
if data_type == CA_FIXLEN && ref.data_type == CA_FIXLEN && bytes != ref.bytes
|
|
82
|
+
a, b = fixlen_widened_with(ref)
|
|
83
|
+
return a.locate_addr(b) unless a.equal?(self) && b.equal?(ref)
|
|
84
|
+
end
|
|
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)
|
|
53
90
|
q = (data_type == t) ? self : to_type(t)
|
|
54
91
|
r = (ref.data_type == t) ? ref : ref.to_type(t)
|
|
55
92
|
q.send(:__locate_addr__, r)
|
|
@@ -57,16 +94,18 @@ class CArray
|
|
|
57
94
|
|
|
58
95
|
# @overload locate_nearest_addr(ref, direction: :round, tolerance: nil)
|
|
59
96
|
# Returns, for each element of `self`, the flat address into `ref` of
|
|
60
|
-
# the nearest reference value. Continuous sibling of {#locate_addr}
|
|
61
|
-
#
|
|
62
|
-
#
|
|
97
|
+
# the nearest reference value. Continuous sibling of {#locate_addr}:
|
|
98
|
+
# each cell is placed among the sorted reference values by comparing
|
|
99
|
+
# it with them, so an integer key is exact at any size and an infinite
|
|
100
|
+
# one keeps its place. `:round` takes the nearer neighbour, the upper
|
|
101
|
+
# one on a tie.
|
|
63
102
|
#
|
|
64
|
-
#
|
|
65
|
-
#
|
|
66
|
-
#
|
|
67
|
-
#
|
|
68
|
-
# before
|
|
69
|
-
#
|
|
103
|
+
# Outside `ref`'s span the direction still decides: `:floor` matches
|
|
104
|
+
# a cell after the last value to that last value, `:ceil` matches a
|
|
105
|
+
# cell before the first value to that first value, and `:round`
|
|
106
|
+
# matches either to the nearer end. The other side has no match
|
|
107
|
+
# (`:floor` before the first value, `:ceil` after the last) and is
|
|
108
|
+
# masked. Use `tolerance:` to refuse matches that are too far away.
|
|
70
109
|
#
|
|
71
110
|
# `tolerance:` (default `nil`) sets a maximum accepted absolute
|
|
72
111
|
# distance between `self[i]` and its matched `ref` value. When
|
|
@@ -76,50 +115,148 @@ class CArray
|
|
|
76
115
|
#
|
|
77
116
|
# `ref` need not be given in ascending order: it is sorted internally
|
|
78
117
|
# and the returned addresses are mapped back to positions in `ref` as
|
|
79
|
-
# passed.
|
|
118
|
+
# passed. A masked or NaN `ref` value matches nothing. Two time keys
|
|
119
|
+
# of different units are compared in the finer of the two.
|
|
120
|
+
# With one value present in `ref`, that value is the match for
|
|
121
|
+
# every cell the direction allows (`:floor` at or after it, `:ceil`
|
|
122
|
+
# at or before it, `:round` all). With none, every cell is masked;
|
|
123
|
+
# an empty `self` gives an empty result. A masked or NaN cell of
|
|
124
|
+
# `self` is always masked.
|
|
80
125
|
#
|
|
81
126
|
# @param ref [CArray] 1-D reference grid to match against.
|
|
82
127
|
# @param direction [Symbol] `:round`, `:floor`, or `:ceil` — rounding
|
|
83
128
|
# applied to the fractional position.
|
|
84
|
-
# @param tolerance [Numeric, nil] maximum
|
|
85
|
-
# distance; cells beyond this are masked.
|
|
129
|
+
# @param tolerance [Numeric, String, CATimedelta::Element, nil] maximum
|
|
130
|
+
# accepted `|self - ref|` distance; cells beyond this are masked.
|
|
131
|
+
# For a time key it is a duration: a String such as `"10 minutes"` or
|
|
132
|
+
# a CATimedelta value (a bare number is refused, since its length
|
|
133
|
+
# would depend on the key's unit). `nil` disables the check.
|
|
86
134
|
# @return [CArray] `:int64` flat addresses into `ref`, same shape as
|
|
87
|
-
# `self`;
|
|
135
|
+
# `self`; cells with no match in the direction and beyond-tolerance
|
|
136
|
+
# cells are masked.
|
|
88
137
|
# @raise [ArgumentError] when `direction` is not one of the accepted
|
|
89
|
-
# symbols.
|
|
138
|
+
# symbols, or `tolerance` is not a duration for a time key.
|
|
90
139
|
def locate_nearest_addr (ref, direction: :round, tolerance: nil)
|
|
91
140
|
unless [:round, :floor, :ceil].include?(direction)
|
|
92
141
|
raise ArgumentError,
|
|
93
142
|
"locate_nearest_addr: direction must be :round / :floor / " \
|
|
94
143
|
":ceil (got #{direction.inspect})"
|
|
95
144
|
end
|
|
145
|
+
[self, ref].each { |key| nearest_refuse_bytes(key) }
|
|
146
|
+
limit = tolerance && DistanceLimit.read(tolerance, self, "tolerance:")
|
|
147
|
+
return CArray.int64(*shape) if elements == 0
|
|
148
|
+
# A masked reference value matches nothing, as in locate_addr: sorting
|
|
149
|
+
# puts the masked ones last, and only the others are searched. A NaN
|
|
150
|
+
# one has no place among the others either; it sorts after them.
|
|
96
151
|
ri = ref.sort_addr
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
# A single-element (scalar-like) self makes linear_section collapse to
|
|
101
|
-
# its scalar-query path, which returns a bare Float (or nil when out of
|
|
102
|
-
# range) instead of a CArray. Rebuild a self-shaped float64 CArray so
|
|
103
|
-
# the mask_invalid -> direction -> project pipeline stays array-valued
|
|
104
|
-
# and the returned addr array matches self's shape.
|
|
105
|
-
fill = CArray.float64(*shape)
|
|
106
|
-
fill[] = sec.nil? ? UNDEF : sec
|
|
107
|
-
sec = fill
|
|
152
|
+
ri = ri[0...ref.count_not_masked] if ref.has_mask?
|
|
153
|
+
if ref.float? && ri.elements > 0
|
|
154
|
+
ri = ri[0...(ri.elements - ref[ri].is_nan.count(true))]
|
|
108
155
|
end
|
|
109
|
-
|
|
156
|
+
if ri.elements == 0
|
|
157
|
+
out = CArray.int64(*shape)
|
|
158
|
+
out[] = UNDEF
|
|
159
|
+
return out
|
|
160
|
+
end
|
|
161
|
+
rs = ref[ri]
|
|
162
|
+
n = rs.elements
|
|
163
|
+
rk, qk = nearest_keys(rs, self)
|
|
164
|
+
# The position of each cell among the sorted references is counted, not
|
|
165
|
+
# interpolated: :floor takes the last reference at or below it, :ceil the
|
|
166
|
+
# first at or above it.
|
|
167
|
+
# Counting keeps an integer key exact past 2**53 and an infinite one in
|
|
168
|
+
# its place, where an interpolated position would not.
|
|
110
169
|
si = case direction
|
|
111
|
-
when :
|
|
112
|
-
when :
|
|
113
|
-
when :
|
|
114
|
-
end
|
|
170
|
+
when :floor then nearest_count_below(rk, qk, true) - 1
|
|
171
|
+
when :ceil then nearest_count_below(rk, qk, false)
|
|
172
|
+
when :round then nearest_round(rk, qk, nearest_count_below(rk, qk, false))
|
|
173
|
+
end
|
|
174
|
+
si[si.lt(0) | si.ge(n)] = UNDEF
|
|
175
|
+
si[qk.is_nan.strip_mask(false)] = UNDEF if qk.float?
|
|
176
|
+
si = si.reshape(*shape)
|
|
177
|
+
si[is_masked] = UNDEF if has_mask?
|
|
115
178
|
idx = ri.project(si)
|
|
116
|
-
if
|
|
179
|
+
if limit
|
|
117
180
|
dist = (ref.project(idx) - self).abs
|
|
118
|
-
idx[dist >
|
|
181
|
+
idx[dist > limit] = UNDEF
|
|
119
182
|
end
|
|
120
183
|
idx
|
|
121
184
|
end
|
|
122
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
|
+
|
|
202
|
+
# The keys locate_nearest_addr orders by: the storage of the sorted
|
|
203
|
+
# reference and of the query, in one numeric type. A Face key is first
|
|
204
|
+
# brought into the reference's space (two time keys of different units
|
|
205
|
+
# into the finer of the two, which holds both exactly); plain keys meet at
|
|
206
|
+
# the type CArray.result_type gives, an integer pair of mixed sign at one
|
|
207
|
+
# that holds both.
|
|
208
|
+
private def nearest_keys(rs, q)
|
|
209
|
+
if rs.face? || q.face?
|
|
210
|
+
if rs.respond_to?(:to_unit) && q.class == rs.class && q.unit != rs.unit
|
|
211
|
+
u = CATimeUnitAlgebra.finer(rs.unit, q.unit)
|
|
212
|
+
rs = rs.to_unit(u)
|
|
213
|
+
q = q.to_unit(u)
|
|
214
|
+
end
|
|
215
|
+
q = rs.__send__(:reconcile_operand, q, "locate_nearest_addr")
|
|
216
|
+
rs = rs.__send__(:face_storage)
|
|
217
|
+
end
|
|
218
|
+
t = rs.send(:query_match_type, q)
|
|
219
|
+
rk = (rs.data_type == t) ? rs : rs.to_type(t)
|
|
220
|
+
qk = (q.data_type == t) ? q : q.to_type(t)
|
|
221
|
+
[rk, qk.flatten]
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
# For each query key, how many of the sorted reference keys are below it
|
|
225
|
+
# (inclusive: at or below). A numeric key is binary-searched in C; an
|
|
226
|
+
# object key is sorted together with the references, a tie putting the
|
|
227
|
+
# reference first when it counts, and counted against the references met
|
|
228
|
+
# before it.
|
|
229
|
+
private def nearest_count_below(rk, qk, inclusive)
|
|
230
|
+
return rk.send(:__count_below__, qk, inclusive) unless rk.data_type == CA_OBJECT
|
|
231
|
+
n, m = rk.elements, qk.elements
|
|
232
|
+
keys = CArray.concatenate([rk, qk])
|
|
233
|
+
tag = CArray.int8(n + m)
|
|
234
|
+
tag[0...n] = inclusive ? 0 : 1
|
|
235
|
+
tag[n...n+m] = inclusive ? 1 : 0
|
|
236
|
+
perm = CArray.sort_addr(keys, tag)
|
|
237
|
+
seen = perm.lt(n).cumsum.int64
|
|
238
|
+
query = perm.ge(n)
|
|
239
|
+
count = CArray.int64(m)
|
|
240
|
+
count[perm[query] - n] = seen[query]
|
|
241
|
+
count
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
# :round -- an exact match takes its first reference; otherwise the
|
|
245
|
+
# nearer of the two neighbours, the upper one on a tie. +lt+ counts the
|
|
246
|
+
# references below each key, so the upper neighbour is at lt and an exact
|
|
247
|
+
# match is the reference there.
|
|
248
|
+
private def nearest_round(rk, qk, lt)
|
|
249
|
+
lo = rk.project(lt - 1)
|
|
250
|
+
hi = rk.project(lt)
|
|
251
|
+
upper = (hi - qk).le(qk - lo).strip_mask(false)
|
|
252
|
+
upper[lo.is_masked] = true if lo.has_mask?
|
|
253
|
+
upper[hi.is_masked] = false if hi.has_mask?
|
|
254
|
+
upper[hi.eq(qk).strip_mask(false)] = true
|
|
255
|
+
si = lt - 1
|
|
256
|
+
si[upper] = lt[upper]
|
|
257
|
+
si
|
|
258
|
+
end
|
|
259
|
+
|
|
123
260
|
# @!endgroup
|
|
124
261
|
|
|
125
262
|
end
|
data/lib/carray/methods/mode.rb
CHANGED
|
@@ -65,12 +65,25 @@ class CArray
|
|
|
65
65
|
# Only meaningful for discrete / binned data: raw float is almost all
|
|
66
66
|
# unique, so every value is modal and the per-axis Array grows to the
|
|
67
67
|
# fiber length. Bin first. Same NaN / mask semantics as {#is_mode}.
|
|
68
|
+
# A complex array raises {CArray::DataTypeError}, since the modes
|
|
69
|
+
# are listed in ascending order; {#is_mode} marks its modal cells.
|
|
68
70
|
#
|
|
69
71
|
# @param axis [Integer, nil] axis to take the mode along; `nil` uses
|
|
70
72
|
# the whole array.
|
|
71
73
|
# @return [CArray, Array<CArray>] 1-D CArray (flat) or an Array of
|
|
72
74
|
# reduced CArrays, one per mode rank (per-axis).
|
|
73
75
|
def mode (axis: nil)
|
|
76
|
+
# An error from the methods mode is made of (sort, ...) names mode.
|
|
77
|
+
CArray.__name_errors_after_method__(:mode) { mode_answered(axis) }
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
private def mode_answered (axis)
|
|
81
|
+
if complex?
|
|
82
|
+
raise CArray::DataTypeError,
|
|
83
|
+
"mode: the modes are listed in ascending order, and a " \
|
|
84
|
+
"#{data_type_name} value has no order (complex numbers are not " \
|
|
85
|
+
"ordered); is_mode marks the cells that hold a mode"
|
|
86
|
+
end
|
|
74
87
|
return __mode_flat if axis.nil?
|
|
75
88
|
k = normalize_axis(axis, "mode")
|
|
76
89
|
|
|
@@ -120,7 +133,11 @@ class CArray
|
|
|
120
133
|
def __mode_flat
|
|
121
134
|
values, counts = value_counts
|
|
122
135
|
return values if values.elements == 0
|
|
123
|
-
values[counts.eq(counts.max)]
|
|
136
|
+
modes = values[counts.eq(counts.max)]
|
|
137
|
+
# Records are sorted only by the members their struct declares in
|
|
138
|
+
# order_by:; without one the modes stay in first-appearance order.
|
|
139
|
+
return modes if is_a?(CARecord) && data_class.order_by.nil?
|
|
140
|
+
modes.sort
|
|
124
141
|
end
|
|
125
142
|
|
|
126
143
|
end
|