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
|
@@ -41,6 +41,8 @@
|
|
|
41
41
|
|
|
42
42
|
#include "ruby.h"
|
|
43
43
|
#include "carray.h"
|
|
44
|
+
#include "carray_internal.h" /* ca_integer_arg, ca_calling_method_name */
|
|
45
|
+
#include "ca_obj_face.h" /* ca_face_refuse_numeric */
|
|
44
46
|
#include <math.h>
|
|
45
47
|
|
|
46
48
|
/* Externals (link-time; kept out of carray.h to keep the header lean). */
|
|
@@ -50,6 +52,7 @@ extern VALUE rb_ca_insert_axis (int argc, VALUE *argv, VALUE self); /* c
|
|
|
50
52
|
static ID id_axis, id_sort_copy, id_is_not_masked;
|
|
51
53
|
/* object lane (arbitrary Ruby objects -> arithmetic is irreducibly funcall) */
|
|
52
54
|
static ID id_plus, id_div, id_mul, id_min, id_max, id_sort, id_copy;
|
|
55
|
+
static ID id_rationalize;
|
|
53
56
|
static ID id_aref, id_aset;
|
|
54
57
|
static VALUE sym_linear, sym_lower, sym_higher, sym_nearest, sym_midpoint;
|
|
55
58
|
|
|
@@ -71,13 +74,14 @@ mp_is_numeric (int8_t dt)
|
|
|
71
74
|
_(CA_INT64, int64_t) _(CA_UINT64, uint64_t) \
|
|
72
75
|
_(CA_FLOAT32, float32_t) _(CA_FLOAT64, double)
|
|
73
76
|
|
|
74
|
-
/* [numeric]
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
fmin ignores NaN (fmin(x,NaN)==x), so a
|
|
78
|
-
NaN and stays branch-free
|
|
79
|
-
|
|
80
|
-
|
|
77
|
+
/* [numeric] sorted[k+1] of a fiber partitioned at k = the min over the
|
|
78
|
+
upper region [k+1, n-1]. NaN sorts after every number, so it is the
|
|
79
|
+
upper element only when the region holds nothing else. stride = element
|
|
80
|
+
stride along the reduce axis. fmin ignores NaN (fmin(x,NaN)==x), so a
|
|
81
|
+
+INFINITY seed skips NaN and the loop stays branch-free and vectorised.
|
|
82
|
+
A result of +INFINITY is either a +INFINITY in the region or a region of
|
|
83
|
+
NaN only; the rescan that tells them apart runs only in that case. An
|
|
84
|
+
empty region (k == n-1) answers the kth value. */
|
|
81
85
|
#define MP_GEN_MINUP(CT, T) \
|
|
82
86
|
static double \
|
|
83
87
|
mp_minup_##T (const T *b, ca_size_t f0, long k, long n, long stride) { \
|
|
@@ -86,8 +90,11 @@ mp_is_numeric (int8_t dt)
|
|
|
86
90
|
double v = (double) b[f0 + (ca_size_t)(ai * stride)]; \
|
|
87
91
|
hi = fmin(hi, v); \
|
|
88
92
|
} \
|
|
89
|
-
|
|
90
|
-
|
|
93
|
+
if ( hi != (double) INFINITY ) return hi; \
|
|
94
|
+
if ( k + 1 >= n ) return (double) b[f0 + (ca_size_t)(k * stride)]; \
|
|
95
|
+
for ( long ai = k + 1; ai < n; ai++ ) \
|
|
96
|
+
if ( (double) b[f0 + (ca_size_t)(ai * stride)] == hi ) return hi; \
|
|
97
|
+
return (double) NAN; \
|
|
91
98
|
}
|
|
92
99
|
MP_TYPES(MP_GEN_MINUP)
|
|
93
100
|
|
|
@@ -137,7 +144,12 @@ static VALUE
|
|
|
137
144
|
mp_axis_all_masked (VALUE self, long axis, int keep_axis)
|
|
138
145
|
{
|
|
139
146
|
int8_t ax = (int8_t) axis;
|
|
140
|
-
|
|
147
|
+
CArray *ca;
|
|
148
|
+
GetCArray(self, ca);
|
|
149
|
+
VALUE out = rb_ca_new_reduced(self, &ax, 1,
|
|
150
|
+
(ca->data_type == CA_OBJECT) ? CA_OBJECT
|
|
151
|
+
: CA_FLOAT64,
|
|
152
|
+
keep_axis);
|
|
141
153
|
CArray *co;
|
|
142
154
|
GetCArray(out, co);
|
|
143
155
|
ca_create_mask(co);
|
|
@@ -185,8 +197,7 @@ pct_compute (VALUE method, long k, double r, long n, double lo, double hi)
|
|
|
185
197
|
return use_k ? lo : hi;
|
|
186
198
|
}
|
|
187
199
|
if ( method == sym_linear ) {
|
|
188
|
-
|
|
189
|
-
return lo * (1.0 - r) + hi * r;
|
|
200
|
+
return ca_percentile_linear(lo, hi, r, k, n);
|
|
190
201
|
}
|
|
191
202
|
if ( method == sym_midpoint ) {
|
|
192
203
|
if ( k + 1 >= n ) return lo;
|
|
@@ -240,7 +251,7 @@ median_fill (double *op, int8_t dt, const char *base,
|
|
|
240
251
|
MP_TYPES(MP_CASE)
|
|
241
252
|
#undef MP_CASE
|
|
242
253
|
default:
|
|
243
|
-
rb_raise(rb_eCADataTypeError, "median:
|
|
254
|
+
rb_raise(rb_eCADataTypeError, "median: source data_type :%s not supported", ca_type_name[dt]);
|
|
244
255
|
}
|
|
245
256
|
}
|
|
246
257
|
|
|
@@ -298,13 +309,13 @@ median_flat (VALUE self, long min_count, VALUE fill_value, int keep_axis)
|
|
|
298
309
|
if ( RTEST(rb_ca_has_mask(self)) ) {
|
|
299
310
|
CArray *mc; GetCArray(self, mc);
|
|
300
311
|
long cnt = (long) ca_count_not_masked(mc); /* = elements - count_masked */
|
|
301
|
-
if ( cnt
|
|
312
|
+
if ( ca_too_few_present(cnt, min_count, 1) ) filled = 1;
|
|
302
313
|
else src = rb_ca_fetch(self, ID2SYM(id_is_not_masked));
|
|
303
314
|
}
|
|
304
315
|
if ( !filled ) {
|
|
305
316
|
CArray *sc;
|
|
306
317
|
GetCArray(src, sc);
|
|
307
|
-
if ( sc->elements
|
|
318
|
+
if ( ca_too_few_present(sc->elements, min_count, 1) ) filled = 1;
|
|
308
319
|
}
|
|
309
320
|
|
|
310
321
|
if ( filled ) {
|
|
@@ -348,15 +359,65 @@ mp_object_interpolates (VALUE v)
|
|
|
348
359
|
}
|
|
349
360
|
if ( rb_obj_is_kind_of(v, rb_cCArray) ) {
|
|
350
361
|
CArray *ca;
|
|
362
|
+
ca_size_t i;
|
|
351
363
|
GetCArray(v, ca);
|
|
352
|
-
|
|
353
|
-
|
|
364
|
+
for ( i = 0; i < ca->elements; i++ ) {
|
|
365
|
+
VALUE e = rb_ca_fetch_addr(v, i);
|
|
366
|
+
if ( e != CA_UNDEF && ! rb_obj_is_kind_of(e, rb_cNumeric) ) {
|
|
367
|
+
return 0;
|
|
368
|
+
}
|
|
354
369
|
}
|
|
355
|
-
return
|
|
370
|
+
return 1;
|
|
356
371
|
}
|
|
357
372
|
return 0;
|
|
358
373
|
}
|
|
359
374
|
|
|
375
|
+
/* [object] The element an interpolation refusal names: lo itself, or the
|
|
376
|
+
first element of a CArray of them that has no arithmetic. */
|
|
377
|
+
static VALUE
|
|
378
|
+
mp_first_non_numeric (VALUE v)
|
|
379
|
+
{
|
|
380
|
+
if ( rb_obj_is_kind_of(v, rb_cCArray) ) {
|
|
381
|
+
CArray *ca;
|
|
382
|
+
ca_size_t i;
|
|
383
|
+
GetCArray(v, ca);
|
|
384
|
+
for ( i = 0; i < ca->elements; i++ ) {
|
|
385
|
+
VALUE e = rb_ca_fetch_addr(v, i);
|
|
386
|
+
if ( e != CA_UNDEF && ! rb_obj_is_kind_of(e, rb_cNumeric) ) {
|
|
387
|
+
return e;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
return v;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/* [object] An order statistic answers in the elements' own terms: a pick
|
|
395
|
+
is the element itself, and an average or an interpolation is done with
|
|
396
|
+
the elements' own arithmetic and an exact weight, so a Rational or a
|
|
397
|
+
BigDecimal stays exact and a Float stays a Float. */
|
|
398
|
+
|
|
399
|
+
/* [object] the weight of the upper element at p, exact: (n-1)*p/100 - k as
|
|
400
|
+
a Rational, with p read as the decimal it was written as. The double r
|
|
401
|
+
carries the rounding of (n-1)*p/100 (p = 30 over five elements is
|
|
402
|
+
0.19999999999999996 there, not 1/5), so it is not used for the weight. */
|
|
403
|
+
static VALUE
|
|
404
|
+
obj_weight (long n, double p, long k)
|
|
405
|
+
{
|
|
406
|
+
VALUE pr = rb_funcall(DBL2NUM(p), id_rationalize, 0);
|
|
407
|
+
VALUE f = rb_funcall(rb_funcall(pr, id_mul, 1, LONG2NUM(n - 1)), rb_intern("quo"), 1, INT2FIX(100));
|
|
408
|
+
return rb_funcall(f, rb_intern("-"), 1, LONG2NUM(k));
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/* The midpoint of lo and hi, (lo + hi) * (1/2): exact for an Integer or a
|
|
412
|
+
Rational, and the same for a cell (the flat path) and for a CArray of
|
|
413
|
+
them (the per-axis path). */
|
|
414
|
+
static VALUE
|
|
415
|
+
obj_midpoint (VALUE lo, VALUE hi)
|
|
416
|
+
{
|
|
417
|
+
return rb_funcall(rb_funcall(lo, id_plus, 1, hi), id_mul, 1,
|
|
418
|
+
rb_rational_new(INT2FIX(1), INT2FIX(2)));
|
|
419
|
+
}
|
|
420
|
+
|
|
360
421
|
/* [object] Refuse to interpolate between two elements that have no
|
|
361
422
|
arithmetic meaning it. A String has both a `*` and a `+`, and
|
|
362
423
|
String#* truncates the Float weight to zero -- so :linear quietly
|
|
@@ -370,12 +431,10 @@ mp_require_interpolable (const char *who, VALUE method, VALUE lo)
|
|
|
370
431
|
return;
|
|
371
432
|
}
|
|
372
433
|
rb_raise(rb_eCADataTypeError,
|
|
373
|
-
"%s: method: :%
|
|
374
|
-
"does not support; use method: :lower, :higher
|
|
375
|
-
"pick an element instead",
|
|
376
|
-
who,
|
|
377
|
-
rb_obj_classname(rb_obj_is_kind_of(lo, rb_cCArray)
|
|
378
|
-
? rb_ca_fetch_addr(lo, 0) : lo));
|
|
434
|
+
"%s: method: :%"PRIsVALUE" interpolates between two elements, "
|
|
435
|
+
"which %"PRIsVALUE" does not support; use method: :lower, :higher "
|
|
436
|
+
"or :nearest to pick an element instead",
|
|
437
|
+
who, rb_sym2str(method), rb_obj_class(mp_first_non_numeric(lo)));
|
|
379
438
|
}
|
|
380
439
|
|
|
381
440
|
/* [object] recv.mid(axis: raxis) -- e.g. min/max/sort along an axis. */
|
|
@@ -433,7 +492,8 @@ obj_kth_pair (VALUE self, long axis, long k, long n, VALUE *lo, VALUE *hi)
|
|
|
433
492
|
}
|
|
434
493
|
|
|
435
494
|
/* [object] keep_axis flat: [1,...,1] entity holding val (UNDEF assigns
|
|
436
|
-
masked). Uses []= so UNDEF / arbitrary
|
|
495
|
+
masked), in self's Face when self is one. Uses []= so UNDEF / arbitrary
|
|
496
|
+
objects store correctly. */
|
|
437
497
|
static VALUE
|
|
438
498
|
obj_keep_axis_full (VALUE self, VALUE val)
|
|
439
499
|
{
|
|
@@ -448,7 +508,7 @@ obj_keep_axis_full (VALUE self, VALUE val)
|
|
|
448
508
|
for ( int i = 0; i < ndim; i++ ) av[i] = INT2FIX(0);
|
|
449
509
|
av[ndim] = val;
|
|
450
510
|
rb_funcallv(out, id_aset, ndim + 1, av);
|
|
451
|
-
return out;
|
|
511
|
+
return ca_is_face(ca) ? ca_face_relift_entity(self, out) : out;
|
|
452
512
|
}
|
|
453
513
|
|
|
454
514
|
/* [object] per-axis median (= reduced CArray; 1-D self -> bare scalar).
|
|
@@ -467,23 +527,50 @@ median_object_axis (VALUE self, long axis, long n, int keep_axis)
|
|
|
467
527
|
if ( ! mp_object_interpolates(lo) ) {
|
|
468
528
|
rb_raise(rb_eCADataTypeError,
|
|
469
529
|
"median: an even number of elements has no middle one, and "
|
|
470
|
-
"%
|
|
471
|
-
"(or :higher / :nearest) to pick an element instead",
|
|
472
|
-
|
|
473
|
-
? rb_ca_fetch_addr(lo, 0) : lo));
|
|
530
|
+
"%"PRIsVALUE" cannot be averaged; use percentile(50, method: "
|
|
531
|
+
":lower) (or :higher / :nearest) to pick an element instead",
|
|
532
|
+
rb_obj_class(mp_first_non_numeric(lo)));
|
|
474
533
|
}
|
|
475
|
-
result =
|
|
534
|
+
result = obj_midpoint(lo, hi);
|
|
476
535
|
} else {
|
|
477
536
|
VALUE kv = obj_kth_one(self, axis, (n - 1) / 2);
|
|
478
|
-
result =
|
|
537
|
+
result = kv;
|
|
479
538
|
}
|
|
480
|
-
|
|
539
|
+
/* a 1-D self reduces to a bare object; mp_axis_answer keeps it */
|
|
540
|
+
if ( keep_axis && rb_obj_is_kind_of(result, rb_cCArray) ) {
|
|
481
541
|
VALUE ia[1] = { LONG2NUM(axis) };
|
|
482
542
|
result = rb_ca_insert_axis(1, ia, result);
|
|
483
543
|
}
|
|
484
544
|
return result;
|
|
485
545
|
}
|
|
486
546
|
|
|
547
|
+
/* [entry] An axis form's answer in the shape the other reductions give it.
|
|
548
|
+
A reduction that removes the only axis of a 1-D array is a bare value, as
|
|
549
|
+
sum(axis: 0) is, and keep_axis: makes it a one-cell array; the lanes answer
|
|
550
|
+
either way, so the answer is shaped here. An answer the object lane took
|
|
551
|
+
as a slice of its partitioned copy is made an array of its own, as the
|
|
552
|
+
other reductions' answers are. */
|
|
553
|
+
static VALUE
|
|
554
|
+
mp_axis_answer (VALUE self, VALUE res, int keep_axis)
|
|
555
|
+
{
|
|
556
|
+
CArray *ca;
|
|
557
|
+
GetCArray(self, ca);
|
|
558
|
+
int is_array = RTEST(rb_obj_is_kind_of(res, rb_cCArray));
|
|
559
|
+
if ( ca->ndim != 1 ) {
|
|
560
|
+
if ( is_array ) {
|
|
561
|
+
CArray *cr;
|
|
562
|
+
GetCArray(res, cr);
|
|
563
|
+
if ( ! ca_is_entity(cr) && ! ca_is_face(cr) ) res = rb_funcall(res, id_copy, 0);
|
|
564
|
+
}
|
|
565
|
+
return res;
|
|
566
|
+
}
|
|
567
|
+
if ( keep_axis ) {
|
|
568
|
+
if ( is_array ) return res;
|
|
569
|
+
return obj_keep_axis_full(self, res);
|
|
570
|
+
}
|
|
571
|
+
return is_array ? rb_funcall(res, id_aref, 1, INT2FIX(0)) : res;
|
|
572
|
+
}
|
|
573
|
+
|
|
487
574
|
/* [object] flat median: mask-strip / min_count, flatten, axis-0 reduce. */
|
|
488
575
|
static VALUE
|
|
489
576
|
median_object_flat (VALUE self, long min_count, VALUE fill_value, int keep_axis)
|
|
@@ -493,13 +580,13 @@ median_object_flat (VALUE self, long min_count, VALUE fill_value, int keep_axis)
|
|
|
493
580
|
if ( RTEST(rb_ca_has_mask(self)) ) {
|
|
494
581
|
CArray *mc; GetCArray(self, mc);
|
|
495
582
|
long cnt = (long) ca_count_not_masked(mc); /* = elements - count_masked */
|
|
496
|
-
if ( cnt
|
|
583
|
+
if ( ca_too_few_present(cnt, min_count, 1) ) filled = 1;
|
|
497
584
|
else src = rb_ca_fetch(self, ID2SYM(id_is_not_masked));
|
|
498
585
|
}
|
|
499
586
|
if ( !filled ) {
|
|
500
587
|
CArray *sc;
|
|
501
588
|
GetCArray(src, sc);
|
|
502
|
-
if ( sc->elements
|
|
589
|
+
if ( ca_too_few_present(sc->elements, min_count, 1) ) filled = 1;
|
|
503
590
|
}
|
|
504
591
|
if ( filled ) {
|
|
505
592
|
result = NIL_P(fill_value) ? CA_UNDEF : fill_value;
|
|
@@ -517,31 +604,28 @@ median_object_flat (VALUE self, long min_count, VALUE fill_value, int keep_axis)
|
|
|
517
604
|
pre-fetched). Reached ONLY by CA_OBJECT percentile/quantile -- never
|
|
518
605
|
by numeric (that path uses pct_compute) and never by median.
|
|
519
606
|
The :linear / :midpoint interpolation funcalls are irreducible for
|
|
520
|
-
arbitrary objects; the
|
|
521
|
-
match the numeric "always Float" output contract. */
|
|
607
|
+
arbitrary objects; the pick methods return the element itself. */
|
|
522
608
|
static VALUE
|
|
523
|
-
pct_compute_object (VALUE method, long k, double r, long n, VALUE lo, VALUE hi)
|
|
609
|
+
pct_compute_object (VALUE method, double p, long k, double r, long n, VALUE lo, VALUE hi)
|
|
524
610
|
{
|
|
525
|
-
if ( method == sym_lower ) return
|
|
526
|
-
if ( method == sym_higher )
|
|
527
|
-
VALUE v = (r == 0.0) ? lo : hi;
|
|
528
|
-
return rb_funcall(v, id_mul, 1, DBL2NUM(1.0));
|
|
529
|
-
}
|
|
611
|
+
if ( method == sym_lower ) return lo;
|
|
612
|
+
if ( method == sym_higher ) return (r == 0.0) ? lo : hi;
|
|
530
613
|
if ( method == sym_nearest ) {
|
|
531
614
|
int use_k = (r < 0.5) || (r == 0.5 && (k % 2 == 0));
|
|
532
|
-
return
|
|
615
|
+
return use_k ? lo : hi;
|
|
533
616
|
}
|
|
534
617
|
if ( method == sym_linear ) {
|
|
535
|
-
if ( r == 0.0 || k + 1 >= n ) return
|
|
618
|
+
if ( r == 0.0 || k + 1 >= n ) return lo;
|
|
536
619
|
mp_require_interpolable("percentile", method, lo);
|
|
537
|
-
VALUE
|
|
538
|
-
VALUE
|
|
620
|
+
VALUE w = obj_weight(n, p, k);
|
|
621
|
+
VALUE a = rb_funcall(lo, id_mul, 1, rb_funcall(INT2FIX(1), rb_intern("-"), 1, w));
|
|
622
|
+
VALUE b = rb_funcall(hi, id_mul, 1, w);
|
|
539
623
|
return rb_funcall(a, id_plus, 1, b);
|
|
540
624
|
}
|
|
541
625
|
if ( method == sym_midpoint ) {
|
|
542
|
-
if ( k + 1 >= n ) return
|
|
626
|
+
if ( k + 1 >= n ) return lo;
|
|
543
627
|
mp_require_interpolable("percentile", method, lo);
|
|
544
|
-
return
|
|
628
|
+
return obj_midpoint(lo, hi);
|
|
545
629
|
}
|
|
546
630
|
rb_raise(rb_eArgError, "percentile: invalid method (BUG)");
|
|
547
631
|
}
|
|
@@ -550,11 +634,8 @@ pct_compute_object (VALUE method, long k, double r, long n, VALUE lo, VALUE hi)
|
|
|
550
634
|
static VALUE
|
|
551
635
|
pct_object_one_partition (VALUE self, long axis, long n, double p, VALUE method)
|
|
552
636
|
{
|
|
553
|
-
if ( p == 100.0 )
|
|
554
|
-
return rb_funcall(obj_call_axis(self, id_max, LONG2NUM(axis)),
|
|
555
|
-
id_mul, 1, DBL2NUM(1.0));
|
|
556
637
|
if ( n == 1 )
|
|
557
|
-
return
|
|
638
|
+
return obj_slice(self, axis, 0);
|
|
558
639
|
double f = (n - 1) * p / 100.0;
|
|
559
640
|
long k = (long) floor(f);
|
|
560
641
|
double r = f - k;
|
|
@@ -562,7 +643,7 @@ pct_object_one_partition (VALUE self, long axis, long n, double p, VALUE method)
|
|
|
562
643
|
VALUE lo = Qnil, hi = Qnil;
|
|
563
644
|
if ( need == PCT_LOWER_ONLY ) lo = obj_kth_one(self, axis, k);
|
|
564
645
|
else obj_kth_pair(self, axis, k, n, &lo, &hi);
|
|
565
|
-
return pct_compute_object(method, k, r, n, lo, hi);
|
|
646
|
+
return pct_compute_object(method, p, k, r, n, lo, hi);
|
|
566
647
|
}
|
|
567
648
|
|
|
568
649
|
/* [object] single p from a fully sorted entity (multi-p shared sort). */
|
|
@@ -570,9 +651,9 @@ static VALUE
|
|
|
570
651
|
pct_object_one_sorted (VALUE sorted, long axis, long n, double p, VALUE method)
|
|
571
652
|
{
|
|
572
653
|
if ( p == 100.0 )
|
|
573
|
-
return
|
|
654
|
+
return obj_slice(sorted, axis, n - 1);
|
|
574
655
|
if ( n == 1 )
|
|
575
|
-
return
|
|
656
|
+
return obj_slice(sorted, axis, 0);
|
|
576
657
|
double f = (n - 1) * p / 100.0;
|
|
577
658
|
long k = (long) floor(f);
|
|
578
659
|
double r = f - k;
|
|
@@ -581,7 +662,7 @@ pct_object_one_sorted (VALUE sorted, long axis, long n, double p, VALUE method)
|
|
|
581
662
|
VALUE lo = Qnil, hi = Qnil;
|
|
582
663
|
if ( need != PCT_UPPER_ONLY ) lo = obj_slice(sorted, axis, k);
|
|
583
664
|
if ( need != PCT_LOWER_ONLY ) hi = obj_slice(sorted, axis, kup);
|
|
584
|
-
return pct_compute_object(method, k, r, n, lo, hi);
|
|
665
|
+
return pct_compute_object(method, p, k, r, n, lo, hi);
|
|
585
666
|
}
|
|
586
667
|
|
|
587
668
|
/* [object] per-axis percentile -> array of (reduced CArray | scalar),
|
|
@@ -633,7 +714,9 @@ pct_object_axis_masked_one (VALUE self, VALUE sorted, long axis, double p,
|
|
|
633
714
|
VALUE fill_value, int keep_axis)
|
|
634
715
|
{
|
|
635
716
|
int8_t ax = (int8_t) axis;
|
|
636
|
-
|
|
717
|
+
/* `sorted` is an entity of self's shape (the storage, when self is a
|
|
718
|
+
Face); the answer is built on it and lifted back by the caller. */
|
|
719
|
+
VALUE out = rb_ca_new_reduced(sorted, &ax, 1, CA_OBJECT, keep_axis);
|
|
637
720
|
CArray *co;
|
|
638
721
|
GetCArray(out, co);
|
|
639
722
|
CArray *cs;
|
|
@@ -652,7 +735,7 @@ pct_object_axis_masked_one (VALUE self, VALUE sorted, long axis, double p,
|
|
|
652
735
|
for ( long j = 0; j < M; j++ )
|
|
653
736
|
if ( !sm[f0 + (ca_size_t)(j * INNER)] ) np++;
|
|
654
737
|
}
|
|
655
|
-
if ( np
|
|
738
|
+
if ( ca_too_few_present(np, min_count, 1) ) {
|
|
656
739
|
if ( is_undef ) {
|
|
657
740
|
if ( !co->mask ) ca_create_mask(co);
|
|
658
741
|
((boolean8_t *) co->mask->ptr)[oc] = 1;
|
|
@@ -670,7 +753,7 @@ pct_object_axis_masked_one (VALUE self, VALUE sorted, long axis, double p,
|
|
|
670
753
|
VALUE lo = Qnil, hi = Qnil;
|
|
671
754
|
if ( need != PCT_UPPER_ONLY ) lo = sb[f0 + (ca_size_t)(k * INNER)];
|
|
672
755
|
if ( need != PCT_LOWER_ONLY ) hi = sb[f0 + (ca_size_t)(kup * INNER)];
|
|
673
|
-
op[oc] = pct_compute_object(method, k, r, np, lo, hi);
|
|
756
|
+
op[oc] = pct_compute_object(method, p, k, r, np, lo, hi);
|
|
674
757
|
}
|
|
675
758
|
}
|
|
676
759
|
return out;
|
|
@@ -695,7 +778,11 @@ percentile_object_axis_masked (VALUE self, VALUE pers, long axis, VALUE method,
|
|
|
695
778
|
return res;
|
|
696
779
|
}
|
|
697
780
|
|
|
781
|
+
/* A Face (a string array) is sorted as itself and read through its
|
|
782
|
+
storage, whose cells are the values. */
|
|
698
783
|
VALUE sorted = rb_funcall(obj_call_axis(self, id_sort, LONG2NUM(axis)), id_copy, 0);
|
|
784
|
+
int is_face = ca_is_face(ca);
|
|
785
|
+
if ( is_face ) sorted = rb_ca_strip_face_value(sorted);
|
|
699
786
|
CArray *cs;
|
|
700
787
|
GetCArray(sorted, cs);
|
|
701
788
|
const boolean8_t *sm = cs->mask ? (const boolean8_t *) cs->mask->ptr : NULL;
|
|
@@ -706,10 +793,10 @@ percentile_object_axis_masked (VALUE self, VALUE pers, long axis, VALUE method,
|
|
|
706
793
|
VALUE result = rb_ary_new_capa(npers);
|
|
707
794
|
for ( long i = 0; i < npers; i++ ) {
|
|
708
795
|
double p = NUM2DBL(rb_ary_entry(pers, i));
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
796
|
+
VALUE one = pct_object_axis_masked_one(self, sorted, axis, p, method,
|
|
797
|
+
OUTER, MM, INNER, sm, min_count,
|
|
798
|
+
fill_value, keep_axis);
|
|
799
|
+
rb_ary_push(result, is_face ? ca_face_relift_entity(self, one) : one);
|
|
713
800
|
}
|
|
714
801
|
RB_GC_GUARD(sorted);
|
|
715
802
|
return result;
|
|
@@ -738,13 +825,13 @@ percentile_object_flat (VALUE self, VALUE pers, long min_count, VALUE fill_value
|
|
|
738
825
|
if ( RTEST(rb_ca_has_mask(self)) ) {
|
|
739
826
|
CArray *mc; GetCArray(self, mc);
|
|
740
827
|
long cnt = (long) ca_count_not_masked(mc); /* = elements - count_masked */
|
|
741
|
-
if ( cnt
|
|
828
|
+
if ( ca_too_few_present(cnt, min_count, 1) ) filled = 1;
|
|
742
829
|
else src = rb_ca_fetch(self, ID2SYM(id_is_not_masked));
|
|
743
830
|
}
|
|
744
831
|
if ( !filled ) {
|
|
745
832
|
CArray *sc;
|
|
746
833
|
GetCArray(src, sc);
|
|
747
|
-
if ( sc->elements
|
|
834
|
+
if ( ca_too_few_present(sc->elements, min_count, 1) ) filled = 1;
|
|
748
835
|
}
|
|
749
836
|
if ( filled ) {
|
|
750
837
|
VALUE fill = NIL_P(fill_value) ? CA_UNDEF : fill_value;
|
|
@@ -765,7 +852,7 @@ static VALUE median_axis_masked (VALUE self, long axis, long min_count,
|
|
|
765
852
|
/* [entry] median(axis:, min_count:, fill_value:, keep_axis:). Dispatches
|
|
766
853
|
fixlen -> reject, object -> object lane, numeric -> numeric lane. */
|
|
767
854
|
static VALUE
|
|
768
|
-
|
|
855
|
+
rb_ca_median_m_body (int argc, VALUE *argv, VALUE self)
|
|
769
856
|
{
|
|
770
857
|
VALUE ropt = rb_pop_options(&argc, &argv);
|
|
771
858
|
VALUE raxis = Qnil, rmin_count = INT2FIX(0), fill_value = Qnil, rkeep = Qfalse;
|
|
@@ -776,6 +863,15 @@ rb_ca_median_m (int argc, VALUE *argv, VALUE self)
|
|
|
776
863
|
|
|
777
864
|
CArray *ca;
|
|
778
865
|
GetCArray(self, ca);
|
|
866
|
+
/* A lazy expression is computed whole here, so a registered evaluator
|
|
867
|
+
may compute it instead. */
|
|
868
|
+
{
|
|
869
|
+
volatile VALUE evaluated = ca_lazy_evaluated(self);
|
|
870
|
+
if ( ! NIL_P(evaluated) ) {
|
|
871
|
+
self = evaluated;
|
|
872
|
+
GetCArray(self, ca);
|
|
873
|
+
}
|
|
874
|
+
}
|
|
779
875
|
/* Boolean rides the f64 lane: 0/1 -> 0.0/1.0, so median interpolates and
|
|
780
876
|
returns a float (a 2-element median averages to 0.5), matching how the
|
|
781
877
|
numeric lane treats integer input. */
|
|
@@ -784,17 +880,25 @@ rb_ca_median_m (int argc, VALUE *argv, VALUE self)
|
|
|
784
880
|
GetCArray(self, ca);
|
|
785
881
|
}
|
|
786
882
|
int8_t dt = ca->data_type;
|
|
787
|
-
if ( ca_is_fixlen_type(ca) )
|
|
883
|
+
if ( ca_is_fixlen_type(ca) ) {
|
|
884
|
+
ca_face_refuse_numeric(self, "median", rb_eCADataTypeError);
|
|
788
885
|
rb_raise(rb_eCADataTypeError,
|
|
789
886
|
"median: not defined for fixlen (no numeric midpoint); "
|
|
790
887
|
"use a numeric or object array");
|
|
888
|
+
}
|
|
791
889
|
int is_obj = (dt == CA_OBJECT);
|
|
792
890
|
if ( !is_obj && !mp_is_numeric(dt) )
|
|
793
|
-
rb_raise(rb_eCADataTypeError, "median:
|
|
891
|
+
rb_raise(rb_eCADataTypeError, "median: source data_type :%s not supported", ca_type_name[dt]);
|
|
794
892
|
|
|
795
|
-
long min_count =
|
|
796
|
-
|
|
797
|
-
|
|
893
|
+
long min_count = ca_min_count_arg(rmin_count, NULL);
|
|
894
|
+
/* fill_value: UNDEF leaves the result undefined; any other fill is what
|
|
895
|
+
a store into the float64 result would make of it. */
|
|
896
|
+
if ( fill_value == CA_UNDEF ) {
|
|
897
|
+
fill_value = Qnil;
|
|
898
|
+
}
|
|
899
|
+
else if ( ! NIL_P(fill_value) && ! is_obj ) {
|
|
900
|
+
fill_value = ca_fill_as(fill_value, CA_FLOAT64);
|
|
901
|
+
}
|
|
798
902
|
int keep_axis = RTEST(rkeep);
|
|
799
903
|
|
|
800
904
|
if ( !NIL_P(raxis) ) {
|
|
@@ -802,16 +906,23 @@ rb_ca_median_m (int argc, VALUE *argv, VALUE self)
|
|
|
802
906
|
long n = (long) ca->dim[axis];
|
|
803
907
|
if ( n == 0 ) return mp_axis_all_masked(self, axis, keep_axis);
|
|
804
908
|
/* mask / min_count / fill_value require the per-fiber select (each fiber
|
|
805
|
-
has its own n_present); the plain partition path assumes a uniform n.
|
|
806
|
-
|
|
807
|
-
|
|
909
|
+
has its own n_present); the plain partition path assumes a uniform n.
|
|
910
|
+
Switch ca_fast_paths_enabled(): off, the per-fiber select. */
|
|
911
|
+
VALUE res;
|
|
912
|
+
if ( RTEST(rb_ca_has_mask(self)) || min_count > 0 || !NIL_P(fill_value)
|
|
913
|
+
|| ! ca_fast_paths_enabled() )
|
|
914
|
+
res = is_obj
|
|
808
915
|
? median_object_axis_masked(self, axis, min_count, fill_value, keep_axis)
|
|
809
916
|
: median_axis_masked(self, axis, min_count, fill_value, keep_axis);
|
|
810
|
-
|
|
811
|
-
|
|
917
|
+
else
|
|
918
|
+
res = is_obj ? median_object_axis(self, axis, n, keep_axis)
|
|
919
|
+
: median_axis(self, axis, n, keep_axis);
|
|
920
|
+
return mp_axis_answer(self, res, keep_axis);
|
|
812
921
|
}
|
|
813
|
-
|
|
814
|
-
|
|
922
|
+
/* self may be the float64 view made above, read through its CArray */
|
|
923
|
+
CA_RETURN_KEEPING(self,
|
|
924
|
+
is_obj ? median_object_flat(self, min_count, fill_value, keep_axis)
|
|
925
|
+
: median_flat(self, min_count, fill_value, keep_axis));
|
|
815
926
|
}
|
|
816
927
|
|
|
817
928
|
/* =====================================================================
|
|
@@ -858,7 +969,7 @@ pct_fill (double *op, int8_t dt, const char *base, int from_sorted,
|
|
|
858
969
|
MP_TYPES(MP_CASE)
|
|
859
970
|
#undef MP_CASE
|
|
860
971
|
default:
|
|
861
|
-
rb_raise(rb_eCADataTypeError, "percentile:
|
|
972
|
+
rb_raise(rb_eCADataTypeError, "percentile: source data_type :%s not supported", ca_type_name[dt]);
|
|
862
973
|
}
|
|
863
974
|
}
|
|
864
975
|
|
|
@@ -886,7 +997,7 @@ pct_fill (double *op, int8_t dt, const char *base, int from_sorted,
|
|
|
886
997
|
for ( long j = 0; j < M; j++ ) \
|
|
887
998
|
if ( !sm[f0 + (ca_size_t)(j * INNER)] ) np++; \
|
|
888
999
|
} \
|
|
889
|
-
if ( np
|
|
1000
|
+
if ( ca_too_few_present(np, min_count, 1) ) { \
|
|
890
1001
|
if ( is_undef ) { \
|
|
891
1002
|
if ( !co->mask ) ca_create_mask(co); \
|
|
892
1003
|
((boolean8_t *) co->mask->ptr)[oc] = 1; \
|
|
@@ -927,7 +1038,7 @@ pct_fill_masked (CArray *co, int8_t dt, const char *base, const boolean8_t *sm,
|
|
|
927
1038
|
MP_TYPES(MP_CASE)
|
|
928
1039
|
#undef MP_CASE
|
|
929
1040
|
default:
|
|
930
|
-
rb_raise(rb_eCADataTypeError, "percentile:
|
|
1041
|
+
rb_raise(rb_eCADataTypeError, "percentile: source data_type :%s not supported", ca_type_name[dt]);
|
|
931
1042
|
}
|
|
932
1043
|
}
|
|
933
1044
|
|
|
@@ -1116,7 +1227,7 @@ pct_flat (VALUE self, VALUE pers_ary, long min_count, VALUE fill_value,
|
|
|
1116
1227
|
if ( RTEST(rb_ca_has_mask(self)) ) {
|
|
1117
1228
|
CArray *mc; GetCArray(self, mc);
|
|
1118
1229
|
long cnt = (long) ca_count_not_masked(mc); /* = elements - count_masked */
|
|
1119
|
-
if ( cnt
|
|
1230
|
+
if ( ca_too_few_present(cnt, min_count, 1) ) {
|
|
1120
1231
|
masked_out = 1; is_undef = NIL_P(fill_value);
|
|
1121
1232
|
if ( !is_undef ) fillv = NUM2DBL(fill_value);
|
|
1122
1233
|
} else {
|
|
@@ -1126,7 +1237,7 @@ pct_flat (VALUE self, VALUE pers_ary, long min_count, VALUE fill_value,
|
|
|
1126
1237
|
if ( !masked_out ) {
|
|
1127
1238
|
CArray *sc;
|
|
1128
1239
|
GetCArray(src, sc);
|
|
1129
|
-
if ( sc->elements
|
|
1240
|
+
if ( ca_too_few_present(sc->elements, min_count, 1) ) {
|
|
1130
1241
|
masked_out = 1; is_undef = NIL_P(fill_value);
|
|
1131
1242
|
if ( !is_undef ) fillv = NUM2DBL(fill_value);
|
|
1132
1243
|
}
|
|
@@ -1174,7 +1285,10 @@ pct_flatten_validate_pers (VALUE pers)
|
|
|
1174
1285
|
rb_raise(rb_eArgError, "percentile: at least one p value required");
|
|
1175
1286
|
for ( long i = 0; i < len; i++ ) {
|
|
1176
1287
|
VALUE p = rb_ary_entry(pers, i);
|
|
1177
|
-
|
|
1288
|
+
/* Written as "inside", so that NaN -- which fails every comparison --
|
|
1289
|
+
is refused rather than let through. */
|
|
1290
|
+
if ( !rb_obj_is_kind_of(p, rb_cNumeric) ||
|
|
1291
|
+
!(NUM2DBL(p) >= 0.0 && NUM2DBL(p) <= 100.0) )
|
|
1178
1292
|
rb_raise(rb_eArgError,
|
|
1179
1293
|
"percentile: p must be Numeric in [0,100] (got %"PRIsVALUE")",
|
|
1180
1294
|
rb_inspect(p));
|
|
@@ -1186,9 +1300,8 @@ pct_flatten_validate_pers (VALUE pers)
|
|
|
1186
1300
|
keep_axis:). Dispatches fixlen -> reject, object -> object lane,
|
|
1187
1301
|
numeric -> numeric lane. */
|
|
1188
1302
|
static VALUE
|
|
1189
|
-
|
|
1303
|
+
rb_ca_percentile_kw (int argc, VALUE *argv, VALUE ropt, VALUE self)
|
|
1190
1304
|
{
|
|
1191
|
-
VALUE ropt = rb_pop_options(&argc, &argv);
|
|
1192
1305
|
VALUE raxis = Qnil, rmin_count = INT2FIX(0), fill_value = Qnil,
|
|
1193
1306
|
method = sym_linear, rkeep = Qfalse;
|
|
1194
1307
|
rb_scan_options(ropt, "axis,min_count,fill_value,method,keep_axis",
|
|
@@ -1196,6 +1309,15 @@ rb_ca_percentile_m (int argc, VALUE *argv, VALUE self)
|
|
|
1196
1309
|
|
|
1197
1310
|
CArray *ca;
|
|
1198
1311
|
GetCArray(self, ca);
|
|
1312
|
+
/* A lazy expression is computed whole here, so a registered evaluator
|
|
1313
|
+
may compute it instead. */
|
|
1314
|
+
{
|
|
1315
|
+
volatile VALUE evaluated = ca_lazy_evaluated(self);
|
|
1316
|
+
if ( ! NIL_P(evaluated) ) {
|
|
1317
|
+
self = evaluated;
|
|
1318
|
+
GetCArray(self, ca);
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1199
1321
|
/* Boolean rides the f64 lane (0/1 -> 0.0/1.0), so percentile / quantile
|
|
1200
1322
|
interpolate and return a float, matching the integer lane. */
|
|
1201
1323
|
if ( ca->data_type == CA_BOOLEAN ) {
|
|
@@ -1203,16 +1325,24 @@ rb_ca_percentile_m (int argc, VALUE *argv, VALUE self)
|
|
|
1203
1325
|
GetCArray(self, ca);
|
|
1204
1326
|
}
|
|
1205
1327
|
int8_t dt = ca->data_type;
|
|
1206
|
-
if ( ca_is_fixlen_type(ca) )
|
|
1328
|
+
if ( ca_is_fixlen_type(ca) ) {
|
|
1329
|
+
ca_face_refuse_numeric(self, "percentile", rb_eCADataTypeError);
|
|
1207
1330
|
rb_raise(rb_eCADataTypeError,
|
|
1208
1331
|
"percentile: not defined for fixlen; use a numeric or object array");
|
|
1332
|
+
}
|
|
1209
1333
|
int is_obj = (dt == CA_OBJECT);
|
|
1210
1334
|
if ( !is_obj && !mp_is_numeric(dt) )
|
|
1211
|
-
rb_raise(rb_eCADataTypeError, "percentile:
|
|
1335
|
+
rb_raise(rb_eCADataTypeError, "percentile: source data_type :%s not supported", ca_type_name[dt]);
|
|
1212
1336
|
|
|
1213
|
-
long min_count =
|
|
1214
|
-
|
|
1215
|
-
|
|
1337
|
+
long min_count = ca_min_count_arg(rmin_count, NULL);
|
|
1338
|
+
/* fill_value: UNDEF leaves the result undefined; any other fill is what
|
|
1339
|
+
a store into the float64 result would make of it. */
|
|
1340
|
+
if ( fill_value == CA_UNDEF ) {
|
|
1341
|
+
fill_value = Qnil;
|
|
1342
|
+
}
|
|
1343
|
+
else if ( ! NIL_P(fill_value) && ! is_obj ) {
|
|
1344
|
+
fill_value = ca_fill_as(fill_value, CA_FLOAT64);
|
|
1345
|
+
}
|
|
1216
1346
|
|
|
1217
1347
|
VALUE pers = pct_flatten_validate_pers(rb_ary_new_from_values(argc, argv));
|
|
1218
1348
|
pct_validate_method(method);
|
|
@@ -1230,12 +1360,13 @@ rb_ca_percentile_m (int argc, VALUE *argv, VALUE self)
|
|
|
1230
1360
|
flattened first and follows the same rule (length 1 unwraps). */
|
|
1231
1361
|
VALUE result;
|
|
1232
1362
|
if ( !NIL_P(raxis) ) {
|
|
1233
|
-
long axis = rb_ca_normalize_axis_value(self, raxis,
|
|
1363
|
+
long axis = rb_ca_normalize_axis_value(self, raxis, NULL);
|
|
1234
1364
|
/* mask / min_count / fill_value require the per-fiber select (each fiber
|
|
1235
1365
|
has its own n_present); the plain partition/sort path assumes a
|
|
1236
|
-
uniform n. These paths bake keep_axis into the reduced output.
|
|
1366
|
+
uniform n. These paths bake keep_axis into the reduced output.
|
|
1367
|
+
Switch ca_fast_paths_enabled(): off, the per-fiber select. */
|
|
1237
1368
|
int per_fiber = RTEST(rb_ca_has_mask(self)) || min_count > 0
|
|
1238
|
-
|| !NIL_P(fill_value);
|
|
1369
|
+
|| !NIL_P(fill_value) || ! ca_fast_paths_enabled();
|
|
1239
1370
|
if ( !is_obj ) {
|
|
1240
1371
|
result = per_fiber
|
|
1241
1372
|
? pct_axis_masked(self, pers, axis, method, min_count, fill_value, keep_axis)
|
|
@@ -1245,7 +1376,7 @@ rb_ca_percentile_m (int argc, VALUE *argv, VALUE self)
|
|
|
1245
1376
|
min_count, fill_value, keep_axis);
|
|
1246
1377
|
} else {
|
|
1247
1378
|
result = percentile_object_axis(self, pers, axis, method);
|
|
1248
|
-
if ( keep_axis ) {
|
|
1379
|
+
if ( keep_axis && ca->ndim > 1 ) {
|
|
1249
1380
|
long len = RARRAY_LEN(result);
|
|
1250
1381
|
VALUE w = rb_ary_new_capa(len);
|
|
1251
1382
|
VALUE ia[1] = { LONG2NUM(axis) };
|
|
@@ -1254,6 +1385,11 @@ rb_ca_percentile_m (int argc, VALUE *argv, VALUE self)
|
|
|
1254
1385
|
result = w;
|
|
1255
1386
|
}
|
|
1256
1387
|
}
|
|
1388
|
+
{
|
|
1389
|
+
long len = RARRAY_LEN(result);
|
|
1390
|
+
for ( long i = 0; i < len; i++ )
|
|
1391
|
+
rb_ary_store(result, i, mp_axis_answer(self, rb_ary_entry(result, i), keep_axis));
|
|
1392
|
+
}
|
|
1257
1393
|
} else if ( !is_obj ) {
|
|
1258
1394
|
result = pct_flat(self, pers, min_count, fill_value, method, keep_axis);
|
|
1259
1395
|
} else {
|
|
@@ -1266,16 +1402,25 @@ rb_ca_percentile_m (int argc, VALUE *argv, VALUE self)
|
|
|
1266
1402
|
result = w;
|
|
1267
1403
|
}
|
|
1268
1404
|
}
|
|
1405
|
+
/* self may be the float64 view made above, read through its CArray */
|
|
1406
|
+
RB_GC_GUARD(self);
|
|
1269
1407
|
|
|
1270
1408
|
if ( single_p ) return rb_ary_entry(result, 0);
|
|
1271
1409
|
return result;
|
|
1272
1410
|
}
|
|
1273
1411
|
|
|
1412
|
+
static VALUE
|
|
1413
|
+
rb_ca_percentile_m_body (int argc, VALUE *argv, VALUE self)
|
|
1414
|
+
{
|
|
1415
|
+
VALUE ropt = rb_pop_options(&argc, &argv);
|
|
1416
|
+
return rb_ca_percentile_kw(argc, argv, ropt, self);
|
|
1417
|
+
}
|
|
1418
|
+
|
|
1274
1419
|
/* [entry] quantile(axis:, keep_axis:) = percentile(0, 25, 50, 75, 100, ...).
|
|
1275
1420
|
Return shape: Array<Float> len 5 (flat) or Array<CArray> len 5 (per-axis),
|
|
1276
1421
|
matching percentile's multi-p wrapping since we pass 5 fixed p values. */
|
|
1277
1422
|
static VALUE
|
|
1278
|
-
|
|
1423
|
+
rb_ca_quantile_m_body (int argc, VALUE *argv, VALUE self)
|
|
1279
1424
|
{
|
|
1280
1425
|
VALUE ropt = rb_pop_options(&argc, &argv);
|
|
1281
1426
|
if ( argc != 0 )
|
|
@@ -1285,9 +1430,62 @@ rb_ca_quantile_m (int argc, VALUE *argv, VALUE self)
|
|
|
1285
1430
|
VALUE opts = rb_hash_new();
|
|
1286
1431
|
if ( !NIL_P(raxis) ) rb_hash_aset(opts, ID2SYM(rb_intern("axis")), raxis);
|
|
1287
1432
|
rb_hash_aset(opts, ID2SYM(rb_intern("keep_axis")), rkeep);
|
|
1288
|
-
VALUE pa[
|
|
1289
|
-
|
|
1290
|
-
|
|
1433
|
+
VALUE pa[5] = { INT2FIX(0), INT2FIX(25), INT2FIX(50), INT2FIX(75), INT2FIX(100) };
|
|
1434
|
+
return rb_ca_percentile_kw(5, pa, opts, self);
|
|
1435
|
+
}
|
|
1436
|
+
|
|
1437
|
+
|
|
1438
|
+
typedef struct { int argc; VALUE *argv; VALUE self; } mp_median_call_t;
|
|
1439
|
+
|
|
1440
|
+
static VALUE
|
|
1441
|
+
mp_median_answer_body (VALUE p)
|
|
1442
|
+
{
|
|
1443
|
+
mp_median_call_t *c = (mp_median_call_t *) p;
|
|
1444
|
+
return rb_ca_median_m_body(c->argc, c->argv, c->self);
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
/* An error raised by the methods this one calls names this one. */
|
|
1448
|
+
static VALUE
|
|
1449
|
+
rb_ca_median_m (int argc, VALUE *argv, VALUE self)
|
|
1450
|
+
{
|
|
1451
|
+
mp_median_call_t c = { argc, argv, self };
|
|
1452
|
+
return ca_answer_as_caller(mp_median_answer_body, (VALUE) &c);
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
|
|
1456
|
+
typedef struct { int argc; VALUE *argv; VALUE self; } mp_percentile_call_t;
|
|
1457
|
+
|
|
1458
|
+
static VALUE
|
|
1459
|
+
mp_percentile_answer_body (VALUE p)
|
|
1460
|
+
{
|
|
1461
|
+
mp_percentile_call_t *c = (mp_percentile_call_t *) p;
|
|
1462
|
+
return rb_ca_percentile_m_body(c->argc, c->argv, c->self);
|
|
1463
|
+
}
|
|
1464
|
+
|
|
1465
|
+
/* An error raised by the methods this one calls names this one. */
|
|
1466
|
+
static VALUE
|
|
1467
|
+
rb_ca_percentile_m (int argc, VALUE *argv, VALUE self)
|
|
1468
|
+
{
|
|
1469
|
+
mp_percentile_call_t c = { argc, argv, self };
|
|
1470
|
+
return ca_answer_as_caller(mp_percentile_answer_body, (VALUE) &c);
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
|
|
1474
|
+
typedef struct { int argc; VALUE *argv; VALUE self; } mp_quantile_call_t;
|
|
1475
|
+
|
|
1476
|
+
static VALUE
|
|
1477
|
+
mp_quantile_answer_body (VALUE p)
|
|
1478
|
+
{
|
|
1479
|
+
mp_quantile_call_t *c = (mp_quantile_call_t *) p;
|
|
1480
|
+
return rb_ca_quantile_m_body(c->argc, c->argv, c->self);
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
/* An error raised by the methods this one calls names this one. */
|
|
1484
|
+
static VALUE
|
|
1485
|
+
rb_ca_quantile_m (int argc, VALUE *argv, VALUE self)
|
|
1486
|
+
{
|
|
1487
|
+
mp_quantile_call_t c = { argc, argv, self };
|
|
1488
|
+
return ca_answer_as_caller(mp_quantile_answer_body, (VALUE) &c);
|
|
1291
1489
|
}
|
|
1292
1490
|
|
|
1293
1491
|
void
|
|
@@ -1297,6 +1495,7 @@ Init_carray_median_percentile (void)
|
|
|
1297
1495
|
id_sort_copy = rb_intern("sort_copy");
|
|
1298
1496
|
id_is_not_masked = rb_intern("is_not_masked");
|
|
1299
1497
|
id_plus = rb_intern("+"); id_div = rb_intern("/"); id_mul = rb_intern("*");
|
|
1498
|
+
id_rationalize = rb_intern("rationalize");
|
|
1300
1499
|
id_min = rb_intern("min"); id_max = rb_intern("max");
|
|
1301
1500
|
id_sort = rb_intern("sort"); id_copy = rb_intern("copy");
|
|
1302
1501
|
id_aref = rb_intern("[]"); id_aset = rb_intern("[]=");
|