carray 3.0.2 → 3.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +304 -0
- data/README.md +3 -4
- data/carray.gemspec +1 -1
- data/ext/ca_axis_dispatch.c +225 -0
- data/ext/ca_axis_group.c +177 -99
- data/ext/ca_binop_dispatch.c +54 -32
- data/ext/ca_binop_dispatch.h +1 -0
- data/ext/ca_categorical_iterator.c +66 -71
- data/ext/ca_compare.c +8 -2
- data/ext/ca_for_buffer.h +21 -68
- data/ext/ca_group_iter.c +27 -11
- data/ext/ca_kernel_iterator.c +663 -214
- data/ext/ca_kernel_iterator.h +231 -167
- data/ext/ca_monop_dispatch.c +4 -4
- data/ext/ca_monop_dispatch.h +4 -0
- data/ext/ca_obj_array.c +132 -22
- data/ext/ca_obj_bincmp.c +152 -121
- data/ext/ca_obj_binop.c +107 -210
- data/ext/ca_obj_bitarray.c +37 -24
- data/ext/ca_obj_bitfield.c +95 -62
- data/ext/ca_obj_block.c +1 -0
- data/ext/ca_obj_byte_swap.c +40 -40
- data/ext/ca_obj_const_string.c +96 -25
- data/ext/ca_obj_face.c +406 -64
- data/ext/ca_obj_face.h +92 -47
- data/ext/ca_obj_fake.c +21 -50
- data/ext/ca_obj_farray.c +1 -0
- data/ext/ca_obj_field.c +16 -3
- data/ext/ca_obj_fixlen_string.c +21 -21
- data/ext/ca_obj_grid.c +181 -124
- data/ext/ca_obj_meld.c +189 -107
- data/ext/ca_obj_moncmp.c +61 -61
- data/ext/ca_obj_monop.c +125 -107
- data/ext/ca_obj_object.c +543 -233
- data/ext/ca_obj_record.c +51 -16
- data/ext/ca_obj_reduce.c +5 -8
- data/ext/ca_obj_refer.c +117 -33
- data/ext/ca_obj_remap.c +57 -17
- data/ext/ca_obj_repeat.c +21 -5
- data/ext/ca_obj_roll.c +115 -11
- data/ext/ca_obj_select.c +47 -49
- data/ext/ca_obj_select_axis.c +58 -60
- data/ext/ca_obj_shift.c +22 -102
- data/ext/ca_obj_stack.c +223 -118
- data/ext/ca_obj_stride.c +195 -34
- data/ext/ca_obj_string.c +17 -18
- data/ext/ca_obj_tile.c +32 -16
- data/ext/ca_obj_time.c +23 -25
- data/ext/ca_obj_timedelta.c +21 -23
- data/ext/ca_obj_transpose.c +12 -3
- data/ext/ca_obj_triop.c +110 -117
- data/ext/ca_obj_window.c +149 -27
- 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 +102 -79
- 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 +463 -0
- data/ext/carray.h +70 -27
- data/ext/carray_access.c +455 -211
- data/ext/carray_address_basis.c +177 -36
- data/ext/carray_attribute.c +45 -3
- data/ext/carray_bincount.c +32 -2
- data/ext/carray_broadcast.c +46 -14
- data/ext/carray_call_cfunc.c +0 -28
- data/ext/carray_cast.c +1444 -209
- data/ext/carray_cast_func.rb +34 -21
- data/ext/carray_conversion.c +197 -73
- data/ext/carray_copy.c +32 -11
- data/ext/carray_core.c +514 -122
- data/ext/carray_count.c +140 -87
- data/ext/carray_element.c +18 -27
- data/ext/carray_factorize.c +569 -277
- data/ext/carray_generate.c +76 -40
- data/ext/carray_histogram.c +152 -94
- data/ext/carray_hold.c +2 -1
- data/ext/carray_index_classifier.c +63 -96
- data/ext/carray_internal.h +385 -3
- data/ext/carray_lazy.c +834 -79
- data/ext/carray_loop.c +81 -65
- data/ext/carray_mask.c +682 -316
- data/ext/carray_math_kernel.h +1 -0
- data/ext/carray_median_percentile.c +221 -76
- data/ext/carray_memory_view.c +254 -248
- data/ext/carray_operator.c +1089 -359
- data/ext/carray_order.c +133 -69
- data/ext/carray_partition.c +102 -56
- data/ext/carray_random.c +273 -239
- data/ext/carray_scatter.c +308 -65
- data/ext/carray_segment.c +257 -0
- data/ext/carray_slab.c +170 -70
- data/ext/carray_sort.c +215 -218
- data/ext/carray_sort_kernel.c +8 -1
- data/ext/carray_struct.c +99 -21
- data/ext/carray_test.c +167 -64
- data/ext/carray_time_text.c +1182 -0
- data/ext/carray_utils.c +363 -98
- data/ext/extconf.rb +27 -2
- data/ext/mk_call_cfunc.rb +0 -2
- data/ext/mk_pow5_table.rb +35 -0
- data/ext/mkkernel.rb +1076 -846
- data/ext/ruby_carray.c +7 -1
- data/ext/version.h +4 -4
- data/ext/version.rb +4 -2
- data/lib/carray/attribute.rb +39 -0
- data/lib/carray/autoload_carray.rb +19 -0
- data/lib/carray/autoload_method_extension.rb +6 -0
- data/lib/carray/axis_group.rb +174 -26
- data/lib/carray/basics.rb +91 -25
- data/lib/carray/bincount_nd.rb +6 -0
- data/lib/carray/block_iterator.rb +118 -48
- data/lib/carray/boolean_reduce.rb +21 -29
- data/lib/carray/categorical.rb +94 -26
- data/lib/carray/categorical_iterator.rb +333 -826
- data/lib/carray/complex.rb +36 -29
- data/lib/carray/conditional.rb +94 -7
- data/lib/carray/const_string.rb +46 -0
- data/lib/carray/frame/concat.rb +157 -6
- data/lib/carray/frame/convert.rb +13 -7
- data/lib/carray/frame/csv_parser.rb +589 -82
- data/lib/carray/frame/describe.rb +79 -0
- data/lib/carray/frame/frame.rb +124 -25
- data/lib/carray/frame/group.rb +189 -63
- data/lib/carray/frame/io.rb +412 -72
- data/lib/carray/frame/join.rb +134 -46
- data/lib/carray/frame/records.rb +37 -16
- data/lib/carray/frame/reshape.rb +448 -0
- data/lib/carray/frame/sort.rb +26 -2
- data/lib/carray/frame/verbs.rb +715 -48
- data/lib/carray/frame.rb +2 -0
- data/lib/carray/fuse_source.rb +111 -2
- data/lib/carray/fusion.rb +229 -38
- data/lib/carray/inspect.rb +29 -11
- data/lib/carray/iterator.rb +14 -2
- data/lib/carray/lazy.rb +54 -30
- data/lib/carray/mask_gap_fill.rb +66 -52
- data/lib/carray/math.rb +32 -19
- data/lib/carray/meld_reduce.rb +40 -7
- data/lib/carray/methods/align_addr.rb +8 -4
- data/lib/carray/methods/bincount.rb +2 -2
- data/lib/carray/methods/bit_string.rb +14 -3
- data/lib/carray/methods/composition.rb +16 -10
- data/lib/carray/methods/factorize.rb +2 -2
- data/lib/carray/methods/gather_nd.rb +13 -2
- data/lib/carray/methods/insert_block.rb +9 -14
- data/lib/carray/methods/is_in.rb +117 -4
- data/lib/carray/methods/join.rb +1 -1
- data/lib/carray/methods/locate_addr.rb +177 -36
- data/lib/carray/methods/mode.rb +18 -1
- 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/record_order.rb +208 -0
- data/lib/carray/runtime.rb +14 -11
- data/lib/carray/segment_iterator.rb +990 -0
- data/lib/carray/serialize.rb +197 -27
- data/lib/carray/slab_iterator.rb +51 -27
- data/lib/carray/stack.rb +10 -4
- data/lib/carray/string_operation_extension.rb +4 -3
- data/lib/carray/struct.rb +142 -51
- data/lib/carray/struct_builder.rb +110 -14
- data/lib/carray/table.rb +2 -2
- data/lib/carray/time.rb +517 -103
- data/lib/carray/window_iterator.rb +127 -72
- data/lib/carray.rb +2 -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 +1 -1
- data/yard-stubs/ca_obj_refer.rb +3 -3
- data/yard-stubs/ca_obj_roll.rb +0 -1
- data/yard-stubs/ca_obj_stride.rb +10 -7
- data/yard-stubs/ca_obj_transpose.rb +1 -1
- data/yard-stubs/ca_obj_window.rb +1 -2
- data/yard-stubs/carray_access.rb +5 -4
- data/yard-stubs/carray_broadcast.rb +1 -1
- data/yard-stubs/carray_cast.rb +78 -43
- data/yard-stubs/carray_conversion.rb +13 -1
- data/yard-stubs/carray_copy.rb +8 -3
- data/yard-stubs/carray_core.rb +3 -3
- data/yard-stubs/carray_count.rb +9 -0
- data/yard-stubs/carray_lazy.rb +5 -1
- data/yard-stubs/carray_loop.rb +4 -1
- data/yard-stubs/carray_mask.rb +34 -10
- data/yard-stubs/carray_math.rb +25 -13
- data/yard-stubs/carray_median_percentile.rb +10 -6
- data/yard-stubs/carray_order.rb +34 -13
- data/yard-stubs/carray_scatter.rb +10 -3
- data/yard-stubs/carray_segment.rb +72 -0
- data/yard-stubs/carray_slab.rb +2 -1
- data/yard-stubs/carray_sort.rb +43 -3
- data/yard-stubs/carray_test.rb +10 -0
- data/yard-stubs/carray_utils.rb +0 -26
- metadata +11 -19
- data/ext/ca_for_each_element.h +0 -239
- data/ext/carray_build_flags.h +0 -3
- data/ext/carray_config.h +0 -28
- data/ext/carray_kernels_bincmp.c +0 -4446
- data/ext/carray_kernels_binop.c +0 -11001
- data/ext/carray_kernels_init.c +0 -1131
- data/ext/carray_kernels_map.c +0 -3467
- data/ext/carray_kernels_moncmp.c +0 -2097
- data/ext/carray_kernels_monop.c +0 -18313
- data/ext/carray_kernels_reduce_aggregate.c +0 -26005
- data/ext/carray_kernels_reduce_boolean.c +0 -330
- data/ext/carray_kernels_reduce_cumulative.c +0 -14862
- data/ext/carray_kernels_reduce_extreme.c +0 -17494
- data/ext/carray_kernels_reduce_variance.c +0 -3910
- data/ext/carray_kernels_scan.c +0 -3693
- data/ext/carray_kernels_search.c +0 -32218
- data/ext/carray_kernels_sort.c +0 -10626
- data/ext/carray_kernels_triop.c +0 -1392
data/ext/ruby_carray.c
CHANGED
|
@@ -44,6 +44,7 @@ void Init_carray_access ();
|
|
|
44
44
|
void Init_carray_index_classifier ();
|
|
45
45
|
void Init_carray_element ();
|
|
46
46
|
void Init_carray_scatter (); /* scatter_*! family */
|
|
47
|
+
void Init_carray_segment (); /* segment lengths / offsets / index */
|
|
47
48
|
void Init_ca_axis_group (); /* devel/MEMO_AXIS_GROUP.md — axis-group reduction kernel */
|
|
48
49
|
void Init_ca_group_iter (); /* devel/MEMO_AXIS_GROUP.md — axis-group [] surface */
|
|
49
50
|
void Init_carray_bincount (); /* bincount dedicated kernel */
|
|
@@ -89,6 +90,8 @@ void Init_ca_obj_byte_swap ();
|
|
|
89
90
|
void Init_ca_face (); /* CAFace abstract base; must precede CATime / CATimedelta / CARecord / CAConstString */
|
|
90
91
|
void Init_ca_source (); /* CASource abstract base; concrete sources live in C extensions */
|
|
91
92
|
void Init_ca_obj_time (); /* first numeric Face */
|
|
93
|
+
void Init_carray_time_text ();
|
|
94
|
+
void Init_caframe_csv_reader ();
|
|
92
95
|
void Init_ca_obj_timedelta (); /* CATime sibling */
|
|
93
96
|
void Init_ca_obj_record (); /* composite Face for CAStruct backing */
|
|
94
97
|
void Init_ca_obj_const_string ();/* read-only variable-length string Face */
|
|
@@ -169,7 +172,7 @@ Init_carray_ext (void)
|
|
|
169
172
|
/* -- Exception class -- */
|
|
170
173
|
|
|
171
174
|
rb_eCADataTypeError =
|
|
172
|
-
rb_define_class_under(rb_cCArray, "DataTypeError",
|
|
175
|
+
rb_define_class_under(rb_cCArray, "DataTypeError", rb_eTypeError);
|
|
173
176
|
|
|
174
177
|
/* -- CA module -- namespace for misc utilities related to CArray */
|
|
175
178
|
|
|
@@ -286,6 +289,7 @@ Init_carray_ext (void)
|
|
|
286
289
|
Init_carray_index_classifier();
|
|
287
290
|
Init_carray_element();
|
|
288
291
|
Init_carray_scatter();
|
|
292
|
+
Init_carray_segment();
|
|
289
293
|
Init_ca_axis_group(); /* devel/MEMO_AXIS_GROUP.md — axis-group reduction kernel */
|
|
290
294
|
Init_carray_bincount();
|
|
291
295
|
Init_carray_factorize();
|
|
@@ -343,6 +347,8 @@ Init_carray_ext (void)
|
|
|
343
347
|
(datetime / timedelta / record /
|
|
344
348
|
const_string). */
|
|
345
349
|
Init_ca_obj_time();
|
|
350
|
+
Init_carray_time_text();
|
|
351
|
+
Init_caframe_csv_reader();
|
|
346
352
|
Init_ca_obj_timedelta();
|
|
347
353
|
Init_ca_obj_record();
|
|
348
354
|
Init_ca_obj_const_string();
|
data/ext/version.h
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
#define CA_VERSION "3.0.
|
|
2
|
-
#define CA_VERSION_CODE
|
|
1
|
+
#define CA_VERSION "3.0.3"
|
|
2
|
+
#define CA_VERSION_CODE 30003
|
|
3
3
|
#define CA_VERSION_MAJOR 3
|
|
4
4
|
#define CA_VERSION_MINOR 0
|
|
5
|
-
#define CA_VERSION_TEENY
|
|
6
|
-
#define CA_VERSION_DATE "2026/
|
|
5
|
+
#define CA_VERSION_TEENY 3
|
|
6
|
+
#define CA_VERSION_DATE "2026/10/13"
|
data/ext/version.rb
CHANGED
|
@@ -21,8 +21,10 @@ def carray_version
|
|
|
21
21
|
|
|
22
22
|
ca_version2 = format("%i.%i.%i",
|
|
23
23
|
ca_version_major, ca_version_minor, ca_version_teeny)
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
# Two decimal digits for each of minor and teeny, so 3.0.10 (30010)
|
|
25
|
+
# comes after 3.0.9 (30009) and before 3.1.0 (30100).
|
|
26
|
+
ca_version_code2 =
|
|
27
|
+
10000 * ca_version_major + 100 * ca_version_minor + ca_version_teeny
|
|
26
28
|
|
|
27
29
|
# Allow pre-release suffix like "3.0.0.dev" / "3.0.0.alpha1" while still
|
|
28
30
|
# validating the numeric MAJOR.MINOR.TEENY prefix.
|
data/lib/carray/attribute.rb
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
# Surface:
|
|
5
5
|
# ca.attr(:unit) # => getter; nil if absent
|
|
6
6
|
# ca.set_attr(:unit, "m/s") # => setter; writes to self's @attr
|
|
7
|
+
# ca.set_attrs(other.attrs) # => sets every key of a Hash at once
|
|
7
8
|
# ca.attrs # => frozen shallow clone of the full Hash
|
|
8
9
|
# ca.has_attr? # => any attribute present?
|
|
9
10
|
# ca.has_attr?(:unit) # => specific key present?
|
|
@@ -27,6 +28,14 @@
|
|
|
27
28
|
# `attrs` returns the effective merged Hash (deeper writes
|
|
28
29
|
# override shallower ones), frozen.
|
|
29
30
|
# Setter side always writes to self's @attr.
|
|
31
|
+
# The walk stops at a lazy operation view (CAMonOp / CABinOp /
|
|
32
|
+
# CATriOp / CAMonCmp / CABinCmp): its cells are computed, not the
|
|
33
|
+
# parent's cells, so the parent's attributes do not describe them.
|
|
34
|
+
# `a.lazy` itself (CALazyMarker) is a view of `a` and walks through.
|
|
35
|
+
#
|
|
36
|
+
# Copies: `copy` and `to_type` give the new array the attributes the
|
|
37
|
+
# source shows, as its own Hash. Arithmetic and reductions do not
|
|
38
|
+
# carry them -- the result is a different quantity.
|
|
30
39
|
|
|
31
40
|
class CArray
|
|
32
41
|
|
|
@@ -61,6 +70,30 @@ class CArray
|
|
|
61
70
|
(@attr ||= {})[attr_normalize_key(key)] = attr_coerce_value(value)
|
|
62
71
|
end
|
|
63
72
|
|
|
73
|
+
# @overload set_attrs(hash)
|
|
74
|
+
# Sets every key of `hash` on `self`, as {#set_attr} does one key.
|
|
75
|
+
# Keys already on `self` and not in `hash` are kept. Every value is
|
|
76
|
+
# validated before any is written, so a rejected value leaves `self`
|
|
77
|
+
# unchanged. `b.set_attrs(a.attrs)` gives `b` the attributes `a`
|
|
78
|
+
# shows.
|
|
79
|
+
# @param hash [Hash{Symbol, String => Object}] attributes to set.
|
|
80
|
+
# @return [CArray] self.
|
|
81
|
+
# @raise [TypeError] when a key or a value is not accepted.
|
|
82
|
+
def set_attrs (hash)
|
|
83
|
+
unless hash.is_a?(Hash)
|
|
84
|
+
raise TypeError, "set_attrs takes a Hash (got #{hash.class})"
|
|
85
|
+
end
|
|
86
|
+
entries = hash.map do |key, value|
|
|
87
|
+
attr_validate_value(value)
|
|
88
|
+
[attr_normalize_key(key), attr_coerce_value(value)]
|
|
89
|
+
end
|
|
90
|
+
unless entries.empty?
|
|
91
|
+
h = (@attr ||= {})
|
|
92
|
+
entries.each { |k, v| h[k] = v }
|
|
93
|
+
end
|
|
94
|
+
self
|
|
95
|
+
end
|
|
96
|
+
|
|
64
97
|
# @overload attrs
|
|
65
98
|
# Returns a frozen shallow Hash of all attributes visible on
|
|
66
99
|
# `self`, merged along the parent chain (deeper writes shadow
|
|
@@ -114,6 +147,7 @@ class CArray
|
|
|
114
147
|
h = ca.instance_variable_get(:@attr)
|
|
115
148
|
yield(h) if h
|
|
116
149
|
end
|
|
150
|
+
return nil if attr_computing_view?(ca)
|
|
117
151
|
parent = ca.respond_to?(:parent) ? ca.parent : nil
|
|
118
152
|
return nil if parent.nil? || parent.equal?(ca)
|
|
119
153
|
ca = parent
|
|
@@ -121,6 +155,11 @@ class CArray
|
|
|
121
155
|
nil
|
|
122
156
|
end
|
|
123
157
|
|
|
158
|
+
def attr_computing_view? (ca)
|
|
159
|
+
ca.is_a?(CAMonOp) || ca.is_a?(CABinOp) || ca.is_a?(CATriOp) ||
|
|
160
|
+
ca.is_a?(CAMonCmp) || ca.is_a?(CABinCmp)
|
|
161
|
+
end
|
|
162
|
+
|
|
124
163
|
def attr_normalize_key (key)
|
|
125
164
|
case key
|
|
126
165
|
when String then key
|
|
@@ -47,6 +47,23 @@ class CArray
|
|
|
47
47
|
autoload_method "marshal_load", serialize_rb
|
|
48
48
|
end
|
|
49
49
|
|
|
50
|
+
# A view, a wrap and a scalar are marshalled through _dump / self._load,
|
|
51
|
+
# which return a new array: with marshal_dump Marshal records the class and
|
|
52
|
+
# loads into an empty instance of it, which the plain array the payload
|
|
53
|
+
# holds cannot become. A record array keeps marshal_dump, under which it
|
|
54
|
+
# already round-trips with its class.
|
|
55
|
+
[CAView, CAWrap, CScalar].each do |klass|
|
|
56
|
+
klass.class_eval do
|
|
57
|
+
undef_method :marshal_dump
|
|
58
|
+
autoload_method "_dump", "carray/serialize"
|
|
59
|
+
autoload_method "self._load", "carray/serialize"
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
class CARecord
|
|
64
|
+
autoload_method "marshal_dump", "carray/serialize"
|
|
65
|
+
end
|
|
66
|
+
|
|
50
67
|
# ---- Arrow tensor IPC (interop) --------------------------------------------
|
|
51
68
|
|
|
52
69
|
class CArray
|
|
@@ -126,10 +143,12 @@ class CArray
|
|
|
126
143
|
categorical_iterator_rb = "carray/categorical_iterator"
|
|
127
144
|
autoload_method "group_by_category", categorical_iterator_rb
|
|
128
145
|
autoload_method "group_by_run", categorical_iterator_rb
|
|
146
|
+
autoload_method "segments", "carray/segment_iterator"
|
|
129
147
|
end
|
|
130
148
|
|
|
131
149
|
autoload :CACategorical, "carray/categorical"
|
|
132
150
|
autoload :CACategoricalIterator, "carray/categorical_iterator"
|
|
151
|
+
autoload :CASegmentIterator, "carray/segment_iterator"
|
|
133
152
|
autoload :AxisGroup, "carray/axis_group"
|
|
134
153
|
autoload :GroupLabels, "carray/axis_group"
|
|
135
154
|
|
|
@@ -53,5 +53,11 @@ module AutoloadMethodExtension
|
|
|
53
53
|
send(name, *args, **kwargs, &block)
|
|
54
54
|
end
|
|
55
55
|
stub = target.instance_method(name)
|
|
56
|
+
# The library replaces the stub with a plain `def`, which `ruby -w`
|
|
57
|
+
# reports as a redefinition. Replacing is what the stub is for, so mark
|
|
58
|
+
# it as aliased: Ruby does not warn when the old definition has an alias.
|
|
59
|
+
tmp = :"__autoload_stub_#{name}__"
|
|
60
|
+
target.send(:alias_method, tmp, name)
|
|
61
|
+
target.send(:remove_method, tmp)
|
|
56
62
|
end
|
|
57
63
|
end
|
data/lib/carray/axis_group.rb
CHANGED
|
@@ -213,6 +213,57 @@ class AxisGroup
|
|
|
213
213
|
end
|
|
214
214
|
end
|
|
215
215
|
|
|
216
|
+
# The keywords a group reduction takes besides axis:. Checked before the
|
|
217
|
+
# work, so an unknown keyword is refused rather than dropped.
|
|
218
|
+
REDUCE_KEYWORDS = [:axis, :min_count, :fill_value].freeze
|
|
219
|
+
|
|
220
|
+
# all / any take axis: only, as the core ones do.
|
|
221
|
+
LOGICAL_REDUCE_KEYWORDS = [:axis].freeze
|
|
222
|
+
|
|
223
|
+
def self.__check_reduce_keywords__ (op, kw)
|
|
224
|
+
allowed = [:all, :any].include?(op.to_sym) ? LOGICAL_REDUCE_KEYWORDS : REDUCE_KEYWORDS
|
|
225
|
+
unknown = kw.keys - allowed
|
|
226
|
+
unless unknown.empty?
|
|
227
|
+
raise ArgumentError,
|
|
228
|
+
"#{op}: unknown keyword#{unknown.size > 1 ? "s" : ""}: " +
|
|
229
|
+
unknown.map(&:inspect).join(", ")
|
|
230
|
+
end
|
|
231
|
+
CArray.__min_count_need__(op, kw[:min_count], 0)
|
|
232
|
+
nil
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
# A boolean array carrying value's mask: what a count reads, for a value
|
|
236
|
+
# whose payload the group kernel cannot read.
|
|
237
|
+
def self.__count_stand_in__ (value)
|
|
238
|
+
return value if value.boolean? || value.integer? || value.float?
|
|
239
|
+
stand_in = CArray.boolean(*value.shape)
|
|
240
|
+
stand_in.mask = value.mask if value.has_mask?
|
|
241
|
+
stand_in
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
# A count of the mask reads every cell of the group, as the core's does.
|
|
245
|
+
MASK_COUNTS = [:count, :count_not_masked, :count_masked].freeze
|
|
246
|
+
|
|
247
|
+
# min_count: masks a group with fewer contributing cells (present cells,
|
|
248
|
+
# or every cell for a count of the mask); fill_value: then fills the masked
|
|
249
|
+
# cells -- as a core reduction does with each.
|
|
250
|
+
def self.__finish_reduction__ (iter, op, kw, result)
|
|
251
|
+
need = CArray.__min_count_need__(op, kw[:min_count], 0)
|
|
252
|
+
if need > 0
|
|
253
|
+
counts = if MASK_COUNTS.include?(op.to_sym)
|
|
254
|
+
iter.elements(axis: kw[:axis])
|
|
255
|
+
else
|
|
256
|
+
iter.count_not_masked(axis: kw[:axis])
|
|
257
|
+
end
|
|
258
|
+
if result.is_a?(CArray)
|
|
259
|
+
result[counts.lt(need)] = UNDEF
|
|
260
|
+
elsif counts < need
|
|
261
|
+
result = UNDEF
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
CArray.__reduce_fill__(result, kw[:fill_value])
|
|
265
|
+
end
|
|
266
|
+
|
|
216
267
|
# Parse an axis: spec into [has_group, fused_band_slot_positions].
|
|
217
268
|
def self.parse_axis (axis)
|
|
218
269
|
has_group = false
|
|
@@ -348,11 +399,9 @@ class CAGroupIterator
|
|
|
348
399
|
end
|
|
349
400
|
v = args.first
|
|
350
401
|
return count_masked(**kw) if v.equal?(UNDEF)
|
|
351
|
-
# a masked cell
|
|
352
|
-
#
|
|
353
|
-
|
|
354
|
-
hit = hit.strip_mask(false) if hit.has_mask?
|
|
355
|
-
self.class.__build__(hit.int64, spec).sum(**kw).int64
|
|
402
|
+
# eq leaves a masked cell UNDEF, so the sum leaves it out and its
|
|
403
|
+
# min_count: counts the present cells, as the core count(v) does
|
|
404
|
+
self.class.__build__(value.eq(v).int64, spec).sum(**kw).int64
|
|
356
405
|
end
|
|
357
406
|
|
|
358
407
|
alias __all_folded__ all
|
|
@@ -383,7 +432,30 @@ class CAGroupIterator
|
|
|
383
432
|
|
|
384
433
|
# Per-group count of value-masked cells = elements - count_not_masked.
|
|
385
434
|
def count_masked (**kw)
|
|
386
|
-
|
|
435
|
+
has_group, = AxisGroup.parse_axis(kw[:axis])
|
|
436
|
+
return value.count_masked(**kw) unless has_group
|
|
437
|
+
AxisGroup.__check_reduce_keywords__(:count_masked, kw)
|
|
438
|
+
plain = { axis: kw[:axis] }
|
|
439
|
+
AxisGroup.__finish_reduction__(self, :count_masked, kw,
|
|
440
|
+
elements(**plain) - count_not_masked(**plain))
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
# The scatter kernel reads an integer or float payload, and answers there
|
|
444
|
+
# as the core does. Any other payload -- boolean, complex, object, a Face --
|
|
445
|
+
# is reduced group by group by the core, through the composite categorical,
|
|
446
|
+
# so the data type, the Face and a refusal are the core's: a boolean's sum
|
|
447
|
+
# is uint64, a time column's minimum a time, its sum refused.
|
|
448
|
+
[:sum, :accumulate, :prod, :mean, :min, :max,
|
|
449
|
+
:variance, :stddev, :variancep, :stddevp].each do |op|
|
|
450
|
+
alias_method :"__#{op}_by_kernel__", op
|
|
451
|
+
define_method(op) do |**kw|
|
|
452
|
+
has_group, = AxisGroup.parse_axis(kw[:axis])
|
|
453
|
+
if ! has_group || kernel_reads_value?
|
|
454
|
+
__send__(:"__#{op}_by_kernel__", **kw)
|
|
455
|
+
else
|
|
456
|
+
reduce_by_core(op, kw)
|
|
457
|
+
end
|
|
458
|
+
end
|
|
387
459
|
end
|
|
388
460
|
|
|
389
461
|
# Per-group [min, max] pair (matching CArray#minmax).
|
|
@@ -427,13 +499,21 @@ class CAGroupIterator
|
|
|
427
499
|
# (no present pair) is UNDEF (matching CArray#wmean); a present group whose
|
|
428
500
|
# weights sum to zero yields NaN/Inf (core's 0/0).
|
|
429
501
|
def wmean (weights, **kw)
|
|
502
|
+
has_group, = AxisGroup.parse_axis(kw[:axis])
|
|
503
|
+
return value.wmean(weights, **kw) unless has_group
|
|
504
|
+
AxisGroup.__check_reduce_keywords__(:wmean, kw)
|
|
505
|
+
plain = { axis: kw[:axis] }
|
|
430
506
|
prod = value * weights
|
|
431
|
-
num = self.class.__build__(prod, spec).sum(**
|
|
432
|
-
den = self.class.__build__(value * 0 + weights, spec).sum(**
|
|
433
|
-
cnt = self.class.__build__(prod, spec).count_not_masked(**
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
507
|
+
num = self.class.__build__(prod, spec).sum(**plain)
|
|
508
|
+
den = self.class.__build__(value * 0 + weights, spec).sum(**plain)
|
|
509
|
+
cnt = self.class.__build__(prod, spec).count_not_masked(**plain)
|
|
510
|
+
# No present pair is UNDEF (an empty group), set on the denominator so
|
|
511
|
+
# the division never meets an empty group's 0 / 0.
|
|
512
|
+
den = den.copy
|
|
513
|
+
den[cnt.eq(0)] = UNDEF
|
|
514
|
+
# min_count: counts the present pairs, as the core wmean does.
|
|
515
|
+
pairs = self.class.__build__(prod, spec)
|
|
516
|
+
AxisGroup.__finish_reduction__(pairs, :wmean, kw, num / den)
|
|
437
517
|
end
|
|
438
518
|
|
|
439
519
|
# ---- segment scan: within-group running statistics ---------------------
|
|
@@ -569,12 +649,13 @@ class CAGroupIterator
|
|
|
569
649
|
raise LocalJumpError, "no block given (yield)" unless block
|
|
570
650
|
ccat, _kd, gslots, bslots, gaxes = composite_layout
|
|
571
651
|
return value.group_by_category(ccat).map(data_type: data_type, &block) if bslots.empty?
|
|
572
|
-
|
|
573
|
-
out = CArray.new(dt, value.shape)
|
|
652
|
+
out = nil
|
|
574
653
|
each_band_block(ccat, gslots, bslots, gaxes) do |val_idx, _oi, _co, gi|
|
|
575
|
-
|
|
654
|
+
result = gi.map(data_type: data_type, &block)
|
|
655
|
+
out ||= blank_of(result, value.shape)
|
|
656
|
+
out[*val_idx] = result
|
|
576
657
|
end
|
|
577
|
-
out
|
|
658
|
+
out || (data_type ? CArray.new(data_type, value.shape) : blank_of(value, value.shape))
|
|
578
659
|
end
|
|
579
660
|
|
|
580
661
|
# @overload reduce { |members| ... }
|
|
@@ -622,7 +703,7 @@ class CAGroupIterator
|
|
|
622
703
|
# askable. Asking a one-cell array of the same data type lets the core's own
|
|
623
704
|
# refusal through, unworded by us.
|
|
624
705
|
def boolean_payload! (op)
|
|
625
|
-
CArray.new(value.data_type, [1]).public_send(op)
|
|
706
|
+
CArray.new(value.data_type, [1], bytes: value.bytes).public_send(op)
|
|
626
707
|
nil
|
|
627
708
|
end
|
|
628
709
|
|
|
@@ -696,10 +777,71 @@ class CAGroupIterator
|
|
|
696
777
|
"axis_group scan: folding a band into a scan (axis: [:group, k]) " \
|
|
697
778
|
"is not supported; a scan preserves the source shape"
|
|
698
779
|
end
|
|
780
|
+
return scan_by_core(op) unless kernel_scans?(op)
|
|
699
781
|
group_axes, bundles, = spec.reduce_plan([])
|
|
700
782
|
value.__axis_group_scan__(group_axes, bundles, op)
|
|
701
783
|
end
|
|
702
784
|
|
|
785
|
+
# Whether the scatter kernels read this payload: an integer or a float,
|
|
786
|
+
# not a Face. They answer there in the core's data types.
|
|
787
|
+
def kernel_reads_value?
|
|
788
|
+
! value.face? && (value.integer? || value.float?)
|
|
789
|
+
end
|
|
790
|
+
|
|
791
|
+
# Whether the scan kernel gives the core's answer: it reads an integer,
|
|
792
|
+
# float, boolean or object payload (not a complex, not a Face), and answers cumsum / cumprod in
|
|
793
|
+
# float64 (an object in object), cummax / cummin in the payload's data type
|
|
794
|
+
# and cumcount in int64 -- not the core's answer for a boolean's running sum.
|
|
795
|
+
def kernel_scans? (op)
|
|
796
|
+
return false if value.face? || value.complex?
|
|
797
|
+
return true if value.object? || op == :cumcount
|
|
798
|
+
core = CArray.new(value.data_type, [1], bytes: value.bytes).public_send(op).data_type
|
|
799
|
+
kernel = [:cumsum, :cumprod].include?(op) ? CA_FLOAT64 : value.data_type
|
|
800
|
+
core == kernel
|
|
801
|
+
end
|
|
802
|
+
|
|
803
|
+
# A scan the kernel does not answer as the core does: each group block is
|
|
804
|
+
# grouped through the composite categorical, whose scans are the core's.
|
|
805
|
+
def scan_by_core (op)
|
|
806
|
+
ccat, _kd, gslots, bslots, gaxes = composite_layout
|
|
807
|
+
return value.group_by_category(ccat).public_send(op) if bslots.empty?
|
|
808
|
+
out = nil
|
|
809
|
+
each_band_block(ccat, gslots, bslots, gaxes) do |val_idx, _oi, _co, gi|
|
|
810
|
+
result = gi.public_send(op)
|
|
811
|
+
out ||= blank_of(result, value.shape)
|
|
812
|
+
out[*val_idx] = result
|
|
813
|
+
end
|
|
814
|
+
out || blank_of(value, value.shape)
|
|
815
|
+
end
|
|
816
|
+
|
|
817
|
+
# A reduction of a payload the scatter kernel does not read, group by
|
|
818
|
+
# group through the composite categorical. Folding a band into the
|
|
819
|
+
# statistic needs the kernel.
|
|
820
|
+
def reduce_by_core (op, kw)
|
|
821
|
+
_, fused = AxisGroup.parse_axis(kw[:axis])
|
|
822
|
+
unless fused.empty?
|
|
823
|
+
raise ArgumentError,
|
|
824
|
+
"axis_group #{op}: folding a band into the statistic " \
|
|
825
|
+
"(axis: [:group, k]) is not supported for a #{value.class} of " \
|
|
826
|
+
"data type #{value.data_type_name}"
|
|
827
|
+
end
|
|
828
|
+
AxisGroup.__check_reduce_keywords__(op, kw)
|
|
829
|
+
AxisGroup.__finish_reduction__(self, op, kw, composite_order(op, []))
|
|
830
|
+
end
|
|
831
|
+
|
|
832
|
+
# An array of +shape+ of the same kind as +model+ -- its data type, or its
|
|
833
|
+
# Face -- with every cell UNDEF. A read-only Face cannot be written cell by
|
|
834
|
+
# cell, so its values are held in an object array.
|
|
835
|
+
def blank_of (model, shape)
|
|
836
|
+
out = if model.face? && model.read_only?
|
|
837
|
+
CArray.new(CA_OBJECT, shape)
|
|
838
|
+
else
|
|
839
|
+
model.__send__(:__blank_like__, shape)
|
|
840
|
+
end
|
|
841
|
+
out[] = UNDEF
|
|
842
|
+
out
|
|
843
|
+
end
|
|
844
|
+
|
|
703
845
|
# Dispatch an order statistic (median / percentile / quantile). No :group ->
|
|
704
846
|
# plain value reduction; :group -> per-group order statistics via the composite
|
|
705
847
|
# materialize. Folding a band into an order statistic (axis: [:group, k])
|
|
@@ -707,13 +849,19 @@ class CAGroupIterator
|
|
|
707
849
|
# operation, and remains a follow-up.
|
|
708
850
|
def order_stat (op, args, kw)
|
|
709
851
|
has_group, fused = AxisGroup.parse_axis(kw[:axis])
|
|
710
|
-
return value.public_send(op, *args) unless has_group
|
|
852
|
+
return value.public_send(op, *args, **kw) unless has_group
|
|
711
853
|
unless fused.empty?
|
|
712
854
|
raise NotImplementedError,
|
|
713
855
|
"axis_group: folding a band into an order statistic (axis: [:group, k]) " \
|
|
714
856
|
"is not supported; order statistics gather every member of a group"
|
|
715
857
|
end
|
|
716
|
-
|
|
858
|
+
AxisGroup.__check_reduce_keywords__(op, kw)
|
|
859
|
+
res = composite_order(op, args)
|
|
860
|
+
if op == :quantile
|
|
861
|
+
res.map { |r| AxisGroup.__finish_reduction__(self, op, kw, r) }
|
|
862
|
+
else
|
|
863
|
+
AxisGroup.__finish_reduction__(self, op, kw, res)
|
|
864
|
+
end
|
|
717
865
|
end
|
|
718
866
|
|
|
719
867
|
# Per-group order statistic over the composite grouping. A flat grouping (no
|
|
@@ -732,15 +880,15 @@ class CAGroupIterator
|
|
|
732
880
|
return nout == 1 ? res.reshape(*kdims) : res.map { |r| r.reshape(*kdims) }
|
|
733
881
|
end
|
|
734
882
|
out_shape = spec.slot_meta.map { |m| m[:kind] == :group ? m[:k] : m[:len] }
|
|
735
|
-
outs =
|
|
883
|
+
outs = nil
|
|
736
884
|
each_band_block(ccat, gslots, bslots, gaxes) do |_vi, out_idx, _co, gi|
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
end
|
|
885
|
+
results = gi.public_send(op, *args)
|
|
886
|
+
results = [results] unless results.is_a?(Array) # quantile answers five
|
|
887
|
+
# each output takes the kind the categorical answered in
|
|
888
|
+
outs ||= results.map { |r| blank_of(r, out_shape) }
|
|
889
|
+
results.each_with_index { |r, q| outs[q][*out_idx] = r.reshape(*kdims) }
|
|
743
890
|
end
|
|
891
|
+
outs ||= Array.new(nout) { blank_of(value, out_shape) }
|
|
744
892
|
nout == 1 ? outs[0] : outs
|
|
745
893
|
end
|
|
746
894
|
|
data/lib/carray/basics.rb
CHANGED
|
@@ -80,31 +80,31 @@ class CArray
|
|
|
80
80
|
Array.new(flat.length, repeat)
|
|
81
81
|
end
|
|
82
82
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
83
|
+
# Validate each value. A positive Integer only; nil is not a valid
|
|
84
|
+
# per-axis repeat.
|
|
85
|
+
reps.each do |r|
|
|
86
|
+
unless r.is_a?(Integer)
|
|
87
|
+
raise ArgumentError,
|
|
88
|
+
"insert_axis: repeat must be a positive Integer, got #{r.inspect}"
|
|
89
|
+
end
|
|
90
|
+
raise ArgumentError, "insert_axis: repeat count must be >= 1" if r < 1
|
|
89
91
|
end
|
|
90
|
-
raise ArgumentError, "insert_axis: repeat count must be >= 1" if r < 1
|
|
91
|
-
end
|
|
92
92
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
93
|
+
# Final output layout: stable order by (gap, argument index) keeps
|
|
94
|
+
# same-gap axes in argument order; the k-th inserted axis lands at output
|
|
95
|
+
# position gap + k. This output position only drives broadcast_to; the
|
|
96
|
+
# insertion itself always goes through the source-frame primitive.
|
|
97
|
+
order = (0...flat.length).sort_by { |i| [gaps[i], i] }
|
|
98
|
+
final = {}
|
|
99
|
+
order.each_with_index { |i, k| final[i] = gaps[i] + k }
|
|
100
100
|
|
|
101
|
-
|
|
102
|
-
|
|
101
|
+
inter = __insert_axis_size1__(*order.map { |i| gaps[i] })
|
|
102
|
+
return inter unless order.any? { |i| reps[i] > 1 }
|
|
103
103
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
end
|
|
104
|
+
shp = inter.shape
|
|
105
|
+
order.each { |i| shp[final[i]] = reps[i] if reps[i] > 1 }
|
|
106
|
+
inter.broadcast_to(*shp)
|
|
107
|
+
end
|
|
108
108
|
|
|
109
109
|
# @overload drop_axis
|
|
110
110
|
# Returns a view of `self` with every size-1 axis dropped.
|
|
@@ -142,6 +142,28 @@ end
|
|
|
142
142
|
return template(:boolean) { 1 }
|
|
143
143
|
end
|
|
144
144
|
|
|
145
|
+
# A Face is clipped in its own terms: its comparisons decide which cells are
|
|
146
|
+
# out of range (a time array reads its bounds as times, a record array
|
|
147
|
+
# orders by its declared members, a Face that cannot be ordered refuses),
|
|
148
|
+
# and its store writes the bound or the fill value into them.
|
|
149
|
+
private def __face_clip__ (min, max, fill_value, lfill, ufill)
|
|
150
|
+
lfill = fill_value if lfill.nil?
|
|
151
|
+
ufill = fill_value if ufill.nil?
|
|
152
|
+
out = copy
|
|
153
|
+
out[self < min] = lfill.nil? ? min : lfill unless min.nil?
|
|
154
|
+
out[self > max] = ufill.nil? ? max : ufill unless max.nil?
|
|
155
|
+
out
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# An array of +shape+ of the same kind as self: its data type and cell
|
|
159
|
+
# width, or its Face over a zero storage. Nothing is written into it, so
|
|
160
|
+
# it can be built for a read-only Face too.
|
|
161
|
+
private def __blank_like__ (shape)
|
|
162
|
+
storage = __face_storage__
|
|
163
|
+
out = CArray.new(storage.data_type, shape, bytes: storage.bytes)
|
|
164
|
+
face? ? out.face_lift(self) : out
|
|
165
|
+
end
|
|
166
|
+
|
|
145
167
|
# Sub-region copy (paste / crop), the old C `ca_paste` / `ca_clip` pair.
|
|
146
168
|
# `clip` is reserved for value clamping (clamp to a min/max range), so
|
|
147
169
|
# the read-out side is `crop`. Kept together as a pair (crop is rare, but paste is reached
|
|
@@ -250,6 +272,31 @@ end
|
|
|
250
272
|
|
|
251
273
|
# @!endgroup
|
|
252
274
|
|
|
275
|
+
# An Integer shift count on an integer array means what it means to
|
|
276
|
+
# Integer#<< and #>>: negative shifts the other way, and a count past the
|
|
277
|
+
# width shifts every bit out. The kernels do that for a count of the
|
|
278
|
+
# array's own type; this brings a count the type cannot hold -- -1 for a
|
|
279
|
+
# uint8 array, 300 for an int8 one -- to one it can.
|
|
280
|
+
module IntegerShiftCount
|
|
281
|
+
UNSIGNED = [CA_UINT8, CA_UINT16, CA_UINT32, CA_UINT64].freeze
|
|
282
|
+
|
|
283
|
+
def << (other)
|
|
284
|
+
return super unless other.is_a?(Integer) && integer? && ! face?
|
|
285
|
+
w = bytes * 8
|
|
286
|
+
return self >> -other if other < 0 && UNSIGNED.include?(data_type)
|
|
287
|
+
super(other.clamp(-w, w))
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
def >> (other)
|
|
291
|
+
return super unless other.is_a?(Integer) && integer? && ! face?
|
|
292
|
+
w = bytes * 8
|
|
293
|
+
return self << -other if other < 0 && UNSIGNED.include?(data_type)
|
|
294
|
+
super(other.clamp(-w, w))
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
# Prepended by carray/lazy, after it has wrapped << and >>: prepended
|
|
298
|
+
# here, the method lazy aliases as the eager form would be this one.
|
|
299
|
+
|
|
253
300
|
# @overload clip(min, max = nil, fill_value = nil, lfill: nil, ufill: nil)
|
|
254
301
|
# Returns `self` with every element clamped to `[min, max]`.
|
|
255
302
|
#
|
|
@@ -258,7 +305,12 @@ end
|
|
|
258
305
|
# `fill_value` (or `lfill` / `ufill`) is given, out-of-range
|
|
259
306
|
# cells are replaced by the fill instead of clamped -- pass
|
|
260
307
|
# `UNDEF` to mask that end. `fill_value` is sugar for symmetric
|
|
261
|
-
# dual-fill; `lfill` / `ufill` override per side
|
|
308
|
+
# dual-fill; `lfill` / `ufill` override per side, and a side
|
|
309
|
+
# given no fill is still clamped.
|
|
310
|
+
#
|
|
311
|
+
# `min` may not exceed `max` (in any cell, when a bound is an
|
|
312
|
+
# array); equal bounds give every unmasked cell that value.
|
|
313
|
+
# A NaN bound bounds nothing.
|
|
262
314
|
#
|
|
263
315
|
# Boundary is strict `[min, max]`: values equal to a bound
|
|
264
316
|
# remain unchanged in both the clamped and filled variants.
|
|
@@ -272,7 +324,8 @@ end
|
|
|
272
324
|
# @param lfill [Object, nil] override below-range fill.
|
|
273
325
|
# @param ufill [Object, nil] override above-range fill.
|
|
274
326
|
# @return [CArray] new CArray with clamped or filled values.
|
|
275
|
-
# @raise [ArgumentError] when both `min` and `max` are `nil
|
|
327
|
+
# @raise [ArgumentError] when both `min` and `max` are `nil`,
|
|
328
|
+
# or when `min` exceeds `max`.
|
|
276
329
|
# @example
|
|
277
330
|
# a.clip(0, 10) # strict clamp
|
|
278
331
|
# a.clip(0, 10, -1) # both ends -> -1
|
|
@@ -281,6 +334,16 @@ end
|
|
|
281
334
|
if min.nil? && max.nil?
|
|
282
335
|
raise ArgumentError, "clip: at least one of (min, max) must be given"
|
|
283
336
|
end
|
|
337
|
+
return __face_clip__(min, max, fill_value, lfill, ufill) if face?
|
|
338
|
+
if !min.nil? && !max.nil?
|
|
339
|
+
reversed = if min.is_a?(CArray) then min.gt(max).any
|
|
340
|
+
elsif max.is_a?(CArray) then max.lt(min).any
|
|
341
|
+
else min > max
|
|
342
|
+
end
|
|
343
|
+
if reversed
|
|
344
|
+
raise ArgumentError, "clip: min must be less than or equal to max"
|
|
345
|
+
end
|
|
346
|
+
end
|
|
284
347
|
|
|
285
348
|
# `fill_value` as a single argument is sugar applied to both ends; kwargs override.
|
|
286
349
|
lfill = fill_value if lfill.nil?
|
|
@@ -292,9 +355,12 @@ end
|
|
|
292
355
|
return pmin(max)
|
|
293
356
|
end
|
|
294
357
|
|
|
358
|
+
# A side without a fill still clamps.
|
|
295
359
|
out = self.copy
|
|
296
|
-
out
|
|
297
|
-
out
|
|
360
|
+
out = out.pmax(min) if !min.nil? && lfill.nil?
|
|
361
|
+
out = out.pmin(max) if !max.nil? && ufill.nil?
|
|
362
|
+
out[self.lt(min)] = lfill unless min.nil? || lfill.nil?
|
|
363
|
+
out[self.gt(max)] = ufill unless max.nil? || ufill.nil?
|
|
298
364
|
out
|
|
299
365
|
end
|
|
300
366
|
|
data/lib/carray/bincount_nd.rb
CHANGED
|
@@ -269,6 +269,12 @@ class CArray
|
|
|
269
269
|
term = strides_ext[k] == 1 ? b : b * strides_ext[k]
|
|
270
270
|
ravel = ravel.nil? ? term : ravel + term
|
|
271
271
|
end
|
|
272
|
+
# A NaN weight drops its sample, as the fiber kernel and histogram
|
|
273
|
+
# do; bincount itself would add the NaN in.
|
|
274
|
+
if tweights && tweights.float?
|
|
275
|
+
nan = tweights.is_nan
|
|
276
|
+
tweights = tweights.mask_where(nan) if nan.any
|
|
277
|
+
end
|
|
272
278
|
chunk_counts = ravel.bincount(weights: tweights, length: total_ext)
|
|
273
279
|
@full_counts[] = @full_counts + chunk_counts.reshape(*@full_counts.shape)
|
|
274
280
|
else
|