Class: CAGroupIterator
- Inherits:
-
CAIterator
- Object
- CAIterator
- CAGroupIterator
- Defined in:
- lib/carray/axis_group.rb,
lib/carray/axis_group.rb
Overview
Let the C [] type gate (ext/ca_group_iter.c) recognise these classes without
a kind_of on every index. Registered once on first load of this file, which
happens either eagerly (a axis_group / AxisGroup call) or lazily from the C
gate itself the first time it meets a fixlen-surface CArray index (see
ca_argv_has_group). CACategorical is the classifier, so it must be defined
before the register call.
CAGroupIterator (C-defined in ext/ca_group_iter.c) — the reduction dispatcher
returned by the [] group gate. Its scatterable reductions (sum / prod / mean
/ min / max / variance / stddev / variancep / stddevp / count /
count_not_masked / all / any) bind in C to one driver; the rest of the common
iterator surface that composes cheaply from those is added here.
CAGroupIterator subclasses CAIterator, but the class itself is created in
C, so the reopening below carries no superclass for YARD to read.
Instance Method Summary collapse
- #__all_folded__ ⇒ Object
- #__any_folded__ ⇒ Object
-
#__count_present__ ⇒ Object
The C dispatcher every reduction shares takes keywords only, so the value-equality and masked forms the base declares -- count(v), count(UNDEF) -- arrived as "wrong number of arguments", which reads as a method that does not take one rather than one whose engine has no such fold.
-
#all ⇒ CArray
Per-group
allover a boolean payload. -
#any ⇒ CArray
Per-group
anyover a boolean payload. - #count(*args, **kw) ⇒ Object
-
#count_masked(**kw) ⇒ Object
Per-group count of value-masked cells = elements - count_not_masked.
-
#cumcount(axis: :group) ⇒ CArray
Per-group 1-based within-group ordinal (int64), source-shaped: the first member of a group is 1, the next 2, ...
-
#cummax(axis: :group) ⇒ CArray
Per-group inclusive running maximum, source-shaped, in the source data type (extrema do not grow magnitude, so the data type is preserved).
-
#cummin(axis: :group) ⇒ CArray
Per-group inclusive running minimum, source-shaped, in the source data type.
-
#cumprod(axis: :group) ⇒ CArray
Per-group inclusive running product (float64), source-shaped.
-
#cumsum(axis: :group) ⇒ CArray
Per-group inclusive running sum (float64), source-shaped.
-
#each({ |members| ... }) ⇒ Object
Yields each group's members (a CArray).
-
#elements(**kw) ⇒ Object
Per-group classified cell count (mask-independent) = count on the mask-stripped value, so every classified cell is counted regardless of the value mask (unlike count / count_not_masked, which count present cells).
-
#map(data_type: nil) ⇒ CArray
Group-wise element-wise transform back to a source-shaped array.
- #max_index ⇒ Object
-
#median ⇒ CArray
Per-group median (float64), any grouping.
- #min_index ⇒ Object
-
#minmax(**kw) ⇒ Object
Per-group [min, max] pair (matching CArray#minmax).
- #ndim ⇒ Object
-
#percentile(*pers) ⇒ CArray+
Per-group percentile(s) (float64), any grouping.
-
#quantile ⇒ Array<CArray>
Per-group five-number summary
[min, Q1, median, Q3, max], any grouping. - #reduce(*args, data_type: nil, &block) ⇒ Object
-
#shape ⇒ Object
(also: #dim)
The shape one value per piece comes back in: a group slot contributes its category count, a band slot its length, in slot order.
-
#sort_addr ⇒ CArray
Per-group sorted flat source addresses (group-major).
-
#wmean(weights, **kw) ⇒ Object
Weighted mean = Sum(v*w) / Sum(w), both over the combined present-set.
-
#wsum(weights, **kw) ⇒ Object
Weighted sum: group-sum of value*weight.
Methods inherited from CAIterator
#accumulate, #count_not_masked, #max, #max_addr, #mean, #min, #min_addr, #prod, #stddev, #stddevp, #sum, #variance, #variancep
Instance Method Details
#__all_folded__ ⇒ Object
358 |
# File 'lib/carray/axis_group.rb', line 358 alias __all_folded__ all |
#__any_folded__ ⇒ Object
359 |
# File 'lib/carray/axis_group.rb', line 359 alias __any_folded__ any |
#__count_present__ ⇒ Object
The C dispatcher every reduction shares takes keywords only, so the value-equality and masked forms the base declares -- count(v), count(UNDEF) -- arrived as "wrong number of arguments", which reads as a method that does not take one rather than one whose engine has no such fold. Both are composed here instead, out of folds the engine does have.
335 |
# File 'lib/carray/axis_group.rb', line 335 alias __count_present__ count |
#all ⇒ CArray
364 365 366 367 |
# File 'lib/carray/axis_group.rb', line 364 def all (**kw) boolean_payload!(:all) __all_folded__(**kw) end |
#any ⇒ CArray
372 373 374 375 |
# File 'lib/carray/axis_group.rb', line 372 def any (**kw) boolean_payload!(:any) __any_folded__(**kw) end |
#count ⇒ Object #count(UNDEF) ⇒ Object #count(v) ⇒ CArray
344 345 346 347 348 349 350 351 352 353 354 355 356 |
# File 'lib/carray/axis_group.rb', line 344 def count (*args, **kw) return __count_present__(**kw) if args.empty? if args.size > 1 raise ArgumentError, "wrong number of arguments (given #{args.size}, expected 0..1)" end v = args.first return count_masked(**kw) if v.equal?(UNDEF) # a masked cell equals nothing, and eq marks it UNDEF; drop that to false # so it is simply not counted hit = value.eq(v) hit = hit.strip_mask(false) if hit.has_mask? self.class.__build__(hit.int64, spec).sum(**kw).int64 end |
#count_masked(**kw) ⇒ Object
Per-group count of value-masked cells = elements - count_not_masked.
385 386 387 |
# File 'lib/carray/axis_group.rb', line 385 def count_masked (**kw) elements(**kw) - count_not_masked(**kw) end |
#cumcount(axis: :group) ⇒ CArray
492 493 494 |
# File 'lib/carray/axis_group.rb', line 492 def cumcount (**kw) scan_op(:cumcount, kw) end |
#cummax(axis: :group) ⇒ CArray
474 475 476 |
# File 'lib/carray/axis_group.rb', line 474 def cummax (**kw) scan_op(:cummax, kw) end |
#cummin(axis: :group) ⇒ CArray
482 483 484 |
# File 'lib/carray/axis_group.rb', line 482 def cummin (**kw) scan_op(:cummin, kw) end |
#cumprod(axis: :group) ⇒ CArray
464 465 466 |
# File 'lib/carray/axis_group.rb', line 464 def cumprod (**kw) scan_op(:cumprod, kw) end |
#cumsum(axis: :group) ⇒ CArray
456 457 458 |
# File 'lib/carray/axis_group.rb', line 456 def cumsum (**kw) scan_op(:cumsum, kw) end |
#each({ |members| ... }) ⇒ Object
555 556 557 558 559 560 561 |
# File 'lib/carray/axis_group.rb', line 555 def each (&block) ccat, _kd, gslots, bslots, gaxes = composite_layout return value.group_by_category(ccat).each(&block) if bslots.empty? return to_enum(:each) unless block each_band_block(ccat, gslots, bslots, gaxes) { |_vi, _oi, _co, gi| gi.each(&block) } self end |
#elements(**kw) ⇒ Object
Per-group classified cell count (mask-independent) = count on the mask-stripped value, so every classified cell is counted regardless of the value mask (unlike count / count_not_masked, which count present cells).
380 381 382 |
# File 'lib/carray/axis_group.rb', line 380 def elements (**kw) self.class.__build__(value.value, spec).count(**kw) end |
#map(data_type: nil) ⇒ CArray
568 569 570 571 572 573 574 575 576 577 578 |
# File 'lib/carray/axis_group.rb', line 568 def map (data_type: nil, &block) raise LocalJumpError, "no block given (yield)" unless block ccat, _kd, gslots, bslots, gaxes = composite_layout return value.group_by_category(ccat).map(data_type: data_type, &block) if bslots.empty? dt = data_type || value.data_type out = CArray.new(dt, value.shape) each_band_block(ccat, gslots, bslots, gaxes) do |val_idx, _oi, _co, gi| out[*val_idx] = gi.map(data_type: dt, &block) end out end |
#max_index ⇒ Object
409 410 411 412 413 414 |
# File 'lib/carray/axis_group.rb', line 409 def max_index (*) raise NotImplementedError, "CAGroupIterator has no max_index: a group preserves source order, so " \ "a within-group index is weak; use max_addr for the winner's flat " \ "source address (it indexes back into the original array)." end |
#median ⇒ CArray
519 520 521 |
# File 'lib/carray/axis_group.rb', line 519 def median (**kw) order_stat(:median, [], kw) end |
#min_index ⇒ Object #max_index ⇒ Object
402 403 404 405 406 407 |
# File 'lib/carray/axis_group.rb', line 402 def min_index (*) raise NotImplementedError, "CAGroupIterator has no min_index: a group preserves source order, so " \ "a within-group index is weak; use min_addr for the winner's flat " \ "source address (it indexes back into the original array)." end |
#minmax(**kw) ⇒ Object
Per-group [min, max] pair (matching CArray#minmax).
390 391 392 |
# File 'lib/carray/axis_group.rb', line 390 def minmax (**kw) [min(**kw), max(**kw)] end |
#ndim ⇒ Object
326 327 328 |
# File 'lib/carray/axis_group.rb', line 326 def ndim spec.nslots end |
#percentile(*pers) ⇒ CArray+
526 527 528 |
# File 'lib/carray/axis_group.rb', line 526 def percentile (*pers, **kw) order_stat(:percentile, pers, kw) end |
#quantile ⇒ Array<CArray>
533 534 535 |
# File 'lib/carray/axis_group.rb', line 533 def quantile (**kw) order_stat(:quantile, [], kw) end |
#reduce({ |members| ... }) ⇒ Object #reduce(init) ⇒ CArray
586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 |
# File 'lib/carray/axis_group.rb', line 586 def reduce (*args, data_type: nil, &block) raise LocalJumpError, "no block given (yield)" unless block ccat, kdims, gslots, bslots, gaxes = composite_layout if bslots.empty? return value.group_by_category(ccat).reduce(*args, data_type: data_type, &block) .reshape(*kdims) end dt = data_type || CA_OBJECT out_shape = spec..map { |m| m[:kind] == :group ? m[:k] : m[:len] } out = CArray.new(dt, out_shape) each_band_block(ccat, gslots, bslots, gaxes) do |_vi, out_idx, _co, gi| out[*out_idx] = gi.reduce(*args, data_type: dt, &block).reshape(*kdims) end out end |
#shape ⇒ Object Also known as: dim
The shape one value per piece comes back in: a group slot contributes its category count, a band slot its length, in slot order. The base declares these and every other member answers them; this one inherited the readers without anything ever setting the ivars, so it answered nil -- which the base's own rule calls out as the one thing a member must not do ("a clean NotImplementedError, never a wrong answer").
321 322 323 |
# File 'lib/carray/axis_group.rb', line 321 def shape spec..map { |m| m[:kind] == :group ? m[:k] : m[:len] } end |
#sort_addr ⇒ CArray
542 543 544 545 546 547 548 |
# File 'lib/carray/axis_group.rb', line 542 def sort_addr (**kw) has_group, _ = AxisGroup.parse_axis(kw[:axis]) return value.sort_addr unless has_group ccat, _kd, gslots, bslots, gaxes = composite_layout return value.group_by_category(ccat).sort_addr if bslots.empty? composite_band_sort_addr(ccat, gslots, bslots, gaxes) end |
#wmean(weights, **kw) ⇒ Object
Weighted mean = Sum(vw) / Sum(w), both over the combined present-set. The
value*0 + weights denominator carries the same value|weight mask, so its
group-sum is Sum(w) over exactly the cells valueweight used. An empty group
(no present pair) is UNDEF (matching CArray#wmean); a present group whose
weights sum to zero yields NaN/Inf (core's 0/0).
429 430 431 432 433 434 435 436 437 |
# File 'lib/carray/axis_group.rb', line 429 def wmean (weights, **kw) prod = value * weights num = self.class.__build__(prod, spec).sum(**kw) den = self.class.__build__(value * 0 + weights, spec).sum(**kw) cnt = self.class.__build__(prod, spec).count_not_masked(**kw) out = num / den out[cnt.eq(0)] = UNDEF # no present pair -> masked (empty group) out end |
#wsum(weights, **kw) ⇒ Object
Weighted sum: group-sum of value*weight. weights is a per-cell CArray in
the source layout (same shape as value); the product carries the combined
value|weight mask, so masked cells drop out. Empty group -> 0.0 (identity).
No weighted kernel needed -- it is a plain group-sum of a derived array.
420 421 422 |
# File 'lib/carray/axis_group.rb', line 420 def wsum (weights, **kw) self.class.__build__(value * weights, spec).sum(**kw) end |