Supplemental math functions

Overview

Hugo provides a robust set of built-in math functions, but in some scenarios you might need more. The partial template below operates on a non-empty slice of numbers, returning an object with the:

Examples

{{ $s := slice 2 3.1 5 1 4 }}
{{ $result := partial "functions/math" $s }}

{{ $result.MeanArithmetic }} --> 3.02 (float64)
{{ $result.MeanGeometric }} --> 2.622311847181126 (float64)
{{ $result.Median }} --> 3.1 (float64)
{{ $result.VariancePopulation }} --> 2.0016 (float64)
{{ $result.VarianceSample }} --> 2.502 (float64)
{{ $result.StdDeviationPopulation }} --> 1.4147791347061915 (float64)
{{ $result.StdDeviationSample }} --> 1.581771159175688 (float64)

The .Average and .Mean keys are aliases for .MeanArithmetic.

{{ $result.Mean }} --> 3.02 (float64)
{{ $result.Average }} --> 3.02 (float64)

Source code

layouts/partials/functions/math.html
{{- /* Last modified: 2023-06-30T12:24:14-07:00 */}}

{{- /*
Copyright 2023 Veriphor LLC

Licensed under the Apache License, Version 2.0 (the "License"); you may not
use this file except in compliance with the License. You may obtain a copy of
the License at

https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations under
the License.
*/}}

{{- /*
Returns the arithmetic mean (average), geometric mean, median, standard deviation, and variance of a slice of numbers.

@context {slice} . A slice of numbers.

@returns {object}

@example

  {{ $s := slice 2 3.1 5 1 4 }}
  {{ $result := partial "functions/math" $s }}

  {{ $result.MeanArithmetic }} --> 3.02 (float64)
  {{ $result.MeanGeometric }} --> 2.622311847181126 (float64)
  {{ $result.Median }} --> 3.1 (float64)
  {{ $result.VariancePopulation }} --> 2.0016 (float64)
  {{ $result.VarianceSample }} --> 2.502 (float64)
  {{ $result.StdDeviationPopulation }} --> 1.4147791347061915 (float64)
  {{ $result.StdDeviationSample }} --> 1.581771159175688 (float64)

  The .Average and .Mean keys are aliases for .MeanArithmetic.

  {{ $result.Mean }} --> 3.02 (float64)
  {{ $result.Average }} --> 3.02 (float64)

*/}}

{{- /* Initialize. */}}
{{- $commonErrorMessage := "The context passed to the %q partial must be a non-empty slice of numbers." }}
{{- $partialName := "functions/math" }}
{{- $result := dict }}

{{- /* Verify minimum required version. */}}
{{- $minHugoVersion := "0.114.0" }}
{{- if lt hugo.Version $minHugoVersion }}
  {{- errorf "The %q partial requires Hugo v%s or later." $partialName $minHugoVersion }}
{{- end }}

{{- /* Validate context. */}}
{{- if not (reflect.IsSlice .) }}
  {{- errorf $commonErrorMessage $partialName }}
{{- else }}
  {{- if eq (len .) 0 }}
    {{- errorf $commonErrorMessage $partialName }}
  {{- else }}
    {{- range . }}
      {{- if not (in (slice "int" "float64") (printf "%T" .)) }}
        {{- errorf $commonErrorMessage $partialName }}
      {{- end }}
    {{- end }}
  {{- end }}
{{- end }}

{{- /* Cast list to float64 and sort. */}}
{{- $list := apply . "float" "." | sort }}

{{- /* Determine length of list. */}}
{{- $len := len . }}

{{- /* Mean (arithmetic). */}}
{{- $meanArithmetic := div (math.Sum $list) $len }}
{{- $result = merge $result (dict "MeanArithmetic" $meanArithmetic "Average" $meanArithmetic "Mean" $meanArithmetic) }}

{{- /* Mean (geometric). */}}
{{- $meanGeometric := math.Pow (math.Product $list) (div 1 (float $len)) }}
{{- $result = merge $result (dict "MeanGeometric" $meanGeometric) }}

{{- /* Median. */}}
{{- $median := 0 }}
{{- if math.ModBool $len 2 }}
  {{- $n1 := index $list (div $len 2) }}
  {{- $n2 := index $list (sub (div $len 2) 1) }}
  {{- $median = div (add $n1 $n2) 2 }}
{{- else }}
  {{- $median = index $list (div $len 2) }}
{{- end }}
{{- $result = merge $result (dict "Median" (float $median)) }}

{{- /* Variance (population). */}}
{{- $sumOfSquaredDifferences := 0 }}
{{- range $list }}
  {{- $sumOfSquaredDifferences = add $sumOfSquaredDifferences (math.Pow (sub . $meanArithmetic) 2) }}
{{- end }}
{{- $variancePopulation := div (float $sumOfSquaredDifferences) $len }}
{{- $result = merge $result (dict "VariancePopulation" $variancePopulation) }}

{{- /* Variance (sample). */}}
{{- $varianceSample := div (float $sumOfSquaredDifferences) (sub $len 1) }}
{{- $result = merge $result (dict "VarianceSample" $varianceSample) }}

{{- /* Standard deviation (population). */}}
{{- $stdDeviationPopulation := math.Sqrt $variancePopulation }}
{{- $result = merge $result (dict "StdDeviationPopulation" $stdDeviationPopulation) }}

{{- /* Standard deviation (sample). */}}
{{- $stdDeviationSample := math.Sqrt $varianceSample }}
{{- $result = merge $result (dict "StdDeviationSample" $stdDeviationSample) }}

{{- /* Return result. */}}
{{- return $result }}
Last modified: