average


See On Github

Data

Contributor

Generic placeholder thumbnail

by Yonaba

in lua

Tags

Source Code

-- Various average (means) algorithms implementation
-- See: http://en.wikipedia.org/wiki/Average

-- Returns the sum of a sequence of values
local function sum(x)
  local s = 0
  for _, v in ipairs(x) do s = s + v end
  return s
end

-- Calculates the arithmetic mean of a set of values
-- x       : an array of values
-- returns : the arithmetic mean
local function arithmetic_mean(x)
  return (sum(x) / #x)
end

-- Calculates the geometric mean of a set of values
-- x       : an array of values
-- returns : the geometric mean
local function geometric_mean(x)
  local prod = 1
  for _, v in ipairs(x) do prod = prod * v end
  return (prod ^ (1 / #x))
end

-- Calculates the harmonic mean of a set of values
-- x       : an array of values
-- returns : the harmonic mean
local function harmonic_mean(x)
  local s = 0
  for _, v in ipairs(x) do s = s + (1 / v) end
  return (#x / s)
end

-- Calculates the quadratic mean of a set of values
-- x       : an array of values
-- returns : the quadratic mean
local function quadratic_mean(x)
  local ssquares = 0
  for _, v in ipairs(x) do ssquares = ssquares + (v * v) end
  return math.sqrt((1 / #x) * ssquares)
end

-- Calculates the generalized mean (to a specified power p) of a set of values
-- x       : an array of values
-- p       : a power
-- returns : the generalized mean
local function generalized_mean(x, p)
  local sump = 0
  for _, v in ipairs(x) do sump = sump + (v ^ p) end
  return ((1 / #x) * sump) ^ (1 / p)
end

-- Calculates the weighted mean of a set of values
-- x       : an array of values
-- w       : an array of weights for each value in x
-- returns : the weighted mean
local function weighted_mean(x, w)
  local sump = 0
  for i, v in ipairs (x) do sump = sump + (v * w[i]) end
  return sump / sum(w)
end

-- Calculates the midrange mean of a set of values
-- x       : an array of values
-- returns : the midrange mean
local function midrange_mean(x)
  local sump = 0
  return 0.5 * (math.min(unpack(x)) + math.max(unpack(x)))
end

-- Calculates the energetic mean of a set of values
-- x       : an array of values
-- returns : the energetic mean
local function energetic_mean(x)
  local s = 0
  for _,v in ipairs(x) do s = s + (10 ^ (v / 10)) end
  return 10 * math.log10((1 / #x) * s)
end

return {
  arithmetic     = arithmetic_mean,
  geometric      = geometric_mean,
  harmonic       = harmonic_mean,
  quadratic      = quadratic_mean,
  generalized    = generalized_mean,
  weighted       = weighted_mean,
  midrange       = midrange_mean,
  energetic = energetic_mean,
}
-- Tests for average.lua
local mean = require 'average'

local total, pass = 0, 0

local function dec(str, len)
  return #str < len
     and str .. (('.'):rep(len-#str))
      or str:sub(1,len)
end

local function run(message, f)
  total = total + 1
  local ok, err = pcall(f)
  if ok then pass = pass + 1 end
  local status = ok and 'PASSED' or 'FAILED'
  print(('%02d. %68s: %s'):format(total, dec(message,68), status))
end

local function fuzzyEqual(a, b, eps) return math.abs(a - b) < eps end
local x = {1, 2, 3, 4, 5}  

run('Arithmetic mean', function()
  assert(mean.arithmetic(x) == 3)
end)

run('Geometric mean', function()
  assert(fuzzyEqual(mean.geometric(x), 2.60517108,1e-8))
end)

run('Harmonic mean', function()
  assert(fuzzyEqual(mean.harmonic(x), 2.18978102,1e-8))
end)

run('Quadratic mean', function()
  assert(fuzzyEqual(mean.quadratic(x), 3.31662479,1e-8))
end)

run('Generalized mean', function()
  assert(fuzzyEqual(mean.generalized(x,1), mean.arithmetic(x),1e-8))
  assert(fuzzyEqual(mean.generalized(x,2), mean.quadratic(x),1e-8))
  assert(fuzzyEqual(mean.generalized(x,-1), mean.harmonic(x),1e-8))
end)

run('Weighted mean', function()
  local w = { 0.1, 0.2, 0.2, 0.3, 0.2}
  assert(mean.weighted(x, w) == 3.3)
end)

run('Midrange mean', function()
  assert(mean.midrange(x) == 3)
end)

run('Energetic mean', function()
  assert(fuzzyEqual(mean.energetic(x), 3.22766781,1e-8))
end)

print(('-'):rep(80))
print(('Total : %02d: Pass: %02d - Failed : %02d - Success: %.2f %%')
  :format(total, pass, total-pass, (pass*100/total)))