Spaces:
Runtime error
Runtime error
adriens Claude Sonnet 4.6 commited on
Commit ·
a2178d7
1
Parent(s): 59fb2ba
feat: add optimal threshold chart (Jenks + KMeans convergence at 99 chars)
Browse filesHistogramme bicolore avec les 2 clusters, centroïdes et seuil à 99 chars.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
app.py
CHANGED
|
@@ -233,3 +233,41 @@ cdf_line = alt.Chart(cdf_data).mark_line(color="#1f77b4", strokeWidth=2).encode(
|
|
| 233 |
)
|
| 234 |
st.altair_chart(cdf_line + rule, use_container_width=True)
|
| 235 |
st.caption(f"La ligne rouge marque le seuil à **{threshold} caractères** — {pct_free:.1f}% des messages sont gratuits, {pct_paid:.1f}% sont payants.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 233 |
)
|
| 234 |
st.altair_chart(cdf_line + rule, use_container_width=True)
|
| 235 |
st.caption(f"La ligne rouge marque le seuil à **{threshold} caractères** — {pct_free:.1f}% des messages sont gratuits, {pct_paid:.1f}% sont payants.")
|
| 236 |
+
|
| 237 |
+
st.divider()
|
| 238 |
+
|
| 239 |
+
# --- Détermination du seuil optimal (Jenks + KMeans) ---
|
| 240 |
+
st.subheader("📐 Détermination du seuil optimal — Jenks & KMeans")
|
| 241 |
+
st.caption("Les deux méthodes convergent à **99 caractères** : frontière naturelle entre deux populations distinctes.")
|
| 242 |
+
|
| 243 |
+
SPLIT = 99
|
| 244 |
+
CENTER_SHORT, CENTER_LONG = 61.5, 137.0
|
| 245 |
+
|
| 246 |
+
col_a, col_b = st.columns(2)
|
| 247 |
+
col_a.metric("Centroïde messages courts", f"{CENTER_SHORT:.0f} chars", "cluster 1")
|
| 248 |
+
col_b.metric("Centroïde messages longs", f"{CENTER_LONG:.0f} chars", "cluster 2")
|
| 249 |
+
|
| 250 |
+
# Histogramme avec les 2 clusters colorés
|
| 251 |
+
hist_data = pd.DataFrame({"length": lengths})
|
| 252 |
+
hist_data["tier"] = hist_data["length"].apply(lambda x: "≤ 99 chars (gratuit)" if x <= SPLIT else "> 99 chars (auth requise)")
|
| 253 |
+
|
| 254 |
+
hist_chart = alt.Chart(hist_data).mark_bar(opacity=0.8).encode(
|
| 255 |
+
x=alt.X("length:Q", bin=alt.Bin(maxbins=60), title="Longueur (caractères)"),
|
| 256 |
+
y=alt.Y("count():Q", title="Fréquence"),
|
| 257 |
+
color=alt.Color("tier:N", scale=alt.Scale(
|
| 258 |
+
domain=["≤ 99 chars (gratuit)", "> 99 chars (auth requise)"],
|
| 259 |
+
range=["#1f77b4", "#d62728"]
|
| 260 |
+
), title="Tier"),
|
| 261 |
+
tooltip=["tier", "count()"],
|
| 262 |
+
).properties(height=300)
|
| 263 |
+
|
| 264 |
+
centroid_data = pd.DataFrame({"x": [CENTER_SHORT, CENTER_LONG], "label": ["Centre cluster 1", "Centre cluster 2"]})
|
| 265 |
+
centroid_rules = alt.Chart(centroid_data).mark_rule(strokeDash=[4, 2], strokeWidth=1.5).encode(
|
| 266 |
+
x="x:Q", color=alt.Color("label:N", scale=alt.Scale(range=["#1f77b4", "#d62728"]), legend=None)
|
| 267 |
+
)
|
| 268 |
+
split_rule = alt.Chart(pd.DataFrame({"x": [SPLIT]})).mark_rule(
|
| 269 |
+
color="black", strokeWidth=2
|
| 270 |
+
).encode(x="x:Q")
|
| 271 |
+
|
| 272 |
+
st.altair_chart(hist_chart + centroid_rules + split_rule, use_container_width=True)
|
| 273 |
+
st.caption("**Ligne noire** = seuil à 99 chars · **lignes pointillées** = centroïdes des deux clusters · bleu = gratuit · rouge = authentification requise")
|