Replace benchmark timeline with monthly usage bar chart
Browse files
app.py
CHANGED
|
@@ -408,15 +408,6 @@ def openness_chip(is_open: bool) -> str:
|
|
| 408 |
return f'<span class="chip"><i style="background:{color}"></i>{text}</span>'
|
| 409 |
|
| 410 |
|
| 411 |
-
def openness_legend() -> str:
|
| 412 |
-
return (
|
| 413 |
-
'<div class="legend">'
|
| 414 |
-
'<span class="key"><i class="round" style="background:var(--open)"></i>open-weight</span>'
|
| 415 |
-
'<span class="key"><i class="round" style="background:var(--closed)"></i>closed / API-only</span>'
|
| 416 |
-
"</div>"
|
| 417 |
-
)
|
| 418 |
-
|
| 419 |
-
|
| 420 |
def nice_ceil(v: float) -> float:
|
| 421 |
if v <= 0:
|
| 422 |
return 1
|
|
@@ -593,55 +584,63 @@ def popularity_view(category_filter, openness_filter, min_models, search) -> str
|
|
| 593 |
|
| 594 |
|
| 595 |
# ---------------------------------------------------------------------------
|
| 596 |
-
# Tab 2: Benchmark -> Models (summary card, SVG
|
| 597 |
# ---------------------------------------------------------------------------
|
| 598 |
-
def
|
| 599 |
-
|
| 600 |
-
|
| 601 |
-
|
| 602 |
-
|
| 603 |
-
|
| 604 |
-
|
| 605 |
-
|
| 606 |
-
|
| 607 |
-
|
| 608 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 609 |
|
| 610 |
parts = [
|
| 611 |
f'<svg class="viz" viewBox="0 0 {W} {H}" role="img" '
|
| 612 |
-
f'aria-label="
|
| 613 |
]
|
| 614 |
-
for
|
| 615 |
-
|
| 616 |
-
|
| 617 |
-
parts.append(
|
| 618 |
-
|
| 619 |
-
|
| 620 |
-
|
| 621 |
-
for i,
|
| 622 |
-
|
| 623 |
-
|
| 624 |
-
|
| 625 |
-
parts.append(f'<text x="{ML - 8}" y="{cy + 4}" text-anchor="end" font-size="11.5" fill="var(--ink)">{esc(name)}</text>')
|
| 626 |
-
sub = df[df["lab"] == lab].sort_values("release_date")
|
| 627 |
-
prev_cx, flip = None, 1
|
| 628 |
-
for r in sub.itertuples():
|
| 629 |
-
cx = x(r.release_date)
|
| 630 |
-
dy = 0.0
|
| 631 |
-
if prev_cx is not None and cx - prev_cx < 11:
|
| 632 |
-
dy = 5.0 * flip
|
| 633 |
-
flip = -flip
|
| 634 |
-
else:
|
| 635 |
-
flip = 1
|
| 636 |
-
prev_cx = cx
|
| 637 |
-
color = "var(--open)" if r.is_open else "var(--closed)"
|
| 638 |
-
kind = "open-weight" if r.is_open else "closed"
|
| 639 |
parts.append(
|
| 640 |
-
f'<
|
| 641 |
-
f
|
| 642 |
-
f"<title>{esc(r.model_id)} β {r.release_date.date()} ({kind})</title></circle>"
|
| 643 |
)
|
| 644 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 645 |
parts.append("</svg>")
|
| 646 |
return "".join(parts)
|
| 647 |
|
|
@@ -667,9 +666,8 @@ def benchmark_view(benchmark):
|
|
| 667 |
)
|
| 668 |
|
| 669 |
chart = (
|
| 670 |
-
f'<div class="viz-title">
|
| 671 |
-
+
|
| 672 |
-
+ svg_benchmark_timeline(df)
|
| 673 |
)
|
| 674 |
|
| 675 |
rows = []
|
|
@@ -738,14 +736,6 @@ def model_view(model_id):
|
|
| 738 |
# ---------------------------------------------------------------------------
|
| 739 |
# Tab 4: Category evolution (SVG stacked bars + table)
|
| 740 |
# ---------------------------------------------------------------------------
|
| 741 |
-
def _rounded_top_rect(x, y, w, h, r) -> str:
|
| 742 |
-
r = min(r, h / 2, w / 2)
|
| 743 |
-
return (
|
| 744 |
-
f'M {x:.1f} {y + h:.1f} L {x:.1f} {y + r:.1f} Q {x:.1f} {y:.1f} {x + r:.1f} {y:.1f} '
|
| 745 |
-
f'L {x + w - r:.1f} {y:.1f} Q {x + w:.1f} {y:.1f} {x + w:.1f} {y + r:.1f} L {x + w:.1f} {y + h:.1f} Z'
|
| 746 |
-
)
|
| 747 |
-
|
| 748 |
-
|
| 749 |
def svg_category_bars(counts: pd.DataFrame, normalize: bool) -> str:
|
| 750 |
periods = counts.index.tolist()
|
| 751 |
plot = counts.copy()
|
|
|
|
| 408 |
return f'<span class="chip"><i style="background:{color}"></i>{text}</span>'
|
| 409 |
|
| 410 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 411 |
def nice_ceil(v: float) -> float:
|
| 412 |
if v <= 0:
|
| 413 |
return 1
|
|
|
|
| 584 |
|
| 585 |
|
| 586 |
# ---------------------------------------------------------------------------
|
| 587 |
+
# Tab 2: Benchmark -> Models (summary card, monthly-usage SVG bars, table)
|
| 588 |
# ---------------------------------------------------------------------------
|
| 589 |
+
def _rounded_top_rect(x, y, w, h, r) -> str:
|
| 590 |
+
r = min(r, h / 2, w / 2)
|
| 591 |
+
return (
|
| 592 |
+
f'M {x:.1f} {y + h:.1f} L {x:.1f} {y + r:.1f} Q {x:.1f} {y:.1f} {x + r:.1f} {y:.1f} '
|
| 593 |
+
f'L {x + w - r:.1f} {y:.1f} Q {x + w:.1f} {y:.1f} {x + w:.1f} {y + r:.1f} L {x + w:.1f} {y + h:.1f} Z'
|
| 594 |
+
)
|
| 595 |
+
|
| 596 |
+
|
| 597 |
+
def svg_benchmark_monthly_usage(df: pd.DataFrame) -> str:
|
| 598 |
+
"""Bars of how many model releases reported this benchmark each month, spanning
|
| 599 |
+
the whole dataset time range (empty months stay visible as gaps)."""
|
| 600 |
+
t0 = MODELS_DF["release_date"].min().to_period("M")
|
| 601 |
+
t1 = MODELS_DF["release_date"].max().to_period("M")
|
| 602 |
+
months = pd.period_range(t0, t1, freq="M")
|
| 603 |
+
counts = df["release_date"].dt.to_period("M").value_counts().reindex(months, fill_value=0)
|
| 604 |
+
|
| 605 |
+
W, H, ML, MR, MT, MB = 920, 300, 44, 10, 12, 36
|
| 606 |
+
plot_h = H - MT - MB
|
| 607 |
+
base = MT + plot_h
|
| 608 |
+
vmax = int(counts.max())
|
| 609 |
+
step = max(1, math.ceil(vmax / 4))
|
| 610 |
+
ymax = step * 4
|
| 611 |
+
scale = plot_h / ymax
|
| 612 |
+
band = (W - ML - MR) / len(months)
|
| 613 |
+
bw = max(band - 2, 1.5) # 2px surface gap between adjacent bars
|
| 614 |
|
| 615 |
parts = [
|
| 616 |
f'<svg class="viz" viewBox="0 0 {W} {H}" role="img" '
|
| 617 |
+
f'aria-label="Monthly count of model releases reporting this benchmark, over the whole dataset period">'
|
| 618 |
]
|
| 619 |
+
for k in range(5):
|
| 620 |
+
v = step * k
|
| 621 |
+
y = base - v * scale
|
| 622 |
+
parts.append(f'<line x1="{ML}" y1="{y:.1f}" x2="{W - MR}" y2="{y:.1f}" stroke="var(--grid)" stroke-width="1"/>')
|
| 623 |
+
parts.append(f'<text x="{ML - 7}" y="{y + 4:.1f}" text-anchor="end" font-size="11" fill="var(--muted)">{v}</text>')
|
| 624 |
+
|
| 625 |
+
tick_months = {t.to_period("M") for t in month_ticks(months[0].to_timestamp(), months[-1].to_timestamp(how="end"))}
|
| 626 |
+
for i, month in enumerate(months):
|
| 627 |
+
x0 = ML + i * band
|
| 628 |
+
if month in tick_months:
|
| 629 |
+
cx = x0 + band / 2
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 630 |
parts.append(
|
| 631 |
+
f'<text x="{cx:.1f}" y="{base + 20}" text-anchor="middle" font-size="11" fill="var(--muted)">'
|
| 632 |
+
f"{month.to_timestamp().strftime('%b %Y')}</text>"
|
|
|
|
| 633 |
)
|
| 634 |
+
n = int(counts.loc[month])
|
| 635 |
+
if n == 0:
|
| 636 |
+
continue
|
| 637 |
+
h = n * scale
|
| 638 |
+
label = f"{month.to_timestamp().strftime('%b %Y')}: {n} model{'s' if n != 1 else ''}"
|
| 639 |
+
parts.append(
|
| 640 |
+
f'<path class="seg" d="{_rounded_top_rect(x0 + 1, base - h, bw, h, 4)}" fill="var(--accent)">'
|
| 641 |
+
f"<title>{esc(label)}</title></path>"
|
| 642 |
+
)
|
| 643 |
+
parts.append(f'<line x1="{ML}" y1="{base}" x2="{W - MR}" y2="{base}" stroke="var(--muted)" stroke-width="1"/>')
|
| 644 |
parts.append("</svg>")
|
| 645 |
return "".join(parts)
|
| 646 |
|
|
|
|
| 666 |
)
|
| 667 |
|
| 668 |
chart = (
|
| 669 |
+
f'<div class="viz-title">How often is β{esc(benchmark)}β used? Model releases reporting it per month</div>'
|
| 670 |
+
+ svg_benchmark_monthly_usage(df)
|
|
|
|
| 671 |
)
|
| 672 |
|
| 673 |
rows = []
|
|
|
|
| 736 |
# ---------------------------------------------------------------------------
|
| 737 |
# Tab 4: Category evolution (SVG stacked bars + table)
|
| 738 |
# ---------------------------------------------------------------------------
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 739 |
def svg_category_bars(counts: pd.DataFrame, normalize: bool) -> str:
|
| 740 |
periods = counts.index.tolist()
|
| 741 |
plot = counts.copy()
|