Statistics Calculator — Kotlin 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 Kotlin implementation — the same logic the interactive tool runs, in a shareable, citable form.
// statistics — descriptive statistics over a free-form number list. Language: Kotlin (JVM 1.9+). 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.
import kotlin.math.ceil
import kotlin.math.floor
import kotlin.math.sqrt
data class ParseResult(val values: List<Double>, val invalid: List<String>)
data class Stats(
val count: Int, val sum: Double, val mean: Double, val median: Double, val mode: List<Double>,
val min: Double, val max: Double, val range: Double, val variance: Double, val stddev: Double,
val q1: Double, val q3: Double, val iqr: Double,
)
/* Split on comma/whitespace runs; only fully-parsed finite tokens count as values. */
fun parseNumbers(input: String): ParseResult {
val values = mutableListOf<Double>()
val invalid = mutableListOf<String>()
for (tok in input.replace(',', ' ').split(Regex("\\s+"))) {
if (tok.isEmpty()) continue
val v = tok.toDoubleOrNull() // accepts inf/nan — filtered by isFinite
if (v != null && v.isFinite()) values.add(v) else invalid.add(tok)
}
return ParseResult(values, invalid)
}
/* Linear-interpolation quantile (R-7 / NumPy / Excel PERCENTILE) over ascending, non-empty data. */
fun quantile(sorted: List<Double>, p: Double): Double {
val h = (sorted.size - 1) * p
val lo = floor(h).toInt()
val hi = ceil(h).toInt()
return if (lo == hi) sorted[lo] else sorted[lo] + (h - lo) * (sorted[hi] - sorted[lo])
}
/* Most frequent value(s), ascending; empty when every distinct value ties (uniform). */
fun computeMode(values: List<Double>): List<Double> {
val freq = values.groupingBy { it }.eachCount()
if (freq.size == 1) return listOf(values[0]) // a single distinct value is the mode
val best = freq.values.max()
val modes = freq.filterValues { it == best }.keys.sorted()
return if (modes.size == freq.size) emptyList() else modes
}
fun summarize(values: List<Double>, sample: Boolean = true): Stats {
if (values.isEmpty()) return Stats(0, Double.NaN, Double.NaN, Double.NaN, emptyList(), Double.NaN,
Double.NaN, Double.NaN, Double.NaN, Double.NaN, Double.NaN, Double.NaN, Double.NaN)
val s = values.sorted()
val sum = values.reduce(Double::plus) // sum in original order
val mean = sum / values.size
val ss = values.sumOf { (it - mean) * (it - mean) }
// Sample variance needs n >= 2; the population estimator always divides by n.
val variance = if (sample) (if (values.size >= 2) ss / (values.size - 1) else Double.NaN) else ss / values.size
val q1 = quantile(s, 0.25)
val med = quantile(s, 0.5)
val q3 = quantile(s, 0.75)
return Stats(values.size, sum, mean, med, computeMode(values), s.first(), s.last(), s.last() - s.first(),
variance, sqrt(variance), q1, q3, q3 - q1)
}
fun main() {
val (values, invalid) = parseNumbers("2, 4 4, 6, 8, 11, oops")
val st = summarize(values) // sample=true — population: summarize(values, false)
println("n=${st.count} sum=${st.sum} mean=${st.mean}")
println("median=${st.median} q1=${st.q1} q3=${st.q3} iqr=${st.iqr}")
println("min=${st.min} max=${st.max} range=${st.range}")
println("variance(sample)=${st.variance} stddev=${st.stddev}")
println("mode=${st.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 →