class MoreMath::Sequence

values for them.
This class is used to contain elements and compute various statistical

def arithmetic_mean

Returns the arithmetic mean of the elements.
def arithmetic_mean
  sum / size
end

def autocorrelation

1).
Returns the array of autocorrelation values c_k / c_0 (of length size -
def autocorrelation
  c = autovariance
  Array.new(c.size) { |k| c[k] / c[0] }
end

def autovariance

Returns the array of autovariances (of length size - 1).
def autovariance
  Array.new(size - 1) do |k|
    s = 0.0
    0.upto(size - k - 1) do |i|
      s += (@elements[i] - arithmetic_mean) * (@elements[i + k] - arithmetic_mean)
    end
    s / size
  end
end

def common_standard_deviation(other)

elements of this and +other+.
Returns an estimation of the common standard deviation of the
def common_standard_deviation(other)
  Math.sqrt(common_variance(other))
end

def common_variance(other)

and +other+.
Returns an estimation of the common variance of the elements of this
def common_variance(other)
  (size - 1) * sample_variance + (other.size - 1) *
    other.sample_variance / (size + other.size - 2)
end

def compute_student_df(other)

Compute the # degrees of freedom for Student's t-test.
def compute_student_df(other)
  size + other.size - 2
end

def compute_welch_df(other)

degrees of freedom for Welch's t-test.
Use an approximation of the Welch-Satterthwaite equation to compute the
def compute_welch_df(other)
  (sample_variance / size + other.sample_variance / other.size) ** 2 / (
    (sample_variance ** 2 / (size ** 2 * (size - 1))) +
    (other.sample_variance ** 2 / (other.size ** 2 * (other.size - 1))))
end

def confidence_interval(alpha = 0.05)

the elements of this Sequence instance as a Range object.
Return the confidence interval for the arithmetic mean with alpha level +alpha+ of
def confidence_interval(alpha = 0.05)
  td = TDistribution.new(size - 1)
  t = td.inverse_probability(alpha / 2).abs
  delta = t * sample_standard_deviation / Math.sqrt(size)
  (arithmetic_mean - delta)..(arithmetic_mean + delta)
end

def cover?(other, alpha = 0.05)

level.
arithmetic mean value is most likely to be equal for the +alpha+ error
Return true, if the Sequence instance covers the +other+, that is their
def cover?(other, alpha = 0.05)
  t = t_welch(other)
  td = TDistribution.new(compute_welch_df(other))
  t.abs < td.inverse_probability(1 - alpha.abs / 2.0)
end

def detect_autocorrelation(lags = 20, alpha_level = 0.05)

:detected:: true if a correlation was found.
:p:: the p-value computed, if p is higher than alpha no correlation was detected,
:q:: the value of the ljung_box_statistic,
:alpha_level:: the alpha level for the test,
:lags:: the number of lags,
results, otherwise nil is returned. The keys are
statistic. If enough lags can be considered it returns a hash with
This method tries to detect autocorrelation with the Ljung-Box
def detect_autocorrelation(lags = 20, alpha_level = 0.05)
  if q = ljung_box_statistic(lags)
    p = ChiSquareDistribution.new(lags).probability(q)
    return {
      :lags         => lags,
      :alpha_level  => alpha_level,
      :q            => q,
      :p            => p,
      :detected     => p >= 1 - alpha_level,
    }
  end
end

def detect_outliers(factor = 3.0, epsilon = 1E-5)

less than epsilon, nil is returned.
:median and :iqr parameters. If no outliers were found or the iqr is
:very_high outliers, determined by the box plotting algorithm run with
Return a result hash with the number of :very_low, :low, :high, and
def detect_outliers(factor = 3.0, epsilon = 1E-5)
  half_factor = factor / 2.0
  quartile1 = percentile(25)
  quartile3 = percentile(75)
  iqr = quartile3 - quartile1
  iqr < epsilon and return
  result = @elements.inject(Hash.new(0)) do |h, t|
    extreme =
      case t
      when -Infinity..(quartile1 - factor * iqr)
        :very_low
      when (quartile1 - factor * iqr)..(quartile1 - half_factor * iqr)
        :low
      when (quartile1 + half_factor * iqr)..(quartile3 + factor * iqr)
        :high
      when (quartile3 + factor * iqr)..Infinity
        :very_high
      end and h[extreme] += 1
    h
  end
  unless result.empty?
    result[:median] = median
    result[:iqr] = iqr
    result[:factor] = factor
    result
  end
end

def durbin_watson_statistic

for positive, d >> 2 for negative and d around 2 for no autocorrelation.
Returns the d-value for the Durbin-Watson statistic. The value is d << 2
def durbin_watson_statistic
  e = linear_regression.residues
  e.size <= 1 and return 2.0
  (1...e.size).inject(0.0) { |s, i| s + (e[i] - e[i - 1]) ** 2 } /
    e.inject(0.0) { |s, x| s + x ** 2 }
end

def each(&block)

Calls the +block+ for every element of this Sequence.
def each(&block)
  @elements.each(&block)
end

def empty?

Returns true if this sequence is empty, otherwise false.
def empty?
  @elements.empty?
end

def geometric_mean

elements is less than 0.0, this method returns NaN.
Returns the geometric mean of the elements. If any of the
def geometric_mean
  sum = @elements.inject(0.0) { |s, t|
    case
    when t > 0
      s + Math.log(t)
    when t == 0
      break :null
    else
      break nil
    end
  }
  case sum
  when :null
    0.0
  when Float
    Math.exp(sum / size)
  else
    0 / 0.0
  end
end

def harmonic_mean

is less than or equal to 0.0, this method returns NaN.
Returns the harmonic mean of the elements. If any of the elements
def harmonic_mean
  sum = @elements.inject(0.0) { |s, t|
    if t > 0
      s + 1.0 / t
    else
      break nil
    end
  }
  sum ? size / sum : 0 / 0.0
end

def histogram(bins)

analysis' elements.
Returns a Histogram instance with +bins+ as the number of bins for this
def histogram(bins)
  Histogram.new(self, bins)
end

def initialize(elements)

def initialize(elements)
  @elements = elements.dup.freeze
end

def linear_regression

represents the line computed by the linear regression algorithm.
Returns the LinearRegression object for the equation a * x + b which
def linear_regression
  LinearRegression.new @elements
end

def ljung_box_statistic(lags = 20)

(at least lags) lags available.
this Sequence instance. This method returns nil if there weren't enough
+lags+. A higher value might indicate autocorrelation in the elements of
Returns the q value of the Ljung-Box statistic for the number of lags
def ljung_box_statistic(lags = 20)
  r = autocorrelation
  lags >= r.size and return
  n = size
  n * (n + 2) * (1..lags).inject(0.0) { |s, i| s + r[i] ** 2 / (n - i) }
end

def max

Returns the maximum of the elements.
def max
  @elements.max
end

def min

Returns the minimum of the elements.
def min
  @elements.min
end

def percentile(p = 50)

(excluding the 100).
the weighted average at x_(n + 1)p, which allows p to be in 0...100
There are many methods to compute the percentile, this method uses the
Returns the +p+-percentile of the elements.
def percentile(p = 50)
  (0...100).include?(p) or
    raise ArgumentError, "p = #{p}, but has to be in (0...100)"
  p /= 100.0
  sorted_elements = sorted
  r = p * (sorted_elements.size + 1)
  r_i = r.to_i
  r_f = r - r_i
  if r_i >= 1
    result = sorted_elements[r_i - 1]
    if r_i < sorted_elements.size
      result += r_f * (sorted_elements[r_i] - sorted_elements[r_i - 1])
    end
  else
    result = sorted_elements[0]
  end
  result
end

def reset

Reset all memoized values of this sequence.
def reset
  self.class.memoize_cache_clear
  self
end

def sample_standard_deviation

Returns the sample standard deviation of the elements.
def sample_standard_deviation
  Math.sqrt(sample_variance)
end

def sample_standard_deviation_percentage

of the arithmetic mean.
Returns the sample standard deviation of the elements in percentage
def sample_standard_deviation_percentage
  100.0 * sample_standard_deviation / arithmetic_mean
end

def sample_variance

Returns the sample_variance of the elements.
def sample_variance
  size > 1 ? sum_of_squares / (size - 1.0) : 0.0
end

def size

Returns the number of elements, on which the analysis is based.
def size
  @elements.size
end

def sorted

Return a sorted array of the elements.
def sorted
  @elements.sort
end

def standard_deviation

Returns the standard deviation of the elements.
def standard_deviation
  Math.sqrt(variance)
end

def standard_deviation_percentage

arithmetic mean.
Returns the standard deviation of the elements in percentage of the
def standard_deviation_percentage
  100.0 * standard_deviation / arithmetic_mean
end

def suggested_sample_size(other, alpha = 0.05, beta = 0.05)

and +beta+ as levels for the first- and second-order errors.
between this instance's elements and those of +other+. Use +alpha+
Compute a sample size, that will more likely yield a mean difference
def suggested_sample_size(other, alpha = 0.05, beta = 0.05)
  alpha, beta = alpha.abs, beta.abs
  signal = arithmetic_mean - other.arithmetic_mean
  df = size + other.size - 2
  pooled_variance_estimate = (sum_of_squares + other.sum_of_squares) / df
  td = TDistribution.new df
  (((td.inverse_probability(alpha) + td.inverse_probability(beta)) *
    Math.sqrt(pooled_variance_estimate)) / signal) ** 2
end

def sum

Returns the sum of all elements.
def sum
  @elements.inject(0.0) { |s, t| s + t }
end

def sum_of_squares

elements.
Returns the sum of squares (the sum of the squared deviations) of the
def sum_of_squares
  @elements.inject(0.0) { |s, t| s + (t - arithmetic_mean) ** 2 }
end

def t_student(other)

instance and the +other+.
Returns the t value of the Student's t-test between this Sequence
def t_student(other)
  signal = arithmetic_mean - other.arithmetic_mean
  noise = common_standard_deviation(other) *
    Math.sqrt(size ** -1 + size ** -1)
rescue Errno::EDOM
  0.0
end

def t_welch(other)

instance and the +other+.
Returns the t value of the Welch's t-test between this Sequence
def t_welch(other)
  signal = arithmetic_mean - other.arithmetic_mean
  noise = Math.sqrt(sample_variance / size +
    other.sample_variance / other.size)
  signal / noise
rescue Errno::EDOM
  0.0
end

def variance

Returns the variance of the elements.
def variance
  sum_of_squares / size
end