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