- dati/ rinominata input/ (CSV ignorati, cartella tracciata con .gitkeep)
- lavoro/ sostituita da output/<nome csv>/{01_domini,02_sovrapposizione,03_report}
- report.md con indice, tabella equazioni e immagini incorporate
- sovrapposizione complessiva dati-equazioni per serie
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
fitseg
Legge un CSV t_ms,T_C,dT_C (temperatura di un sensore IR che si scalda e si raffredda), riconosce riposo, riscaldamento (gradini di flusso in entrata) e raffreddamento, lo divide in domini e trova per ogni dominio e ogni serie l'equazione che lo approssima meglio, con minimi quadrati non lineari.
Risultato per dominio: modello scelto, parametri con intervallo di confidenza al 95 %, metriche (RMSE, R², AICc, BIC, autocorrelazione dei residui), un grafico dati/equazioni e un report Markdown.
Installazione
Python ≥ 3.11 e uv (curl -LsSf https://astral.sh/uv/install.sh | sh):
uv venv .venv
uv pip install -e ".[dev]" # numpy, scipy, matplotlib; pytest e ruff per lo sviluppo
Dati di ingresso
CSV con intestazione, tempo crescente e le serie indicate in config.toml ([dati]: colonne, unità ms/s, separatore). I CSV di prova vengono dal generatore ../gen_data; metti i CSV da analizzare nella cartella input/ (la cartella è nel repository, i file *.csv no); i test di integrazione usano i seed 1–3:
mkdir -p input
for s in 1 2 3; do
(cd ../gen_data && .venv/bin/python genera.py config.toml --seed $s -o ../EquationMiner/input/gen_$s.csv)
done
Uso
.venv/bin/fitseg domini input/gen_1.csv # passo 1: come verrebbero suddivisi i dati (tabella, domini.png, domini.toml), nessun fit
.venv/bin/fitseg analizza input/gen_1.csv # tutti i passi: domini, equazioni, sovrapposizione, report
--config PATH (default config.toml) e --output DIR (default output) vanno prima del sottocomando. I file di ogni analisi vanno in output/<nome del csv>/.
Correggere i domini a mano: apri output/gen_1/01_domini/domini.toml, modifica i tempi (i domini devono restare contigui) e rilancia
.venv/bin/fitseg analizza input/gen_1.csv --domini output/gen_1/01_domini/domini.toml
[[dominio]]
tipo = "riscaldamento" # riposo | riscaldamento | raffreddamento
t0_s = 15.8 # incluso
t1_s = 17.76 # escluso (l'ultimo dominio arriva a fine dati)
Cosa produce
Una cartella per ogni CSV analizzato, con un passo per sottocartella (output/ è ignorata da git):
output/gen_1/
├── 01_domini/ suddivisione dei dati
│ ├── domini.png dati grezzi con i domini (colore per tipo, numerati)
│ └── domini.toml domini usati, modificabili a mano
├── 02_sovrapposizione/ dati grezzi contro le equazioni trovate
│ ├── sovrapposizione_<serie>.png serie intera con le equazioni a tratti e i residui
│ └── dominio<k>_<serie>.png un dominio: dati, modello scelto, altri modelli validi, residui
└── 03_report/
├── report.md report completo con tutte le immagini incorporate
└── risultati.json tutti i fit con tutte le metriche
report.md contiene: (1) suddivisione in domini, (2) tabella delle equazioni con parametri ± IC 95 %, (3) sovrapposizione dati–equazioni, (4) dettaglio per dominio con metriche e tabella di tutti i modelli provati. Le immagini sono link relativi: si vedono in un visualizzatore Markdown.
Come leggere il report: non adeguato = RMSE oltre 1.3 volte il rumore stimato (guarda l'ACF1 dei residui); indeterminato = nessun modello valido o dominio con meno di n_min_punti campioni. Nel raffreddamento τ è la costante di tempo e c l'asintoto (≈ ambiente per T_C, ≈ 0 per dT_C).
Come funziona
- Domini (
domini.py): un salto positivo tra due campioni consecutivi soprasoglia_gradino_cè un gradino di riscaldamento. Prima del primo gradino:riposo; tra due gradini:riscaldamento; dopo l'ultimo:raffreddamento. - Modelli (
modelli.py):riposo→costante,lineare,esp_decad;riscaldamentoeraffreddamento→ soloesp_decady = c + A·exp(−t'/τ), cont' = t − t0del dominio. - Fit (
fit.py):scipy.optimize.least_squarescon perdita robusta; partenza dell'esponenziale da una griglia su τ. I domini di raffreddamento si fittano per primi e nei domini di riscaldamentoceτsono fissati ai loro valori (stesse costanti fisiche; in 2–4 s di dati con scarti casuali τ non si stima da solo), restando libera soloA. Gli IC diAsono condizionati ai valori fissi. - Scelta (
analisi.py): scarta i modelli non convergenti, con parametri a limite o non identificabili; preferisce i modelli «adeguati» (RMSE ≤rmse_vs_rumore_max× σ rumore); tra quelli entrosoglia_delta_aiccdal miglior AICc vince chi ha meno parametri.
Specifica e limiti: docs/SPEC.md.
Configurazione
Tutti i valori sono in config.toml, nessun default nel codice (chiave mancante = errore, exit 3). Le principali: [domini] soglia_gradino_c (20 °C), scarta_dopo_salto_n (campioni di transizione esclusi dal fit), n_min_punti; [fit.modelli_per_tipo] (modelli per tipo di dominio); [fit] riscaldamento_fissa_da_raffreddamento (["c", "tau"], [] = parametri liberi), soglia_delta_aicc, rmse_vs_rumore_max.
Nuovo modello: voce in MODELLI di src/fitseg/modelli.py, riga in SPEC §4 e nome in [fit.modelli_per_tipo].
Test
.venv/bin/pytest # tutti (≈ 4 s)
.venv/bin/pytest tests/test_fit.py # un file
.venv/bin/pytest -k raffreddamento # per nome
.venv/bin/ruff check src tests
| File | Verifica | Servono i CSV? |
|---|---|---|
test_domini.py |
3 gradini, nessun gradino, un solo gradino, gradino sotto soglia, confini vicini ai gradini veri, domini.toml (andata e ritorno, non contiguo, tipo non valido) |
no |
test_fit.py |
recupero di τ, c, A su segnale sintetico (τ = 16 s), parametri fissi, τ non identificabile su tratti corti e rumorosi, analisi completa, dominio troppo corto, riproducibilità |
no |
test_selezione.py |
regola di scelta | no |
test_config_dati.py |
errori di config e CSV | no |
test_integrazione.py |
domini e τ su input/gen_{1,2,3}.csv; CLI domini e analizza --domini; codici di uscita |
sì (altrimenti saltati) |
I test unitari generano il segnale da soli (tests/conftest.py). Prova manuale: fitseg analizza input/gen_1.csv e controlla in output/gen_1/03_report/report.md 4 domini (riposo, due riscaldamenti, raffreddamento) con τ ≈ 16 s.
Codici di uscita: 0 ok · 3 configurazione non valida · 4 CSV illeggibile/non valido · 6 domini.toml non valido.
Limiti noti
- Nel raffreddamento i residui sono autocorrelati (ACF1 ≈ 0.7) per scarti casuali e deriva lenta del sensore: i parametri sono buoni, ma gli IC sono troppo stretti e il dominio risulta «non adeguato».
- Ogni dominio è fittato in modo indipendente (nessun vincolo di continuità); provato su pochi seed e con la config d'esempio di
gen_data.