ML-001: Feature Store und erste ML-Tests hinzufügen
This commit is contained in:
5
app/ml/__init__.py
Normal file
5
app/ml/__init__.py
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
"""Machine-Learning-Grundbausteine für SillyHome Next."""
|
||||||
|
|
||||||
|
from app.ml.feature_store import FeatureStore, FeatureVector
|
||||||
|
|
||||||
|
__all__ = ["FeatureStore", "FeatureVector"]
|
||||||
31
app/ml/feature_store.py
Normal file
31
app/ml/feature_store.py
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from collections import defaultdict
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from typing import Iterable
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
|
class FeatureVector:
|
||||||
|
sensor_id: str
|
||||||
|
values: dict[str, float]
|
||||||
|
label: str | None = None
|
||||||
|
|
||||||
|
|
||||||
|
class FeatureStore:
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self._vectors: dict[str, list[FeatureVector]] = defaultdict(list)
|
||||||
|
|
||||||
|
def add(self, vector: FeatureVector) -> None:
|
||||||
|
self._vectors[vector.sensor_id].append(vector)
|
||||||
|
|
||||||
|
def add_batch(self, vectors: Iterable[FeatureVector]) -> None:
|
||||||
|
for vector in vectors:
|
||||||
|
self.add(vector)
|
||||||
|
|
||||||
|
def latest(self, sensor_id: str) -> FeatureVector | None:
|
||||||
|
series = self._vectors.get(sensor_id)
|
||||||
|
return series[-1] if series else None
|
||||||
|
|
||||||
|
def all(self) -> list[FeatureVector]:
|
||||||
|
return [vector for vectors in self._vectors.values() for vector in vectors]
|
||||||
42
tests/ml/test_feature_store.py
Normal file
42
tests/ml/test_feature_store.py
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from app.ml.feature_store import FeatureStore, FeatureVector
|
||||||
|
|
||||||
|
|
||||||
|
def _vector(sensor_id: str, temperature: float, label: str | None = None) -> FeatureVector:
|
||||||
|
return FeatureVector(sensor_id=sensor_id, values={"temperature": temperature}, label=label)
|
||||||
|
|
||||||
|
|
||||||
|
def test_append_and_latest_returns_last_vector() -> None:
|
||||||
|
store = FeatureStore()
|
||||||
|
vectors = [_vector("sensor.living_room", 20.0), _vector("sensor.living_room", 21.5)]
|
||||||
|
for item in vectors:
|
||||||
|
store.add(item)
|
||||||
|
assert store.latest("sensor.living_room") == vectors[-1]
|
||||||
|
|
||||||
|
|
||||||
|
def test_latest_returns_none_when_empty() -> None:
|
||||||
|
store = FeatureStore()
|
||||||
|
assert store.latest("sensor.living_room") is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_add_batch_appends_all_vectors() -> None:
|
||||||
|
store = FeatureStore()
|
||||||
|
vectors = [
|
||||||
|
_vector("sensor.kitchen", 19.0),
|
||||||
|
_vector("sensor.kitchen", 20.0),
|
||||||
|
_vector("sensor.bathroom", 23.5),
|
||||||
|
]
|
||||||
|
store.add_batch(vectors)
|
||||||
|
assert len(store.all()) == 3
|
||||||
|
assert store.latest("sensor.kitchen").values["temperature"] == 20.0
|
||||||
|
|
||||||
|
|
||||||
|
def test_different_sensors_are_stored_independently() -> None:
|
||||||
|
store = FeatureStore()
|
||||||
|
store.add(_vector("sensor.living_room", 21.0))
|
||||||
|
store.add(_vector("sensor.bedroom", 18.5))
|
||||||
|
assert store.latest("sensor.living_room").values["temperature"] == 21.0
|
||||||
|
assert store.latest("sensor.bedroom").values["temperature"] == 18.5
|
||||||
Reference in New Issue
Block a user