Class: CAWindowIterator

Inherits:
CAIterator show all
Defined in:
lib/carray/window_iterator.rb

Overview

Rolling (sliding-window) reduction dispatcher — the Window member of the iterator family (sibling of CASlabIterator / CABlockIterator / CACategoricalIterator). It folds an overlapping window centred on every anchor cell, so the result is shaped like the source rather than reduced.

Obtained from CArray#windows, not constructed directly.

Examples:

sw = a.windows(-1..1)     # width-3 window per anchor
sw.mean                   # rolling mean, shaped like a
sw.correlate(kernel)      # bounded cross-correlation

Instance Attribute Summary collapse

Attributes inherited from CAIterator

#ndim, #shape

Instance Method Summary collapse

Constructor Details

#initialize(source, ranges, bounds: :skip, fill_value: nil) ⇒ CAWindowIterator

Returns a new instance of CAWindowIterator.

Builds a window iterator over source with a per-axis offset range. Each ranges[i] is a lo..hi giving the window's offset span around an anchor (a.windows(-1..1) is a centred width-3 window; 0..2 is forward-looking). bounds: selects the margin policy (:skip / :nearest / :truncate). fill_value: is a constant margin value (an escape for :constant padding); when given it overrides :skip.

For backward compatibility initialize(window_view) accepts a CAWindow view (the old CAWindowIterator.new(a.window(...)) form): the geometry (offset ranges, bounds, fill value) is read back from the view.

Parameters:

  • source (CArray, CAWindow) —

    the array to roll over, or a CAWindow view to read the geometry from.

  • ranges (Array<Range>) —

    per-axis offset ranges.

  • bounds (Symbol) (defaults to: :skip) —

    :skip / :nearest / :truncate.

  • fill_value (Object, nil) (defaults to: nil) —

    constant margin value, overriding :skip.



83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/carray/window_iterator.rb', line 83

def initialize (source, *ranges, bounds: :skip, fill_value: nil)
  if source.is_a?(CArray) && source.obj_type == CA_OBJ_WINDOW
    # Backward-compat: read geometry from a CAWindow view built by #window.
    # start[i] = lo, shape[i] (window width) = w, so hi = lo + w - 1.
    win     = source
    @source = win.parent
    widths  = win.shape
    @ranges = win.start.each_with_index.map { |lo, i| lo..(lo + widths[i] - 1) }
    # The legacy #window default is FILL (constant), whose value is the
    # view's fill_value; map that to a :constant margin.
    @bounds     = :constant
    @fill_value = win.fill_value
  else
    @source     = source
    @ranges     = ranges.flatten(0)
    @bounds     = bounds
    @fill_value = fill_value
  end

  unless @ranges.size == @source.ndim
    raise ArgumentError,
          "windows: expected #{@source.ndim} ranges (one per axis), " \
          "got #{@ranges.size}"
  end

  @sndim  = @source.ndim
  @widths = @ranges.map { |r| r.end - r.begin + 1 }
  @lefts  = @ranges.map { |r| [0, -r.begin].max }   # left margin per axis
  @rights = @ranges.map { |r| [0,  r.end ].max }    # right margin per axis

  # A constant fill_value: overrides :skip (constant margin escape hatch).
  @bounds = :constant if @fill_value != nil && @bounds == :skip

  # Trailing window axes of the sliding_windows view: [ndim .. 2*ndim-1].
  @window_axes = (@sndim...(2 * @sndim)).to_a

  # Output iteration space (reference-shaped, except :truncate, which keeps
  # only the anchors whose window lies wholly inside the source), and where
  # each axis's first window starts in the buffer the windows are read from.
  # A window that does not cover its own anchor -- `windows(1..2)`, the two
  # cells after this one -- starts further along than the margin allows for,
  # which is what @origins carries.
  rshape = @source.shape
  if @bounds == :truncate
    first  = @ranges.map { |r| [0, -r.begin].max }
    last   = @sndim.times.map { |i| [rshape[i] - 1, rshape[i] - 1 - @ranges[i].end].min }
    @shape = @sndim.times.map { |i| [last[i] - first[i] + 1, 0].max }
    @origins = @ranges.map { |r| [r.begin, 0].max }
  else
    @shape = rshape.dup
    @origins = @sndim.times.map { |i| @lefts[i] + @ranges[i].begin }
  end
  @ndim = @shape.size
  self
end

Instance Attribute Details

#bounds ⇒ Symbol (readonly)

Returns the boundary policy symbol.

Returns:

  • (Symbol)


147
148
149
# File 'lib/carray/window_iterator.rb', line 147

def bounds
  @bounds
end

#source ⇒ CArray (readonly)

Returns the array being rolled over.

Returns:



142
143
144
# File 'lib/carray/window_iterator.rb', line 142

def source
  @source
end

Instance Method Details

#accumulate(min_count: nil, fill_value: nil) ⇒ CArray

Rolling sum kept in the source's own data type, wrapping at its width, as the core accumulate does -- sum answers in the type the core promotes to (float64 for integers).

Parameters:

  • min_count (Integer, nil) (defaults to: nil) —

    fewest cells that must be present for a result; a piece with fewer comes back masked.

  • fill_value (Object, nil) (defaults to: nil) —

    value to put in place of a masked result instead of leaving it masked.

Returns:

  • (CArray) —

    reference-shaped (or shrunk, for :truncate)



408
409
410
411
412
413
414
415
416
417
418
419
420
# File 'lib/carray/window_iterator.rb', line 408

[:sum, :accumulate, :prod, :mean, :min, :max, :variance, :stddev, :all, :any,
 :variancep, :stddevp, :minmax, :min_index, :max_index].each do |op|
  class_eval <<~RUBY, __FILE__, __LINE__ + 1
    def #{op} (min_count: nil, fill_value: nil)
      folded = fold_by_offset(:#{op}, min_count, fill_value)
      return folded unless folded.nil?
      kw = {}
      kw[:min_count]  = min_count  unless min_count.nil?
      kw[:fill_value] = fill_value unless fill_value.nil?
      sliding_view.#{op}(axis: @window_axes, **kw)
    end
  RUBY
end

#all(min_count: nil, fill_value: nil) ⇒ CArray

Whether every cell of each rolling is true.

Parameters:

  • min_count (Integer, nil) (defaults to: nil) —

    fewest cells that must be present for a result; a piece with fewer comes back masked.

  • fill_value (Object, nil) (defaults to: nil) —

    value to put in place of a masked result instead of leaving it masked.

Returns:

  • (CArray) —

    reference-shaped (or shrunk, for :truncate)



408
409
410
411
412
413
414
415
416
417
418
419
420
# File 'lib/carray/window_iterator.rb', line 408

[:sum, :accumulate, :prod, :mean, :min, :max, :variance, :stddev, :all, :any,
 :variancep, :stddevp, :minmax, :min_index, :max_index].each do |op|
  class_eval <<~RUBY, __FILE__, __LINE__ + 1
    def #{op} (min_count: nil, fill_value: nil)
      folded = fold_by_offset(:#{op}, min_count, fill_value)
      return folded unless folded.nil?
      kw = {}
      kw[:min_count]  = min_count  unless min_count.nil?
      kw[:fill_value] = fill_value unless fill_value.nil?
      sliding_view.#{op}(axis: @window_axes, **kw)
    end
  RUBY
end

#any(min_count: nil, fill_value: nil) ⇒ CArray

Whether any cell of each rolling is true.

Parameters:

  • min_count (Integer, nil) (defaults to: nil) —

    fewest cells that must be present for a result; a piece with fewer comes back masked.

  • fill_value (Object, nil) (defaults to: nil) —

    value to put in place of a masked result instead of leaving it masked.

Returns:

  • (CArray) —

    reference-shaped (or shrunk, for :truncate)



408
409
410
411
412
413
414
415
416
417
418
419
420
# File 'lib/carray/window_iterator.rb', line 408

[:sum, :accumulate, :prod, :mean, :min, :max, :variance, :stddev, :all, :any,
 :variancep, :stddevp, :minmax, :min_index, :max_index].each do |op|
  class_eval <<~RUBY, __FILE__, __LINE__ + 1
    def #{op} (min_count: nil, fill_value: nil)
      folded = fold_by_offset(:#{op}, min_count, fill_value)
      return folded unless folded.nil?
      kw = {}
      kw[:min_count]  = min_count  unless min_count.nil?
      kw[:fill_value] = fill_value unless fill_value.nil?
      sliding_view.#{op}(axis: @window_axes, **kw)
    end
  RUBY
end

#convolve(kernel, min_count: nil, fill_value: nil) ⇒ CArray

Rolling convolution out[i] = Σ_j a[i-j]·k[j] (true convolution: the kernel is flipped on every window axis). Equals #correlate for a symmetric kernel.

Parameters:

  • kernel (CArray) —

    weights shaped like a single window.

Returns:



776
777
778
# File 'lib/carray/window_iterator.rb', line 776

def convolve (kernel, min_count: nil, fill_value: nil)
  correlate(reverse_all_axes(kernel), min_count: min_count, fill_value: fill_value)
end

#correlate(kernel, min_count: nil, fill_value: nil) ⇒ CArray

Rolling cross-correlation out[i] = Σ_j a[i+j]·k[j] (kernel not flipped). kernel has the shape of one window (w_1 × ... × w_n).

Parameters:

  • kernel (CArray) —

    weights shaped like a single window.

Returns:



749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
# File 'lib/carray/window_iterator.rb', line 749

def correlate (kernel, min_count: nil, fill_value: nil)
  unless kernel.shape == @widths
    raise ArgumentError,
          "correlate: kernel shape #{kernel.shape.inspect} != " \
          "window shape #{@widths.inspect}"
  end
  sv = sliding_view
  # Explicit broadcast of the kernel over the anchor axes: reshape to
  # 1 on every anchor axis, kernel width on every window axis (CArray forbids
  # implicit cross-ndim broadcast, so the shape is made explicit).
  kshape = ([1] * @sndim) + @widths
  # The product routes operand promotion through the single-source binop
  # coercion (result_type), so a float kernel over an int source promotes to
  # float instead of truncating the weights. Do not coerce the kernel here.
  prod   = sv * kernel.reshape(*kshape)
  kw = {}
  kw[:min_count]  = min_count  unless min_count.nil?
  kw[:fill_value] = fill_value unless fill_value.nil?
  prod.sum(axis: @window_axes, **kw)
end

#count(v = <none>) ⇒ CArray

Rolling count over the window. No argument counts present (non-masked) cells (the effective tap count, which drops near a :skip edge); count(UNDEF) counts masked cells; count(v) counts cells equal to v.

Returns:



700
701
702
703
704
705
# File 'lib/carray/window_iterator.rb', line 700

def count (*args)
  return count_not_masked if args.empty?
  # The sliding_windows view is a CAStride, so its #count is not shadowed;
  # dispatch CArray#count explicitly anyway, matching the family regularity.
  CArray.instance_method(:count).bind_call(sliding_view, *args, axis: @window_axes)
end

#count_masked ⇒ CArray

Rolling count of masked cells.

Returns:



718
719
720
# File 'lib/carray/window_iterator.rb', line 718

def count_masked
  sliding_view.count_masked(axis: @window_axes)
end

#count_not_masked ⇒ CArray

Rolling count of present (non-masked) cells -- the denominator of a renormalizing convolution.

Returns:



711
712
713
# File 'lib/carray/window_iterator.rb', line 711

def count_not_masked
  sliding_view.count_not_masked(axis: @window_axes)
end

#cumcount ⇒ Object

Not supported for a window iterator. A segment scan writes a per-cell running count of present cells, which is single-valued only when each cell belongs to exactly one piece. Overlapping windows put a cell in many windows, so there is no single running value. Raises NotImplementedError, exactly as #map / #sort_addr do (min / max reductions stay available: a single winner is well-defined).

Raises:

  • (NotImplementedError)


1010
1011
1012
1013
1014
1015
1016
1017
1018
# File 'lib/carray/window_iterator.rb', line 1010

[:cumsum, :cumprod, :cummax, :cummin, :cumcount].each do |op|
  define_method(op) do |*, **|
    raise NotImplementedError,
          "#{self.class} has no #{op}: a segment scan needs each cell to " \
          "belong to exactly one piece, but overlapping windows share cells, " \
          "so a per-cell running value is ill-defined; use reduce for a " \
          "custom per-window fold."
  end
end

#cummax ⇒ Object

Not supported for a window iterator. A segment scan writes a per-cell running maximum, which is single-valued only when each cell belongs to exactly one piece. Overlapping windows put a cell in many windows, so there is no single running value. Raises NotImplementedError, exactly as #map / #sort_addr do (min / max reductions stay available: a single winner is well-defined).

Raises:

  • (NotImplementedError)


1010
1011
1012
1013
1014
1015
1016
1017
1018
# File 'lib/carray/window_iterator.rb', line 1010

[:cumsum, :cumprod, :cummax, :cummin, :cumcount].each do |op|
  define_method(op) do |*, **|
    raise NotImplementedError,
          "#{self.class} has no #{op}: a segment scan needs each cell to " \
          "belong to exactly one piece, but overlapping windows share cells, " \
          "so a per-cell running value is ill-defined; use reduce for a " \
          "custom per-window fold."
  end
end

#cummin ⇒ Object

Not supported for a window iterator. A segment scan writes a per-cell running minimum, which is single-valued only when each cell belongs to exactly one piece. Overlapping windows put a cell in many windows, so there is no single running value. Raises NotImplementedError, exactly as #map / #sort_addr do (min / max reductions stay available: a single winner is well-defined).

Raises:

  • (NotImplementedError)


1010
1011
1012
1013
1014
1015
1016
1017
1018
# File 'lib/carray/window_iterator.rb', line 1010

[:cumsum, :cumprod, :cummax, :cummin, :cumcount].each do |op|
  define_method(op) do |*, **|
    raise NotImplementedError,
          "#{self.class} has no #{op}: a segment scan needs each cell to " \
          "belong to exactly one piece, but overlapping windows share cells, " \
          "so a per-cell running value is ill-defined; use reduce for a " \
          "custom per-window fold."
  end
end

#cumprod ⇒ Object

Not supported for a window iterator. A segment scan writes a per-cell running product, which is single-valued only when each cell belongs to exactly one piece. Overlapping windows put a cell in many windows, so there is no single running value. Raises NotImplementedError, exactly as #map / #sort_addr do (min / max reductions stay available: a single winner is well-defined).

Raises:

  • (NotImplementedError)


1010
1011
1012
1013
1014
1015
1016
1017
1018
# File 'lib/carray/window_iterator.rb', line 1010

[:cumsum, :cumprod, :cummax, :cummin, :cumcount].each do |op|
  define_method(op) do |*, **|
    raise NotImplementedError,
          "#{self.class} has no #{op}: a segment scan needs each cell to " \
          "belong to exactly one piece, but overlapping windows share cells, " \
          "so a per-cell running value is ill-defined; use reduce for a " \
          "custom per-window fold."
  end
end

#cumsum ⇒ Object

Not supported for a window iterator. A segment scan writes a per-cell running sum, which is single-valued only when each cell belongs to exactly one piece. Overlapping windows put a cell in many windows, so there is no single running value. Raises NotImplementedError, exactly as #map / #sort_addr do (min / max reductions stay available: a single winner is well-defined).

Raises:

  • (NotImplementedError)


1010
1011
1012
1013
1014
1015
1016
1017
1018
# File 'lib/carray/window_iterator.rb', line 1010

[:cumsum, :cumprod, :cummax, :cummin, :cumcount].each do |op|
  define_method(op) do |*, **|
    raise NotImplementedError,
          "#{self.class} has no #{op}: a segment scan needs each cell to " \
          "belong to exactly one piece, but overlapping windows share cells, " \
          "so a per-cell running value is ill-defined; use reduce for a " \
          "custom per-window fold."
  end
end

#each({ |window| ... }) {|window| ... } ⇒ Enumerator, self

Yields each anchor's window as a CArray. Without a block, returns an Enumerator. Per-window materialize, slow; use a named reduction or #convolve for speed.

Yield Parameters:

Returns:

  • (Enumerator, self)


906
907
908
909
910
911
912
# File 'lib/carray/window_iterator.rb', line 906

def each
  return to_enum(:each) unless block_given?
  sv   = sliding_view
  nils = Array.new(@sndim, nil)      # full window on the trailing axes
  each_anchor_index { |idx| yield sv[*idx, *nils] }
  self
end

#elements ⇒ CArray

Window cell count (structural, mask-independent): the constant window size Π w_i, shaped like the output.

Returns:



726
727
728
729
730
731
732
733
# File 'lib/carray/window_iterator.rb', line 726

def elements
  sz = @widths.inject(1) { |p, w| p * w }
  # count_not_masked gives the correct output shape (and is not shadowed);
  # overwrite with the constant window size.
  out = sliding_view.count_not_masked(axis: @window_axes)
  out[] = sz
  out
end

#map ⇒ Object

Not supported for a window iterator: overlapping windows share cells, so an element-wise transform has no well-defined scatter-back. Raises NotImplementedError; use #reduce for a custom per-window fold.

Raises:

  • (NotImplementedError)

Raises:

  • (NotImplementedError)


949
950
951
952
953
954
# File 'lib/carray/window_iterator.rb', line 949

def map (*)
  raise NotImplementedError,
        "#{self.class} has no map: overlapping windows share cells, so an " \
        "element-wise scatter-back is ill-defined; use reduce for a custom " \
        "per-window fold."
end

#max(min_count: nil, fill_value: nil) ⇒ CArray

Rolling maximum.

Parameters:

  • min_count (Integer, nil) (defaults to: nil) —

    fewest cells that must be present for a result; a piece with fewer comes back masked.

  • fill_value (Object, nil) (defaults to: nil) —

    value to put in place of a masked result instead of leaving it masked.

Returns:

  • (CArray) —

    reference-shaped (or shrunk, for :truncate)



408
409
410
411
412
413
414
415
416
417
418
419
420
# File 'lib/carray/window_iterator.rb', line 408

[:sum, :accumulate, :prod, :mean, :min, :max, :variance, :stddev, :all, :any,
 :variancep, :stddevp, :minmax, :min_index, :max_index].each do |op|
  class_eval <<~RUBY, __FILE__, __LINE__ + 1
    def #{op} (min_count: nil, fill_value: nil)
      folded = fold_by_offset(:#{op}, min_count, fill_value)
      return folded unless folded.nil?
      kw = {}
      kw[:min_count]  = min_count  unless min_count.nil?
      kw[:fill_value] = fill_value unless fill_value.nil?
      sliding_view.#{op}(axis: @window_axes, **kw)
    end
  RUBY
end

#max_addr ⇒ CArray

Rolling flat source address of the window maximum. See #min_addr.

Returns:

  • (CArray) —

    reference-shaped (or shrunk, for :truncate)



436
# File 'lib/carray/window_iterator.rb', line 436

def max_addr; window_winner_addr(:max_index); end

#max_index(min_count: nil, fill_value: nil) ⇒ CArray

Rolling position of the maximum, local to the window axes.

Parameters:

  • min_count (Integer, nil) (defaults to: nil) —

    fewest cells that must be present for a result; a piece with fewer comes back masked.

  • fill_value (Object, nil) (defaults to: nil) —

    value to put in place of a masked result instead of leaving it masked.

Returns:

  • (CArray) —

    reference-shaped (or shrunk, for :truncate)



408
409
410
411
412
413
414
415
416
417
418
419
420
# File 'lib/carray/window_iterator.rb', line 408

[:sum, :accumulate, :prod, :mean, :min, :max, :variance, :stddev, :all, :any,
 :variancep, :stddevp, :minmax, :min_index, :max_index].each do |op|
  class_eval <<~RUBY, __FILE__, __LINE__ + 1
    def #{op} (min_count: nil, fill_value: nil)
      folded = fold_by_offset(:#{op}, min_count, fill_value)
      return folded unless folded.nil?
      kw = {}
      kw[:min_count]  = min_count  unless min_count.nil?
      kw[:fill_value] = fill_value unless fill_value.nil?
      sliding_view.#{op}(axis: @window_axes, **kw)
    end
  RUBY
end

#mean(min_count: nil, fill_value: nil) ⇒ CArray

Rolling arithmetic mean.

Parameters:

  • min_count (Integer, nil) (defaults to: nil) —

    fewest cells that must be present for a result; a piece with fewer comes back masked.

  • fill_value (Object, nil) (defaults to: nil) —

    value to put in place of a masked result instead of leaving it masked.

Returns:

  • (CArray) —

    reference-shaped (or shrunk, for :truncate)



408
409
410
411
412
413
414
415
416
417
418
419
420
# File 'lib/carray/window_iterator.rb', line 408

[:sum, :accumulate, :prod, :mean, :min, :max, :variance, :stddev, :all, :any,
 :variancep, :stddevp, :minmax, :min_index, :max_index].each do |op|
  class_eval <<~RUBY, __FILE__, __LINE__ + 1
    def #{op} (min_count: nil, fill_value: nil)
      folded = fold_by_offset(:#{op}, min_count, fill_value)
      return folded unless folded.nil?
      kw = {}
      kw[:min_count]  = min_count  unless min_count.nil?
      kw[:fill_value] = fill_value unless fill_value.nil?
      sliding_view.#{op}(axis: @window_axes, **kw)
    end
  RUBY
end

#median ⇒ CArray

Rolling median. Requires an unmasked margin (bounds: :nearest or :truncate); with the default :skip it raises.

Returns:



809
810
811
# File 'lib/carray/window_iterator.rb', line 809

def median
  order_stat { |view, axis| view.median(axis: axis) }
end

#min(min_count: nil, fill_value: nil) ⇒ CArray

Rolling minimum.

Parameters:

  • min_count (Integer, nil) (defaults to: nil) —

    fewest cells that must be present for a result; a piece with fewer comes back masked.

  • fill_value (Object, nil) (defaults to: nil) —

    value to put in place of a masked result instead of leaving it masked.

Returns:

  • (CArray) —

    reference-shaped (or shrunk, for :truncate)



408
409
410
411
412
413
414
415
416
417
418
419
420
# File 'lib/carray/window_iterator.rb', line 408

[:sum, :accumulate, :prod, :mean, :min, :max, :variance, :stddev, :all, :any,
 :variancep, :stddevp, :minmax, :min_index, :max_index].each do |op|
  class_eval <<~RUBY, __FILE__, __LINE__ + 1
    def #{op} (min_count: nil, fill_value: nil)
      folded = fold_by_offset(:#{op}, min_count, fill_value)
      return folded unless folded.nil?
      kw = {}
      kw[:min_count]  = min_count  unless min_count.nil?
      kw[:fill_value] = fill_value unless fill_value.nil?
      sliding_view.#{op}(axis: @window_axes, **kw)
    end
  RUBY
end

#min_addr ⇒ CArray

Rolling flat SOURCE address of the window minimum — which source cell holds it, so source.reshape(source.elements)[sw.min_addr] are the window minima. Unlike min_index (the position within the window) this indexes back into the original array. The winner's source cell is the anchor plus its window offset; with bounds: :nearest a winning margin cell resolves to the edge source cell it replicates, and with bounds: :constant (or fill_value:) a winning margin cell has no source address and is a masked result.

Returns:

  • (CArray) —

    reference-shaped (or shrunk, for :truncate)



431
# File 'lib/carray/window_iterator.rb', line 431

def min_addr; window_winner_addr(:min_index); end

#min_index(min_count: nil, fill_value: nil) ⇒ CArray

Rolling position of the minimum, local to the window axes.

Parameters:

  • min_count (Integer, nil) (defaults to: nil) —

    fewest cells that must be present for a result; a piece with fewer comes back masked.

  • fill_value (Object, nil) (defaults to: nil) —

    value to put in place of a masked result instead of leaving it masked.

Returns:

  • (CArray) —

    reference-shaped (or shrunk, for :truncate)



408
409
410
411
412
413
414
415
416
417
418
419
420
# File 'lib/carray/window_iterator.rb', line 408

[:sum, :accumulate, :prod, :mean, :min, :max, :variance, :stddev, :all, :any,
 :variancep, :stddevp, :minmax, :min_index, :max_index].each do |op|
  class_eval <<~RUBY, __FILE__, __LINE__ + 1
    def #{op} (min_count: nil, fill_value: nil)
      folded = fold_by_offset(:#{op}, min_count, fill_value)
      return folded unless folded.nil?
      kw = {}
      kw[:min_count]  = min_count  unless min_count.nil?
      kw[:fill_value] = fill_value unless fill_value.nil?
      sliding_view.#{op}(axis: @window_axes, **kw)
    end
  RUBY
end

#minmax(min_count: nil, fill_value: nil) ⇒ Array<CArray>

Rolling minimum and maximum, found in one pass.

Parameters:

  • min_count (Integer, nil) (defaults to: nil) —

    fewest cells that must be present for a result; a piece with fewer comes back masked.

  • fill_value (Object, nil) (defaults to: nil) —

    value to put in place of a masked result instead of leaving it masked.

Returns:

  • (Array<CArray>) —

    the pair [min, max], reference-shaped (or shrunk, for :truncate)



408
409
410
411
412
413
414
415
416
417
418
419
420
# File 'lib/carray/window_iterator.rb', line 408

[:sum, :accumulate, :prod, :mean, :min, :max, :variance, :stddev, :all, :any,
 :variancep, :stddevp, :minmax, :min_index, :max_index].each do |op|
  class_eval <<~RUBY, __FILE__, __LINE__ + 1
    def #{op} (min_count: nil, fill_value: nil)
      folded = fold_by_offset(:#{op}, min_count, fill_value)
      return folded unless folded.nil?
      kw = {}
      kw[:min_count]  = min_count  unless min_count.nil?
      kw[:fill_value] = fill_value unless fill_value.nil?
      sliding_view.#{op}(axis: @window_axes, **kw)
    end
  RUBY
end

#percentile(*pers) ⇒ CArray+

Rolling percentile(s). One argument returns one CArray, several return an array of CArrays (as CArray#percentile). Requires an unmasked margin.

Returns:



818
819
820
# File 'lib/carray/window_iterator.rb', line 818

def percentile (*pers)
  order_stat { |view, axis| view.percentile(*pers, axis: axis) }
end

#prod(min_count: nil, fill_value: nil) ⇒ CArray

Rolling product.

Parameters:

  • min_count (Integer, nil) (defaults to: nil) —

    fewest cells that must be present for a result; a piece with fewer comes back masked.

  • fill_value (Object, nil) (defaults to: nil) —

    value to put in place of a masked result instead of leaving it masked.

Returns:

  • (CArray) —

    reference-shaped (or shrunk, for :truncate)



408
409
410
411
412
413
414
415
416
417
418
419
420
# File 'lib/carray/window_iterator.rb', line 408

[:sum, :accumulate, :prod, :mean, :min, :max, :variance, :stddev, :all, :any,
 :variancep, :stddevp, :minmax, :min_index, :max_index].each do |op|
  class_eval <<~RUBY, __FILE__, __LINE__ + 1
    def #{op} (min_count: nil, fill_value: nil)
      folded = fold_by_offset(:#{op}, min_count, fill_value)
      return folded unless folded.nil?
      kw = {}
      kw[:min_count]  = min_count  unless min_count.nil?
      kw[:fill_value] = fill_value unless fill_value.nil?
      sliding_view.#{op}(axis: @window_axes, **kw)
    end
  RUBY
end

#quantile ⇒ Array<CArray>

Rolling five-number summary [min, Q1, median, Q3, max] (five CArrays), as CArray#quantile. Requires an unmasked margin.

Returns:



826
827
828
# File 'lib/carray/window_iterator.rb', line 826

def quantile
  order_stat { |view, axis| view.quantile(axis: axis) }
end

#reduce({ |window| ... }) {|window| ... } ⇒ CArray #reduce(init) ⇒ CArray

Overloads:

  • #reduce({ |window| ... }) {|window| ... } ⇒ CArray

    Custom rolling reduction: the block receives each window (a CArray) and returns one value per anchor. The escape hatch for statistics not in the named surface.

    Yield Parameters:

    Returns:

    • (CArray) —

      reference-shaped (or shrunk, for :truncate)

  • #reduce(init) ⇒ CArray

    Per-window fiber fold: each window's cells are folded element by element starting from init.

    Parameters:

    • init (Object) —

      initial accumulator.

    Returns:

Raises:

  • (LocalJumpError)


925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
# File 'lib/carray/window_iterator.rb', line 925

def reduce (*args, data_type: nil, &blk)
  raise LocalJumpError, "no block given (yield)" unless blk
  dt   = data_type || CA_OBJECT
  out  = CArray.new(dt, @shape)
  sv   = sliding_view
  nils = Array.new(@sndim, nil)      # full window on the trailing axes
  if args.empty?
    each_anchor_index { |idx| out[*idx] = blk.call(sv[*idx, *nils]) }
  else
    init = args[0]
    each_anchor_index do |idx|
      acc = init
      sv[*idx, *nils].each { |e| acc = blk.call(acc, e) }
      out[*idx] = acc
    end
  end
  out
end

#sliding_view ⇒ CArray

Returns the sliding_windows view feeding the reductions. For :truncate this is the source's own view (zero-copy); otherwise it is the view over the padded entity.

Returns:



160
161
162
# File 'lib/carray/window_iterator.rb', line 160

def sliding_view
  @sliding_view ||= anchored_buffer.sliding_windows(*@widths)
end

#sort_addr ⇒ Object

Not supported for a window iterator: a window's boundary cells are padding with no source address, and overlapping windows share cells, so a per-window sort returning source flat addresses is ill-defined. Raises NotImplementedError. (min_addr / max_addr are fine: the single winning cell of a window is a real source cell.)

Raises:

  • (NotImplementedError)

Raises:

  • (NotImplementedError)


963
964
965
966
967
968
# File 'lib/carray/window_iterator.rb', line 963

def sort_addr (*)
  raise NotImplementedError,
        "#{self.class} has no sort_addr: padded boundary cells have no " \
        "source address and overlapping windows share cells, so a per-window " \
        "sort of source addresses is ill-defined."
end

#stddev(min_count: nil, fill_value: nil) ⇒ CArray

Rolling sample standard deviation (divisor n - 1).

Parameters:

  • min_count (Integer, nil) (defaults to: nil) —

    fewest cells that must be present for a result; a piece with fewer comes back masked.

  • fill_value (Object, nil) (defaults to: nil) —

    value to put in place of a masked result instead of leaving it masked.

Returns:

  • (CArray) —

    reference-shaped (or shrunk, for :truncate)



408
409
410
411
412
413
414
415
416
417
418
419
420
# File 'lib/carray/window_iterator.rb', line 408

[:sum, :accumulate, :prod, :mean, :min, :max, :variance, :stddev, :all, :any,
 :variancep, :stddevp, :minmax, :min_index, :max_index].each do |op|
  class_eval <<~RUBY, __FILE__, __LINE__ + 1
    def #{op} (min_count: nil, fill_value: nil)
      folded = fold_by_offset(:#{op}, min_count, fill_value)
      return folded unless folded.nil?
      kw = {}
      kw[:min_count]  = min_count  unless min_count.nil?
      kw[:fill_value] = fill_value unless fill_value.nil?
      sliding_view.#{op}(axis: @window_axes, **kw)
    end
  RUBY
end

#stddevp(min_count: nil, fill_value: nil) ⇒ CArray

Rolling population standard deviation (divisor n).

Parameters:

  • min_count (Integer, nil) (defaults to: nil) —

    fewest cells that must be present for a result; a piece with fewer comes back masked.

  • fill_value (Object, nil) (defaults to: nil) —

    value to put in place of a masked result instead of leaving it masked.

Returns:

  • (CArray) —

    reference-shaped (or shrunk, for :truncate)



408
409
410
411
412
413
414
415
416
417
418
419
420
# File 'lib/carray/window_iterator.rb', line 408

[:sum, :accumulate, :prod, :mean, :min, :max, :variance, :stddev, :all, :any,
 :variancep, :stddevp, :minmax, :min_index, :max_index].each do |op|
  class_eval <<~RUBY, __FILE__, __LINE__ + 1
    def #{op} (min_count: nil, fill_value: nil)
      folded = fold_by_offset(:#{op}, min_count, fill_value)
      return folded unless folded.nil?
      kw = {}
      kw[:min_count]  = min_count  unless min_count.nil?
      kw[:fill_value] = fill_value unless fill_value.nil?
      sliding_view.#{op}(axis: @window_axes, **kw)
    end
  RUBY
end

#sum(min_count: nil, fill_value: nil) ⇒ CArray

Rolling sum.

Parameters:

  • min_count (Integer, nil) (defaults to: nil) —

    fewest cells that must be present for a result; a piece with fewer comes back masked.

  • fill_value (Object, nil) (defaults to: nil) —

    value to put in place of a masked result instead of leaving it masked.

Returns:

  • (CArray) —

    reference-shaped (or shrunk, for :truncate)



408
409
410
411
412
413
414
415
416
417
418
419
420
# File 'lib/carray/window_iterator.rb', line 408

[:sum, :accumulate, :prod, :mean, :min, :max, :variance, :stddev, :all, :any,
 :variancep, :stddevp, :minmax, :min_index, :max_index].each do |op|
  class_eval <<~RUBY, __FILE__, __LINE__ + 1
    def #{op} (min_count: nil, fill_value: nil)
      folded = fold_by_offset(:#{op}, min_count, fill_value)
      return folded unless folded.nil?
      kw = {}
      kw[:min_count]  = min_count  unless min_count.nil?
      kw[:fill_value] = fill_value unless fill_value.nil?
      sliding_view.#{op}(axis: @window_axes, **kw)
    end
  RUBY
end

#variance(min_count: nil, fill_value: nil) ⇒ CArray

Rolling sample variance (divisor n - 1).

Parameters:

  • min_count (Integer, nil) (defaults to: nil) —

    fewest cells that must be present for a result; a piece with fewer comes back masked.

  • fill_value (Object, nil) (defaults to: nil) —

    value to put in place of a masked result instead of leaving it masked.

Returns:

  • (CArray) —

    reference-shaped (or shrunk, for :truncate)



408
409
410
411
412
413
414
415
416
417
418
419
420
# File 'lib/carray/window_iterator.rb', line 408

[:sum, :accumulate, :prod, :mean, :min, :max, :variance, :stddev, :all, :any,
 :variancep, :stddevp, :minmax, :min_index, :max_index].each do |op|
  class_eval <<~RUBY, __FILE__, __LINE__ + 1
    def #{op} (min_count: nil, fill_value: nil)
      folded = fold_by_offset(:#{op}, min_count, fill_value)
      return folded unless folded.nil?
      kw = {}
      kw[:min_count]  = min_count  unless min_count.nil?
      kw[:fill_value] = fill_value unless fill_value.nil?
      sliding_view.#{op}(axis: @window_axes, **kw)
    end
  RUBY
end

#variancep(min_count: nil, fill_value: nil) ⇒ CArray

Rolling population variance (divisor n).

Parameters:

  • min_count (Integer, nil) (defaults to: nil) —

    fewest cells that must be present for a result; a piece with fewer comes back masked.

  • fill_value (Object, nil) (defaults to: nil) —

    value to put in place of a masked result instead of leaving it masked.

Returns:

  • (CArray) —

    reference-shaped (or shrunk, for :truncate)



408
409
410
411
412
413
414
415
416
417
418
419
420
# File 'lib/carray/window_iterator.rb', line 408

[:sum, :accumulate, :prod, :mean, :min, :max, :variance, :stddev, :all, :any,
 :variancep, :stddevp, :minmax, :min_index, :max_index].each do |op|
  class_eval <<~RUBY, __FILE__, __LINE__ + 1
    def #{op} (min_count: nil, fill_value: nil)
      folded = fold_by_offset(:#{op}, min_count, fill_value)
      return folded unless folded.nil?
      kw = {}
      kw[:min_count]  = min_count  unless min_count.nil?
      kw[:fill_value] = fill_value unless fill_value.nil?
      sliding_view.#{op}(axis: @window_axes, **kw)
    end
  RUBY
end

#wmean(weights) ⇒ CArray

Rolling weighted mean, weights shaped like a single window.

Returns:



868
869
870
# File 'lib/carray/window_iterator.rb', line 868

def wmean (weights)
  weighted(weights) { |sv, w, axis| sv.wmean(w, axis: axis) }
end

#wsum(weights) ⇒ CArray

Rolling weighted sum, weights shaped like a single window.

Returns:



861
862
863
# File 'lib/carray/window_iterator.rb', line 861

def wsum (weights)
  weighted(weights) { |sv, w, axis| sv.wsum(w, axis: axis) }
end