"""Tests for the shared fit data primitives and metrics in ewoksxas.fit.data."""
from dataclasses import FrozenInstanceError
import numpy as np
import pytest
from ewoksxas.fit.data import XYData, as_1d_array, r2, rmse
[docs]
def test_as_1d_array():
# flat numpy array
array = as_1d_array(np.linspace(0, 1, 10))
assert isinstance(array, np.ndarray)
assert array.size == 10
assert array.ndim == 1
# numpy array with shape (1, 10)
array = as_1d_array(np.linspace(0, 1, 10).reshape(-1, 1))
assert isinstance(array, np.ndarray)
assert array.size == 10
assert array.ndim == 1
# empty numpy array
array = as_1d_array(np.empty((0,), dtype=np.float64))
assert isinstance(array, np.ndarray)
assert array.size == 0
assert array.ndim == 1
# 2d numpy array
with pytest.raises(ValueError, match="Array is scalar or has 2"):
as_1d_array(np.ones((10, 10)))
# flat list
array = as_1d_array(np.linspace(0, 1, 10).tolist())
assert isinstance(array, np.ndarray)
assert array.size == 10
assert array.ndim == 1
# flat tuple
array = as_1d_array(tuple(np.linspace(0, 1, 10).tolist()))
assert isinstance(array, np.ndarray)
assert array.size == 10
assert array.ndim == 1
# scalar, strings, bytes, None
with pytest.raises(TypeError, match="Expected an array"):
as_1d_array(1.0)
with pytest.raises(ValueError, match="Array is scalar"):
as_1d_array(np.asarray(1.0))
with pytest.raises(TypeError, match="Expected an array"):
as_1d_array("1.0")
with pytest.raises(ValueError, match="Array is scalar"):
as_1d_array(np.asarray("1.0"))
with pytest.raises(TypeError, match="Expected an array"):
as_1d_array(np.linspace(0, 1, 10).tobytes())
with pytest.raises(TypeError, match="Expected an array"):
as_1d_array(None)
[docs]
def test_xydata():
x = np.linspace(0, np.pi, 10, dtype=np.float64)
y = np.sin(x)
data = XYData(x=x, y=y)
assert data.x.size == data.y.size == 10
assert np.allclose(data.as_dict()["y"], y)
with pytest.raises(ValueError, match="Arrays don't match"):
XYData(x=x, y=np.ones(5))
with pytest.raises(FrozenInstanceError, match="cannot assign to field"):
data.x = np.arange(10, dtype=np.float64)
with pytest.raises(TypeError, match="an instance or subtype of type"):
data.z = y
[docs]
def test_metrics_perfect_model():
observed = np.array([1.0, 2.0, 4.0, 8.0])
assert r2(observed, observed.copy()) == 1.0
assert rmse(observed, observed.copy()) == 0.0
[docs]
def test_rmse_hand_computed():
observed = np.array([0.0, 0.0, 0.0, 0.0])
model = np.array([1.0, -1.0, 1.0, -1.0])
assert rmse(observed, model) == pytest.approx(1.0)
assert rmse(observed, 2.0 * model) == pytest.approx(2.0)
[docs]
def test_r2_hand_computed():
observed = np.array([0.0, 1.0, 2.0, 3.0])
# Half the residual variance of the mean model -> r2 == 0.5.
mean = observed.mean()
model = observed + (mean - observed) / np.sqrt(2.0)
assert r2(observed, model) == pytest.approx(0.5)
# The constant-mean model explains nothing.
assert r2(observed, np.full_like(observed, mean)) == pytest.approx(0.0)
[docs]
def test_r2_zero_variance_is_nan():
observed = np.full(5, 3.0)
assert np.isnan(r2(observed, observed + 0.1))