NaseefNazrul commited on
Commit
17999ac
·
verified ·
1 Parent(s): 9d8ff0f

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +51 -36
app.py CHANGED
@@ -119,72 +119,87 @@ def gaussian_kernel(length=12, sigma=1.2):
119
  kern = kern / kern.sum()
120
  return kern
121
 
122
- def smooth_monthly_probs(raw_probs, alpha=ALPHA, sigma=SMOOTH_SIGMA, min_curve_prob=MIN_CURVE_PROB):
 
 
 
123
  """
124
- Robust smoothing + contrast stretch for raw monthly ML probabilities.
125
-
126
- raw_probs: list/array length 12, values expected in [0,100]
127
- Returns: list length 12, percentages summing to ~100
 
 
 
 
 
 
128
  """
129
- a = np.asarray(raw_probs, dtype=float) / 100.0 # scale to 0-1
130
 
131
- # If all zeros => return uniform (but avoid strict uniform by small floor)
132
  if a.sum() == 0 or np.allclose(a, 0.0):
133
- base = np.ones_like(a) * (min_curve_prob / 100.0)
134
- base = base / base.sum()
135
- return (base * 100.0).round(3).tolist()
 
136
 
137
- # contrast stretch (min-max), but avoid tiny denominator
138
  amin = float(a.min())
139
  amax = float(a.max())
140
  rng = amax - amin
141
- if rng < 1e-4:
142
- # values almost identical -> amplify relative differences using sqrt of original
143
- a_cs = np.sqrt(a)
144
- a_cs = a_cs - a_cs.min()
145
- rng2 = a_cs.max() - a_cs.min()
146
- if rng2 <= 1e-8:
147
- a_cs = np.ones_like(a) / len(a)
148
  else:
149
- a_cs = a_cs / rng2
150
  else:
151
  a_cs = (a - amin) / (rng + 1e-12)
152
 
153
- # small floor to avoid zeros everywhere
154
  floor = min_curve_prob / 100.0
155
- a_cs = np.clip(a_cs, floor * 0.001, None)
156
 
157
- # apply sharpening exponent
158
  if alpha != 1.0:
159
  a_sh = np.power(a_cs, alpha)
160
  else:
161
  a_sh = a_cs
162
 
163
- # circular pad and gaussian smooth
164
  sigma = float(max(0.6, sigma))
165
  pad = max(3, int(round(3 * sigma)))
166
  padded = np.concatenate([a_sh[-pad:], a_sh, a_sh[:pad]])
167
  kern_range = np.arange(-pad, pad + 1)
168
  kern = np.exp(-0.5 * (kern_range / sigma) ** 2)
169
  kern = kern / kern.sum()
170
- smoothed = np.convolve(padded, kern, mode="same")
171
  center = smoothed[pad:pad+12]
172
  center = np.clip(center, 0.0, None)
173
 
 
174
  if center.sum() <= 0:
175
- center = np.ones_like(center) / len(center)
176
-
177
- # Normalize & to percentages
178
  norm = center / center.sum()
179
- perc = (norm * 100.0)
180
- # tiny rounding
181
- perc = np.round(perc, 3)
182
- # final safety: ensure sum ~= 100
183
- s = float(perc.sum())
184
- if abs(s - 100.0) > 0.001 and s > 0:
185
- perc = perc * (100.0 / s)
186
-
187
- return perc.tolist()
 
 
 
 
 
 
 
 
 
188
 
189
  def is_bell_shaped(perc_list):
190
  """
 
119
  kern = kern / kern.sum()
120
  return kern
121
 
122
+ def smooth_monthly_probs_preserve_magnitude(raw_probs,
123
+ alpha=ALPHA,
124
+ sigma=SMOOTH_SIGMA,
125
+ min_curve_prob=MIN_CURVE_PROB):
126
  """
127
+ Convert raw_probs (0..100) -> two arrays:
128
+ - monthly_scores: absolute smoothed scores scaled to 0..100 (preserves magnitude)
129
+ - monthly_perc: normalized percentages summing to 100 (for plotting)
130
+ Steps:
131
+ 1. scale to 0..1
132
+ 2. tiny contrast-stretch so differences matter
133
+ 3. apply exponent alpha to emphasize peaks
134
+ 4. circular gaussian smoothing
135
+ 5. monthly_scores: scale smoothed values so max -> 100
136
+ 6. monthly_perc: normalize the same smoothed values to sum -> 100
137
  """
138
+ a = np.asarray(raw_probs, dtype=float) / 100.0 # 0..1
139
 
140
+ # Guard: if all zeros, return uniform small floor
141
  if a.sum() == 0 or np.allclose(a, 0.0):
142
+ uniform = np.ones(12) * (min_curve_prob / 100.0)
143
+ monthly_scores = (uniform / uniform.max()) * 100.0
144
+ monthly_perc = (uniform / uniform.sum()) * 100.0
145
+ return monthly_perc.tolist(), monthly_scores.tolist()
146
 
147
+ # Contrast stretch (min-max) to amplify differences
148
  amin = float(a.min())
149
  amax = float(a.max())
150
  rng = amax - amin
151
+ if rng < 1e-6:
152
+ a_cs = a - amin
153
+ if a_cs.max() <= 1e-12:
154
+ a_cs = np.ones_like(a) * 1e-6
 
 
 
155
  else:
156
+ a_cs = a_cs / (a_cs.max() + 1e-12)
157
  else:
158
  a_cs = (a - amin) / (rng + 1e-12)
159
 
160
+ # floor tiny values (avoid zero everywhere)
161
  floor = min_curve_prob / 100.0
162
+ a_cs = np.clip(a_cs, floor * 1e-3, 1.0)
163
 
164
+ # sharpen peaks
165
  if alpha != 1.0:
166
  a_sh = np.power(a_cs, alpha)
167
  else:
168
  a_sh = a_cs
169
 
170
+ # circular pad and gaussian kernel
171
  sigma = float(max(0.6, sigma))
172
  pad = max(3, int(round(3 * sigma)))
173
  padded = np.concatenate([a_sh[-pad:], a_sh, a_sh[:pad]])
174
  kern_range = np.arange(-pad, pad + 1)
175
  kern = np.exp(-0.5 * (kern_range / sigma) ** 2)
176
  kern = kern / kern.sum()
177
+ smoothed = np.convolve(padded, kern, mode='same')
178
  center = smoothed[pad:pad+12]
179
  center = np.clip(center, 0.0, None)
180
 
181
+ # monthly_perc: normalized to sum 100 for plotting
182
  if center.sum() <= 0:
183
+ center = np.ones_like(center)
 
 
184
  norm = center / center.sum()
185
+ monthly_perc = (norm * 100.0).round(3)
186
+
187
+ # monthly_scores: scale by max to 0..100 (preserve magnitude)
188
+ maxv = float(center.max())
189
+ if maxv <= 0:
190
+ monthly_scores = np.ones_like(center) * (min_curve_prob)
191
+ else:
192
+ monthly_scores = (center / maxv) * 100.0
193
+ monthly_scores = np.round(monthly_scores, 3)
194
+
195
+ # final safety normalization
196
+ s = float(monthly_perc.sum())
197
+ if s <= 0:
198
+ monthly_perc = np.ones(12) * (100.0 / 12.0)
199
+ else:
200
+ monthly_perc = monthly_perc * (100.0 / monthly_perc.sum())
201
+
202
+ return monthly_perc.tolist(), monthly_scores.tolist()
203
 
204
  def is_bell_shaped(perc_list):
205
  """