view pkg/common/nashsutcliffe.go @ 5694:3bc15e38c7e8 sr-v2

Typo fix
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Mon, 12 Feb 2024 14:43:31 +0100
parents 8c5df0f3562e
children 6270951dda28
line wrap: on
line source

// This is Free Software under GNU Affero General Public License v >= 3.0
// without warranty, see README.md and license for details.
//
// SPDX-License-Identifier: AGPL-3.0-or-later
// License-Filename: LICENSES/AGPL-3.0.txt
//
// Copyright (C) 2019 by via donau
//   – Österreichische Wasserstraßen-Gesellschaft mbH
// Software engineering by Intevation GmbH
//
// Author(s):
//  * Sascha L. Teichmann <sascha.teichmann@intevation.de>

package common

import (
	"fmt"
	"time"
)

type (
	// TimedValue is a tuple of a point in time and an associated value.
	TimedValue struct {
		When  time.Time
		Value float64
	}

	// TimedValues is a slice of TimedValue tuples.
	TimedValues []TimedValue
)

func epsEquals(a, b time.Time) bool {
	d := a.Sub(b)
	return -10*time.Millisecond < d && d < 10*time.Millisecond
}

// Find scans to the tuples and compares the time with an
// epsilon of ten micro seconds. If they are equals the associated
// value is returned. The return bool flags indicated if the
// search was successful.
func (mvs TimedValues) Find(when time.Time) (float64, bool) {
	for i := range mvs {
		if epsEquals(when, mvs[i].When) {
			return mvs[i].Value, true
		}
	}
	return 0, false
}

// NashSutcliffe calculates the Nash-Sutcliffe coefficent for
// given predicted and observed values.
// See
//   https://en.wikipedia.org/wiki/Nash%E2%80%93Sutcliffe_model_efficiency_coefficient
// for details.
// The function panics if predicted and observed are of different lengths.
func NashSutcliffe(predicted, observed []float64) float64 {

	if len(predicted) != len(observed) {
		panic(fmt.Sprintf(
			"NashSutcliffe: predicted and observed len differ: %d != %d",
			len(predicted),
			len(observed)))
	}

	if len(observed) == 0 {
		return 0
	}

	var mo float64
	for _, v := range observed {
		mo += v
	}

	mo /= float64(len(observed))

	var num, denom float64
	for i, o := range observed {
		d1 := predicted[i] - o
		num += d1 * d1
		d2 := o - mo
		denom += d2 * d2
	}

	return 1 - num/denom
}