Skip to content

Statistics Calculator — Ruby source

Compute descriptive statistics - count, sum, mean, median, mode, min/max, range, variance, standard deviation, and quartiles (Q1/Q3/IQR) - from any list of numbers. Tolerates mixed separators and flags unparseable tokens. Choose sample (n−1) or population (n) variance. Everything runs 100% client-side.

This is the Ruby implementation — the same logic the interactive tool runs, in a shareable, citable form.

# statistics — descriptive statistics over a free-form number list. Language: Ruby (3.1+, stdlib only). Port of src/lib/statistics.ts — same logic as this dir's python.py: comma/whitespace parse (finite only), R-7 interpolated quantiles, mode (empty when uniform), sample vs population variance.

# Split on comma/whitespace runs; only fully-parsed finite tokens count as values.
def parse_numbers(input)
  values = []
  invalid = []
  input.gsub(',', ' ').split.each do |tok|
    v = Float(tok, exception: false) # accepts inf/nan — filtered by finite?
    v&.finite? ? values << v : invalid << tok
  end
  [values, invalid]
end

# Linear-interpolation quantile (R-7 / NumPy / Excel PERCENTILE) over ascending, non-empty data.
def quantile(sorted, p)
  h = (sorted.length - 1) * p
  lo = h.floor
  hi = h.ceil
  lo == hi ? sorted[lo] : sorted[lo] + (h - lo) * (sorted[hi] - sorted[lo])
end

# Most frequent value(s), ascending; empty when every distinct value ties (uniform).
def compute_mode(values)
  freq = values.tally
  return [values[0]] if freq.size == 1 # a single distinct value is the mode
  best = freq.values.max
  modes = freq.select { |_, c| c == best }.keys.sort
  modes.size == freq.size ? [] : modes
end

def summarize(values, sample: true)
  n = values.length
  nan = Float::NAN
  return { count: 0, sum: nan, mean: nan, median: nan, mode: [], min: nan, max: nan,
           range: nan, variance: nan, stddev: nan, q1: nan, q3: nan, iqr: nan } if n.zero?
  sorted = values.sort
  sum = values.sum # sum in original order
  mean = sum / n
  ss = values.sum { |x| (x - mean)**2 }
  # Sample variance needs n >= 2; the population estimator always divides by n.
  variance = sample ? (n >= 2 ? ss / (n - 1) : nan) : ss / n
  q1 = quantile(sorted, 0.25)
  med = quantile(sorted, 0.5)
  q3 = quantile(sorted, 0.75)
  { count: n, sum:, mean:, median: med, mode: compute_mode(values), min: sorted[0], max: sorted[-1],
    range: sorted[-1] - sorted[0], variance:, stddev: Math.sqrt(variance), q1:, q3:, iqr: q3 - q1 }
end

values, invalid = parse_numbers('2, 4 4, 6, 8, 11, oops')
s = summarize(values) # sample=true — population: summarize(values, sample: false)
puts "n=#{s[:count]} sum=#{s[:sum]} mean=#{s[:mean]}"
puts "median=#{s[:median]} q1=#{s[:q1]} q3=#{s[:q3]} iqr=#{s[:iqr]}"
puts "min=#{s[:min]} max=#{s[:max]} range=#{s[:range]}"
puts "variance(sample)=#{s[:variance]} stddev=#{s[:stddev]}"
puts "mode=#{s[:mode]}  invalid=#{invalid}"

Also available in 13 other languages

Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →