Skip to content

Statistics Calculator — C# 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 C# implementation — the same logic the interactive tool runs, in a shareable, citable form.

// statistics — descriptive statistics over a free-form number list. Language: C# (.NET 7+). 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.
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;

var (values, invalid) = Statistics.ParseNumbers("2, 4 4, 6, 8, 11, oops");
var s = Statistics.Summarize(values, sample: true); // population: sample: false
Console.WriteLine($"n={s.Count} sum={s.Sum} mean={s.Mean}");
Console.WriteLine($"median={s.Median} q1={s.Q1} q3={s.Q3} iqr={s.Iqr}");
Console.WriteLine($"min={s.Min} max={s.Max} range={s.Range}");
Console.WriteLine($"variance(sample)={s.Variance} stddev={s.StdDev}");
Console.WriteLine($"mode=[{string.Join(", ", s.Mode)}]  invalid=[{string.Join(", ", invalid)}]");

public static class Statistics
{
    public record ParseResult(List<double> Values, List<string> Invalid);
    public record Stats(int Count, double Sum, double Mean, double Median, List<double> Mode,
                        double Min, double Max, double Range, double Variance, double StdDev,
                        double Q1, double Q3, double Iqr);

    // Split on comma/whitespace runs; only fully-parsed finite tokens count as values.
    public static ParseResult ParseNumbers(string input)
    {
        var values = new List<double>();
        var invalid = new List<string>();
        var seps = new[] { ' ', '\t', '\n', '\r' };
        foreach (var tok in input.Replace(',', ' ').Split(seps, StringSplitOptions.RemoveEmptyEntries))
        {
            // NumberStyles.Float rejects trailing junk; inf/nan parse but fail the finite gate.
            if (double.TryParse(tok, NumberStyles.Float, CultureInfo.InvariantCulture, out var v) && double.IsFinite(v))
                values.Add(v);
            else
                invalid.Add(tok);
        }
        return new ParseResult(values, invalid);
    }

    // Linear-interpolation quantile (R-7 / NumPy / Excel PERCENTILE) over ascending, non-empty data.
    static double Quantile(double[] s, double p)
    {
        double h = (s.Length - 1) * p;
        int lo = (int)Math.Floor(h), hi = (int)Math.Ceiling(h);
        return lo == hi ? s[lo] : s[lo] + (h - lo) * (s[hi] - s[lo]);
    }

    // Most frequent value(s), ascending; empty when every distinct value ties (uniform).
    static List<double> ComputeMode(double[] values)
    {
        var freq = new Dictionary<double, int>();
        foreach (var v in values) freq[v] = freq.TryGetValue(v, out var c) ? c + 1 : 1;
        if (freq.Count == 1) return new List<double> { values[0] }; // a single distinct value is the mode
        int best = freq.Values.Max();
        var modes = freq.Where(kv => kv.Value == best).Select(kv => kv.Key).OrderBy(v => v).ToList();
        return modes.Count == freq.Count ? new List<double>() : modes;
    }

    public static Stats Summarize(IReadOnlyCollection<double> values, bool sample = true)
    {
        const double NaN = double.NaN;
        if (values.Count == 0)
            return new Stats(0, NaN, NaN, NaN, new List<double>(), NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN);
        var s = values.OrderBy(v => v).ToArray();
        double sum = 0;
        foreach (var v in values) sum += v; // sum in original order
        double mean = sum / values.Count, ss = 0;
        foreach (var v in values) ss += (v - mean) * (v - mean);
        // Sample variance needs n >= 2; the population estimator always divides by n.
        double variance = sample ? (values.Count >= 2 ? ss / (values.Count - 1) : NaN) : ss / values.Count;
        double q1 = Quantile(s, 0.25), med = Quantile(s, 0.5), q3 = Quantile(s, 0.75);
        return new Stats(values.Count, sum, mean, med, ComputeMode(s), s[0], s[^1], s[^1] - s[0],
                         variance, Math.Sqrt(variance), q1, q3, q3 - q1);
    }
}

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 →