KSvend commited on
Commit ·
cbe084f
1
Parent(s): aa09e0b
feat: add four-factor confidence scoring model
Browse files- app/analysis/confidence.py +78 -0
- tests/test_confidence.py +71 -0
app/analysis/confidence.py
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Four-factor confidence scoring for EO indicators."""
|
| 2 |
+
from __future__ import annotations
|
| 3 |
+
|
| 4 |
+
from typing import Any
|
| 5 |
+
|
| 6 |
+
from app.models import ConfidenceLevel
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
def score_temporal_coverage(valid_months: int) -> float:
|
| 10 |
+
if valid_months >= 10:
|
| 11 |
+
return 1.0
|
| 12 |
+
if valid_months >= 7:
|
| 13 |
+
return 0.75
|
| 14 |
+
if valid_months >= 4:
|
| 15 |
+
return 0.5
|
| 16 |
+
return 0.25
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
def score_observation_density(mean_obs: float) -> float:
|
| 20 |
+
if mean_obs > 10:
|
| 21 |
+
return 1.0
|
| 22 |
+
if mean_obs >= 6:
|
| 23 |
+
return 0.75
|
| 24 |
+
if mean_obs >= 3:
|
| 25 |
+
return 0.5
|
| 26 |
+
return 0.25
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def score_baseline_depth(years_with_data: int) -> float:
|
| 30 |
+
if years_with_data >= 5:
|
| 31 |
+
return 1.0
|
| 32 |
+
if years_with_data >= 4:
|
| 33 |
+
return 0.75
|
| 34 |
+
if years_with_data >= 2:
|
| 35 |
+
return 0.5
|
| 36 |
+
return 0.25
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def score_spatial_completeness(fraction: float) -> float:
|
| 40 |
+
if fraction > 0.9:
|
| 41 |
+
return 1.0
|
| 42 |
+
if fraction > 0.75:
|
| 43 |
+
return 0.75
|
| 44 |
+
if fraction >= 0.5:
|
| 45 |
+
return 0.5
|
| 46 |
+
return 0.25
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
def compute_confidence(
|
| 50 |
+
valid_months: int,
|
| 51 |
+
mean_obs_per_composite: float,
|
| 52 |
+
baseline_years_with_data: int,
|
| 53 |
+
spatial_completeness: float,
|
| 54 |
+
) -> dict[str, Any]:
|
| 55 |
+
temporal = score_temporal_coverage(valid_months)
|
| 56 |
+
obs = score_observation_density(mean_obs_per_composite)
|
| 57 |
+
baseline = score_baseline_depth(baseline_years_with_data)
|
| 58 |
+
spatial = score_spatial_completeness(spatial_completeness)
|
| 59 |
+
|
| 60 |
+
score = temporal * 0.3 + obs * 0.2 + baseline * 0.3 + spatial * 0.2
|
| 61 |
+
|
| 62 |
+
if score > 0.7:
|
| 63 |
+
level = ConfidenceLevel.HIGH
|
| 64 |
+
elif score >= 0.4:
|
| 65 |
+
level = ConfidenceLevel.MODERATE
|
| 66 |
+
else:
|
| 67 |
+
level = ConfidenceLevel.LOW
|
| 68 |
+
|
| 69 |
+
return {
|
| 70 |
+
"level": level,
|
| 71 |
+
"score": round(score, 3),
|
| 72 |
+
"factors": {
|
| 73 |
+
"temporal": temporal,
|
| 74 |
+
"observation_density": obs,
|
| 75 |
+
"baseline_depth": baseline,
|
| 76 |
+
"spatial_completeness": spatial,
|
| 77 |
+
},
|
| 78 |
+
}
|
tests/test_confidence.py
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Tests for four-factor confidence scoring."""
|
| 2 |
+
import pytest
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
def test_score_temporal_coverage():
|
| 6 |
+
from app.analysis.confidence import score_temporal_coverage
|
| 7 |
+
assert score_temporal_coverage(0) == 0.25
|
| 8 |
+
assert score_temporal_coverage(3) == 0.25
|
| 9 |
+
assert score_temporal_coverage(5) == 0.5
|
| 10 |
+
assert score_temporal_coverage(8) == 0.75
|
| 11 |
+
assert score_temporal_coverage(12) == 1.0
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def test_score_observation_density():
|
| 15 |
+
from app.analysis.confidence import score_observation_density
|
| 16 |
+
assert score_observation_density(1.0) == 0.25
|
| 17 |
+
assert score_observation_density(4.0) == 0.5
|
| 18 |
+
assert score_observation_density(8.0) == 0.75
|
| 19 |
+
assert score_observation_density(15.0) == 1.0
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
def test_score_baseline_depth():
|
| 23 |
+
from app.analysis.confidence import score_baseline_depth
|
| 24 |
+
assert score_baseline_depth(1) == 0.25
|
| 25 |
+
assert score_baseline_depth(3) == 0.5
|
| 26 |
+
assert score_baseline_depth(4) == 0.75
|
| 27 |
+
assert score_baseline_depth(5) == 1.0
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
def test_score_spatial_completeness():
|
| 31 |
+
from app.analysis.confidence import score_spatial_completeness
|
| 32 |
+
assert score_spatial_completeness(0.3) == 0.25
|
| 33 |
+
assert score_spatial_completeness(0.6) == 0.5
|
| 34 |
+
assert score_spatial_completeness(0.85) == 0.75
|
| 35 |
+
assert score_spatial_completeness(0.95) == 1.0
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
def test_compute_confidence_high():
|
| 39 |
+
from app.analysis.confidence import compute_confidence
|
| 40 |
+
from app.models import ConfidenceLevel
|
| 41 |
+
result = compute_confidence(
|
| 42 |
+
valid_months=12, mean_obs_per_composite=15.0,
|
| 43 |
+
baseline_years_with_data=5, spatial_completeness=0.95,
|
| 44 |
+
)
|
| 45 |
+
assert result["level"] == ConfidenceLevel.HIGH
|
| 46 |
+
assert result["score"] > 0.7
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
def test_compute_confidence_low():
|
| 50 |
+
from app.analysis.confidence import compute_confidence
|
| 51 |
+
from app.models import ConfidenceLevel
|
| 52 |
+
result = compute_confidence(
|
| 53 |
+
valid_months=2, mean_obs_per_composite=1.5,
|
| 54 |
+
baseline_years_with_data=1, spatial_completeness=0.3,
|
| 55 |
+
)
|
| 56 |
+
assert result["level"] == ConfidenceLevel.LOW
|
| 57 |
+
assert result["score"] < 0.4
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
def test_compute_confidence_returns_factors():
|
| 61 |
+
from app.analysis.confidence import compute_confidence
|
| 62 |
+
result = compute_confidence(
|
| 63 |
+
valid_months=6, mean_obs_per_composite=5.0,
|
| 64 |
+
baseline_years_with_data=3, spatial_completeness=0.8,
|
| 65 |
+
)
|
| 66 |
+
assert "factors" in result
|
| 67 |
+
factors = result["factors"]
|
| 68 |
+
assert "temporal" in factors
|
| 69 |
+
assert "observation_density" in factors
|
| 70 |
+
assert "baseline_depth" in factors
|
| 71 |
+
assert "spatial_completeness" in factors
|