Class: CAGroupIterator

Inherits:
Object
  • Object
show all
Defined in:
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.

Instance Method Summary collapse

Instance Method Details

#count_masked(**kw) ⇒ Object

Per-group count of value-masked cells = elements - count_not_masked.



320
321
322
# File 'lib/carray/axis_group.rb', line 320

def count_masked (**kw)
  elements(**kw) - count_not_masked(**kw)
end

#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, ... (a running count of the group's members up to and including the cell). Without :group delegates to the plain value cumcount (also a 1-based cumulative count of present cells).

Returns:



427
428
429
# File 'lib/carray/axis_group.rb', line 427

def cumcount (**kw)
  scan_op(:cumcount, kw)
end

#cummax(axis: :group) ⇒ CArray

Per-group inclusive running maximum, source-shaped, in the source dtype (extrema do not grow magnitude, so the dtype is preserved). The first member of a group emits its own value. Without :group delegates to value cummax.

Returns:



409
410
411
# File 'lib/carray/axis_group.rb', line 409

def cummax (**kw)
  scan_op(:cummax, kw)
end

#cummin(axis: :group) ⇒ CArray

Per-group inclusive running minimum, source-shaped, in the source dtype. Without :group delegates to value cummin.

Returns:



417
418
419
# File 'lib/carray/axis_group.rb', line 417

def cummin (**kw)
  scan_op(:cummin, kw)
end

#cumprod(axis: :group) ⇒ CArray

Per-group inclusive running product (float64), source-shaped. float64 like cumsum since the product grows. Without :group delegates to value cumprod.

Returns:



399
400
401
# File 'lib/carray/axis_group.rb', line 399

def cumprod (**kw)
  scan_op(:cumprod, kw)
end

#cumsum(axis: :group) ⇒ CArray

Per-group inclusive running sum (float64), source-shaped. Without :group delegates to the plain value cumsum.

Returns:



391
392
393
# File 'lib/carray/axis_group.rb', line 391

def cumsum (**kw)
  scan_op(:cumsum, kw)
end

#each({ |members| ... }) ⇒ Object

Yields each group's members (a CArray). A flat grouping yields per group (composite category); a band-preserving grouping yields per (band position, composite category), band-major then group order. Without a block returns an Enumerator.



490
491
492
493
494
495
496
# File 'lib/carray/axis_group.rb', line 490

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).



315
316
317
# File 'lib/carray/axis_group.rb', line 315

def elements (**kw)
  self.class.__build__(value.value, spec).count(**kw)
end

#map(data_type: nil) ⇒ CArray

Group-wise element-wise transform back to a source-shaped array. Excluded cells (in no group) are UNDEF. Any grouping (single or composite, flat or band-preserving).

Returns:

Raises:

  • (LocalJumpError)


503
504
505
506
507
508
509
510
511
512
513
# File 'lib/carray/axis_group.rb', line 503

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_indexObject

Raises:

  • (NotImplementedError)


344
345
346
347
348
349
# File 'lib/carray/axis_group.rb', line 344

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

#medianCArray

Per-group median (float64), any grouping. See #percentile.

Returns:



454
455
456
# File 'lib/carray/axis_group.rb', line 454

def median (**kw)
  order_stat(:median, [], kw)
end

#min_indexObject #max_indexObject

Overloads:

  • #max_indexObject

    Not provided for a group iterator: a group preserves source order, so a within-group index is weak (the members are not laid out in a private axis to index into). Use min_addr / max_addr for the winner's flat source address, which indexes back into the original array. Raises NotImplementedError.

    Raises:

    • (NotImplementedError)

Raises:

  • (NotImplementedError)


337
338
339
340
341
342
# File 'lib/carray/axis_group.rb', line 337

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).



325
326
327
# File 'lib/carray/axis_group.rb', line 325

def minmax (**kw)
  [min(**kw), max(**kw)]
end

#percentile(*pers) ⇒ CArray+

Per-group percentile(s) (float64), any grouping.

Returns:



461
462
463
# File 'lib/carray/axis_group.rb', line 461

def percentile (*pers, **kw)
  order_stat(:percentile, pers, kw)
end

#quantileArray<CArray>

Per-group five-number summary [min, Q1, median, Q3, max], any grouping.

Returns:



468
469
470
# File 'lib/carray/axis_group.rb', line 468

def quantile (**kw)
  order_stat(:quantile, [], kw)
end

#reduce({ |members| ... }) ⇒ Object #reduce(init) ⇒ CArray

Overloads:

  • #reduce(init) ⇒ CArray

    Custom per-group reduction (dual form). Output shape = slot order (each group slot -> its k, each band slot -> its length). Any grouping (single or composite, flat or band-preserving).

    Returns:

Raises:

  • (LocalJumpError)


521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
# File 'lib/carray/axis_group.rb', line 521

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.slot_meta.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

#sort_addrCArray

Per-group sorted flat source addresses (group-major). Any grouping (single or composite group slots, rank-1 or rank-N, flat or band-preserving) is supported; with no :group it is a plain value.sort_addr.

Returns:



477
478
479
480
481
482
483
# File 'lib/carray/axis_group.rb', line 477

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(v*w) / 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 value*weight 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).



364
365
366
367
368
369
370
371
372
# File 'lib/carray/axis_group.rb', line 364

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.



355
356
357
# File 'lib/carray/axis_group.rb', line 355

def wsum (weights, **kw)
  self.class.__build__(value * weights, spec).sum(**kw)
end