class SPCore::Features

Features analysis methods.

Public Class Methods

envelope(samples) click to toggle source
# File lib/spcore/analysis/features.rb, line 64
def self.envelope samples
  outline = self.outline samples
  upsample_factor = samples.count / outline.count.to_f
  return PolynomialResampling.upsample(outline, upsample_factor)
end
extrema(samples) click to toggle source

Returns all minima and maxima (including positive minima and negative maxima).

# File lib/spcore/analysis/features.rb, line 5
def self.extrema samples
  remove_inner = false
  self.extrema_hash(samples, remove_inner)[:extrema]
end
maxima(samples) click to toggle source

Returns maxima (includes negative maxima).

# File lib/spcore/analysis/features.rb, line 29
def self.maxima samples
  remove_inner = false
  self.extrema_hash(samples, remove_inner)[:maxima]
end
minima(samples) click to toggle source

Returns all minima (including positive minima).

# File lib/spcore/analysis/features.rb, line 17
def self.minima samples
  remove_inner = false
  self.extrema_hash(samples, remove_inner)[:minima]
end
negative_minima(samples) click to toggle source

Returns all minima (excludes positive minima).

# File lib/spcore/analysis/features.rb, line 23
def self.negative_minima samples
  remove_inner = true
  self.extrema_hash(samples, remove_inner)[:minima]
end
outer_extrema(samples) click to toggle source

Returns outer minima and maxima (excludes positive minima and negative maxima).

# File lib/spcore/analysis/features.rb, line 11
def self.outer_extrema samples
  remove_inner = true
  self.extrema_hash(samples, remove_inner)[:extrema]
end
outline(samples) click to toggle source
# File lib/spcore/analysis/features.rb, line 57
def self.outline samples
  starting_outline = make_starting_outline samples
  filled_in_outline = fill_in_starting_outline starting_outline, samples.count
  dropsample_factor = (samples.count / starting_outline.count).to_i
  return dropsample_filled_in_outline filled_in_outline, dropsample_factor
end
positive_maxima(samples) click to toggle source

Returns maxima (excludes negative maxima).

# File lib/spcore/analysis/features.rb, line 35
def self.positive_maxima samples
  remove_inner = true
  self.extrema_hash(samples, remove_inner)[:maxima]
end
top_n(values, n) click to toggle source

return the n greatest values of the given array of values.

# File lib/spcore/analysis/features.rb, line 41
def self.top_n values, n
  top_n = []
  values.each do |value|
    if top_n.count < n
      top_n.push value
    else
      smaller = top_n.select {|x| x < value}
      if smaller.any?
        top_n.delete smaller.min
        top_n.push value
      end
    end
  end
  return top_n.sort
end

Private Class Methods

dropsample_filled_in_outline(filled_in_outline, dropsample_factor) click to toggle source

This properly spaces samples in the filled in outline, so as to avoid time distortion after upsampling.

# File lib/spcore/analysis/features.rb, line 120
def self.dropsample_filled_in_outline filled_in_outline, dropsample_factor
  dropsampled_outline = []

  (0...filled_in_outline.count).step(dropsample_factor) do |n|
    dropsampled_outline.push filled_in_outline[n]
  end
  
  return dropsampled_outline
end
extrema_hash(samples, remove_inner = false) click to toggle source

Finds extrema (minima and maxima), return :extrema, :minima, and :maxima seperately in a hash.

# File lib/spcore/analysis/features.rb, line 132
def self.extrema_hash samples, remove_inner = false
  minima = {}
  maxima = {}
  
  global_min_idx = 0
  global_min_val = samples[0]
  global_max_idx = 0
  global_max_val = samples[0]
  
  diffs = []
  for i in (1...samples.count)
    diffs.push(samples[i] - samples[i-1])
    
    if samples[i] < global_min_val
      global_min_idx = i
      global_min_val = samples[i]
    end
    
    if samples[i] > global_max_val
      global_max_idx = i
      global_max_val = samples[i]
    end
  end
  minima[global_min_idx] = global_min_val
  maxima[global_max_idx] = global_max_val
  
  is_positive = diffs.first > 0.0 # starting off with positive difference?
  
  # at zero crossings there is a local maxima/minima
  for i in (1...diffs.count)
    if is_positive
      # at positive-to-negative transition there is a local maxima
      if diffs[i] <= 0.0
        maxima[i] = samples[i]
        is_positive = false
      end
    else
      # at negative-to-positive transition there is a local minima
      if diffs[i] > 0.0
        minima[i] = samples[i]
        is_positive = true
      end
    end
  end
  
  if remove_inner
    minima.keep_if {|idx,val| val <= 0 }
    maxima.keep_if {|idx,val| val >= 0 }
  end
      
  return :minima => minima, :maxima => maxima, :extrema => minima.merge(maxima)
end
fill_in_starting_outline(starting_outline, tgt_count) click to toggle source
# File lib/spcore/analysis/features.rb, line 87
def self.fill_in_starting_outline starting_outline, tgt_count
  # the extrema we have now are probably not spaced evenly. Upsampling at
  # this point would lead to a time-distorted signal. So the next step is to
  # interpolate between all the extrema to make a crude but properly sampled
  # envelope.
  
  proper_outline = Array.new(tgt_count, 0)
  indices = starting_outline.keys.sort
  
  for i in 1...indices.count
    l_idx = indices[i-1]
    r_idx = indices[i]
    
    l_val = starting_outline[l_idx]
    r_val = starting_outline[r_idx]
    
    proper_outline[l_idx] = l_val
    proper_outline[r_idx] = r_val
    
    idx_span = r_idx - l_idx
    
    for j in (l_idx + 1)...(r_idx)
      x = (j - l_idx).to_f / idx_span
      y = Interpolation.linear l_val, r_val, x
      proper_outline[j] = y
    end
  end
  
  return proper_outline
end
make_starting_outline(samples) click to toggle source
# File lib/spcore/analysis/features.rb, line 72
def self.make_starting_outline samples
  starting_outline = self.outer_extrema(samples)
  
  # add in first and last samples so the envelope follows entire signal
  starting_outline[0] = samples[0].abs
  starting_outline[samples.count - 1] = samples[samples.count - 1].abs
  
  # the envelope is only concerned with absolute values
  starting_outline.keys.each do |key|
    starting_outline[key] = starting_outline[key].abs
  end
  
  return starting_outline
end