GiusMagi commited on
Commit
199ff37
·
verified ·
1 Parent(s): 3cdd126

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +101 -0
app.py CHANGED
@@ -60,6 +60,76 @@ def _load_model():
60
  def health():
61
  return {"ok": predictor is not None, "uptime_s": time.time()-t0}
62
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
63
  @app.post("/predict")
64
  def predict(inp: PredictIn):
65
  if predictor is None:
@@ -89,3 +159,34 @@ def predict(inp: PredictIn):
89
  except Exception as e:
90
  log.exception("predict_error")
91
  raise HTTPException(500, f"Prediction error: {e}") from e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
60
  def health():
61
  return {"ok": predictor is not None, "uptime_s": time.time()-t0}
62
 
63
+ # Ordine delle classi (stesso usato dal modello)
64
+ _CLASS_ORDER = LABELS + ["100%"]
65
+ _CLASS_TO_IDX = {c: i for i, c in enumerate(_CLASS_ORDER)}
66
+
67
+ def _payload_from_inp(inp) -> dict:
68
+ """Ricostruisce un dict 'payload' a partire dall'input pydantic."""
69
+ payload = {}
70
+ for k in FEATURE_MAP.values():
71
+ ak = k.replace(" ", "_").replace(".", "_")
72
+ payload[k] = getattr(inp, ak, None)
73
+ return payload
74
+
75
+ def _moving_average(y: np.ndarray, window: int = 9):
76
+ """Applica una media mobile semplice per smoothing."""
77
+ w = int(window)
78
+ if w < 1:
79
+ return y
80
+ if w % 2 == 0:
81
+ w += 1
82
+ if w > len(y):
83
+ w = max(1, len(y)//2*2+1)
84
+ kernel = np.ones(w) / w
85
+ return np.convolve(y, kernel, mode="same")
86
+
87
+ def _class_curve_png(predictor, base_payload: dict, var_name: str,
88
+ vmin: int = 0, vmax: int = 3000,
89
+ n_base: int = 80, # punti reali (inferenze)
90
+ n_dense: int = 400, # punti interpolati
91
+ ma_window: int = 9,
92
+ title: str = "") -> bytes:
93
+ xs_base = np.linspace(vmin, vmax, n_base).round().astype(int)
94
+ xs_base = np.clip(xs_base, vmin, vmax)
95
+ xs_base = np.unique(xs_base)
96
+
97
+ # classe → indice
98
+ y_base = []
99
+ for v in xs_base:
100
+ p = dict(base_payload)
101
+ p[var_name] = int(v)
102
+ out = predictor.predict_class_fast(p)
103
+ y_base.append(_CLASS_TO_IDX[out["class"]])
104
+ y_base = np.array(y_base, dtype=float)
105
+
106
+ # interpolazione
107
+ xs_dense = np.linspace(vmin, vmax, n_dense)
108
+ y_dense = np.interp(xs_dense, xs_base, y_base)
109
+
110
+ # smoothing
111
+ y_smooth = _moving_average(y_dense, ma_window)
112
+ y_smooth = np.clip(y_smooth, 0, len(_CLASS_ORDER)-1)
113
+
114
+ # plot
115
+ fig, ax = plt.subplots(figsize=(9, 4))
116
+ ax.plot(xs_dense, y_smooth, linewidth=2)
117
+ ax.set_xlim(vmin, vmax)
118
+ ax.set_ylim(-0.2, len(_CLASS_ORDER)-1 + 0.2)
119
+ ax.set_yticks(range(len(_CLASS_ORDER)))
120
+ ax.set_yticklabels(_CLASS_ORDER)
121
+ ax.set_xlabel(var_name)
122
+ ax.set_ylabel("Classe (smooth)")
123
+ ax.set_title(title or f"Classe (smooth) vs {var_name}")
124
+ ax.grid(True, linestyle="--", alpha=0.35)
125
+ fig.tight_layout()
126
+
127
+ buf = io.BytesIO()
128
+ fig.savefig(buf, format="png", dpi=150, bbox_inches="tight")
129
+ plt.close(fig)
130
+ return buf.getvalue()
131
+
132
+
133
  @app.post("/predict")
134
  def predict(inp: PredictIn):
135
  if predictor is None:
 
159
  except Exception as e:
160
  log.exception("predict_error")
161
  raise HTTPException(500, f"Prediction error: {e}") from e
162
+
163
+ @app.post("/plot/curve-class-cessione.png")
164
+ def plot_curve_class_cessione(inp: PredictIn,
165
+ vmin: int = 0, vmax: int = 3000,
166
+ n_base: int = 80, n_dense: int = 400, ma_window: int = 9):
167
+ if predictor is None:
168
+ raise HTTPException(503, "Model not ready")
169
+ base_payload = _payload_from_inp(inp)
170
+ img = _class_curve_png(
171
+ predictor, base_payload,
172
+ var_name="giorni_da_cessione",
173
+ vmin=vmin, vmax=vmax, n_base=n_base, n_dense=n_dense, ma_window=ma_window,
174
+ title="Classe predetta vs Giorni da Cessione"
175
+ )
176
+ return Response(content=img, media_type="image/png")
177
+
178
+ @app.post("/plot/curve-class-iscrizione.png")
179
+ def plot_curve_class_iscrizione(inp: PredictIn,
180
+ vmin: int = 0, vmax: int = 3000,
181
+ n_base: int = 80, n_dense: int = 400, ma_window: int = 9):
182
+ if predictor is None:
183
+ raise HTTPException(503, "Model not ready")
184
+ base_payload = _payload_from_inp(inp)
185
+ img = _class_curve_png(
186
+ predictor, base_payload,
187
+ var_name="giorni_da_iscrizione",
188
+ vmin=vmin, vmax=vmax, n_base=n_base, n_dense=n_dense, ma_window=ma_window,
189
+ title="Classe predetta (smooth) vs Giorni da Iscrizione"
190
+ )
191
+ return Response(content=img, media_type="image/png")
192
+