view pkg/common/nashsutcliffe.go @ 2741:87aed4f9b1b8

Added calculation of Nash Sutcliffe efficiency coefficents. GET /api/data/nash-sutcliffe/{gauge}?when={WHEN} 'when' is optional in form of "2006-01-02T15:04:05.000" and defaults to current server time. curl -H "X-Gemma-Auth:$KEY" http://${server}:${port}/api/data/nash-sutcliffe/${gauge} | jq . { "when": "2019-03-20T10:38:05.687", "coeffs": [ { "value": 0, "samples": 0, "hours": 24 }, { "value": 0, "samples": 0, "hours": 48 }, { "value": 0, "samples": 0, "hours": 72 } ] }
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Wed, 20 Mar 2019 10:47:01 +0100
parents
children 4f66a3ba424b
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 (
	"sort"
	"time"
)

type NSMeasurement struct {
	When      time.Time
	Predicted float64
	Observed  float64
}

func NashSutcliffeSort(measurements []NSMeasurement) {
	sort.Slice(measurements, func(i, j int) bool {
		return measurements[i].When.Before(measurements[j].When)
	})
}

func NashSutcliffe(measurements []NSMeasurement, from, to time.Time) (float64, int) {

	if len(measurements) == 0 {
		return 0, 0
	}

	if to.Before(from) {
		from, to = to, from
	}

	begin := sort.Search(len(measurements), func(i int) bool {
		return !measurements[i].When.Before(from)
	})
	if begin >= len(measurements) {
		return 0, 0
	}

	end := sort.Search(len(measurements), func(i int) bool {
		return measurements[i].When.After(to)
	})
	if end >= len(measurements) {
		end = len(measurements) - 1
	}
	if end <= begin {
		return 0, 0
	}
	sample := measurements[begin:end]

	if len(sample) == 0 {
		return 0, 0
	}

	var mo float64
	for i := range sample {
		mo += sample[i].Observed
	}

	mo /= float64(len(sample))

	var num, denom float64
	for i := range sample {
		d1 := sample[i].Predicted - sample[i].Observed
		num += d1 * d1
		d2 := sample[i].Observed - mo
		denom += d2 * d2
	}

	return 1 - num/denom, len(sample)
}