| | <!DOCTYPE html> |
| | <html lang="en"> |
| | <head> |
| | <meta charset="UTF-8"> |
| | <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| | <title>Global Leaderboard</title> |
| | <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> |
| | <style> |
| | :root { |
| | --primary-color: #8e44ad; |
| | --secondary-color: #2c3e50; |
| | --background-color: #f8f9fa; |
| | --text-color: #333; |
| | --border-color: #dee2e6; |
| | --hover-color: #f1f1f1; |
| | --sidebar-width: 280px; |
| | } |
| | |
| | body { |
| | font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; |
| | margin: 0; |
| | padding: 0; |
| | background-color: var(--background-color); |
| | color: var(--text-color); |
| | display: flex; |
| | min-height: 100vh; |
| | } |
| | |
| | |
| | .sidebar { |
| | width: var(--sidebar-width); |
| | background-color: var(--secondary-color); |
| | color: white; |
| | position: fixed; |
| | height: 100vh; |
| | overflow-y: auto; |
| | padding: 20px; |
| | box-sizing: border-box; |
| | left: 0; |
| | top: 0; |
| | z-index: 100; |
| | display: flex; |
| | flex-direction: column; |
| | gap: 20px; |
| | } |
| | |
| | .sidebar h2 { |
| | font-size: 1.2em; |
| | margin-bottom: 10px; |
| | color: #ecf0f1; |
| | border-bottom: 1px solid #34495e; |
| | padding-bottom: 5px; |
| | } |
| | |
| | .nav-links { |
| | list-style: none; |
| | padding: 0; |
| | margin: 0; |
| | } |
| | |
| | .nav-links li a { |
| | display: block; |
| | padding: 10px; |
| | color: #bdc3c7; |
| | text-decoration: none; |
| | border-radius: 4px; |
| | transition: background 0.2s; |
| | } |
| | |
| | .nav-links li a:hover, .nav-links li a.active { |
| | background: rgba(255,255,255,0.1); |
| | color: white; |
| | } |
| | |
| | |
| | .main-content { |
| | margin-left: var(--sidebar-width); |
| | flex: 1; |
| | padding: 40px; |
| | max-width: calc(100% - var(--sidebar-width)); |
| | box-sizing: border-box; |
| | min-width: 0; |
| | } |
| | |
| | .container { |
| | max-width: 1200px; |
| | margin: 0 auto; |
| | background: white; |
| | padding: 20px; |
| | border-radius: 8px; |
| | box-shadow: 0 2px 4px rgba(0,0,0,0.1); |
| | } |
| | |
| | header { |
| | display: flex; |
| | justify-content: space-between; |
| | align-items: center; |
| | margin-bottom: 20px; |
| | border-bottom: 2px solid var(--primary-color); |
| | padding-bottom: 10px; |
| | } |
| | |
| | h1 { |
| | margin: 0; |
| | color: var(--secondary-color); |
| | } |
| | |
| | .description-box { |
| | background-color: #e8f4fd; |
| | border-left: 4px solid #3498db; |
| | padding: 15px; |
| | margin-bottom: 20px; |
| | border-radius: 4px; |
| | } |
| | |
| | .description-box h3 { |
| | margin-top: 0; |
| | color: #2980b9; |
| | } |
| | |
| | .description-box p { |
| | margin: 5px 0; |
| | line-height: 1.5; |
| | } |
| | |
| | .weights-control { |
| | background-color: #f1f1f1; |
| | padding: 15px; |
| | border-radius: 8px; |
| | margin-bottom: 20px; |
| | border: 1px solid #ddd; |
| | } |
| | |
| | .weights-control h3 { |
| | margin-top: 0; |
| | margin-bottom: 15px; |
| | font-size: 1.1em; |
| | } |
| | |
| | .sliders-container { |
| | display: flex; |
| | gap: 20px; |
| | flex-wrap: wrap; |
| | align-items: center; |
| | } |
| | |
| | .slider-group { |
| | display: flex; |
| | align-items: center; |
| | gap: 10px; |
| | } |
| | |
| | .slider-group label { |
| | font-weight: bold; |
| | min-width: 60px; |
| | } |
| | |
| | input[type="number"] { |
| | width: 80px; |
| | padding: 5px; |
| | border: 1px solid #ccc; |
| | border-radius: 4px; |
| | } |
| | |
| | input[readonly] { |
| | background-color: #e9ecef; |
| | color: #666; |
| | } |
| | |
| | button.recalc-btn { |
| | background-color: var(--primary-color); |
| | color: white; |
| | border: none; |
| | padding: 8px 20px; |
| | border-radius: 4px; |
| | cursor: pointer; |
| | font-size: 14px; |
| | margin-left: auto; |
| | } |
| | |
| | button.recalc-btn:hover { |
| | opacity: 0.9; |
| | } |
| | |
| | table { |
| | width: 100%; |
| | border-collapse: collapse; |
| | margin-top: 10px; |
| | } |
| | |
| | th, td { |
| | padding: 12px 15px; |
| | text-align: left; |
| | border-bottom: 1px solid var(--border-color); |
| | } |
| | |
| | th { |
| | background-color: var(--secondary-color); |
| | color: white; |
| | cursor: pointer; |
| | user-select: none; |
| | position: sticky; |
| | top: 0; |
| | } |
| | |
| | th:hover { |
| | background-color: #34495e; |
| | } |
| | |
| | th .arrow { |
| | font-size: 10px; |
| | margin-left: 5px; |
| | opacity: 0.7; |
| | } |
| | |
| | tr:hover { |
| | background-color: var(--hover-color); |
| | } |
| | |
| | .score-bar { |
| | height: 6px; |
| | background-color: #e9ecef; |
| | border-radius: 3px; |
| | overflow: hidden; |
| | margin-top: 5px; |
| | } |
| | |
| | .score-fill { |
| | height: 100%; |
| | background-color: var(--primary-color); |
| | } |
| | |
| | .version-tag { |
| | font-size: 0.8em; |
| | color: #7f8c8d; |
| | margin-top: 5px; |
| | } |
| | </style> |
| | </head> |
| | <body> |
| |
|
| | <div class="sidebar"> |
| | <div style="text-align: center; margin-bottom: 20px;"> |
| | <h1 style="font-size: 1.5em; margin: 0; color: white;">AutoFS</h1> |
| | <div style="font-size: 0.8em; color: #bdc3c7;">Feature Selection Leaderboard</div> |
| | </div> |
| |
|
| | <div> |
| | <h2>Navigation</h2> |
| | <ul class="nav-links"> |
| | <li><a href="/">π Overview</a></li> |
| | <li><a href="/#main-table">π Leaderboard</a></li> |
| | <li><a href="/#charts">π Charts</a></li> |
| | <li><a href="/#details">βΉοΈ Details</a></li> |
| | <li><a href="/global" class="active">π Global Rankings</a></li> |
| | <li><a href="/submit">π€ Submit Data/Method</a></li> |
| | </ul> |
| | </div> |
| | |
| | <div style="margin-top: auto; padding-top: 20px; border-top: 1px solid #34495e;"> |
| | <p style="font-size: 0.8em; color: #bdc3c7; text-align: center;"> |
| | Need help?<br> |
| | <a href="mailto:support@autofs.com" style="color: var(--primary-color);">Contact Support</a> |
| | </p> |
| | </div> |
| | </div> |
| |
|
| | <div class="main-content"> |
| | <div class="container"> |
| | <header> |
| | <div> |
| | <h1>π Global Algorithm Rankings</h1> |
| | <div id="last-updated" class="version-tag">Data Last Updated: Loading...</div> |
| | </div> |
| | |
| | </header> |
| |
|
| | <div class="description-box"> |
| | <h3>About Global Rankings</h3> |
| | <p> |
| | This page provides a comprehensive evaluation of feature selection algorithms across all available datasets. |
| | Algorithms are ranked based on a weighted score combining <strong>Accuracy (F1)</strong>, <strong>Robustness (AUC)</strong>, and <strong>Efficiency (Time)</strong>. |
| | You can adjust the importance of each factor below to customize the ranking criteria. |
| | </p> |
| | </div> |
| |
|
| | <div class="weights-control"> |
| | <h3>π Scoring Formula: S = Ξ±Β·F1 + Ξ²Β·AUC</h3> |
| | <p style="font-size: 0.9em; color: #666; margin-bottom: 10px;"> |
| | Constraint: Ξ± + Ξ² = 1. |
| | </p> |
| | |
| | <div class="sliders-container"> |
| | <div class="slider-group"> |
| | <label for="weight-a">F1 (Ξ±):</label> |
| | <input type="number" id="weight-a" value="0.5" step="0.05" min="0" max="1"> |
| | </div> |
| | |
| | <div class="slider-group"> |
| | <label for="weight-b">AUC (Ξ²):</label> |
| | <input type="number" id="weight-b" value="0.5" readonly title="Auto-calculated: 1 - Ξ±"> |
| | </div> |
| |
|
| | <button class="recalc-btn" onclick="calculateAndRender()">Recalculate Rankings</button> |
| | </div> |
| | </div> |
| |
|
| | <div id="loading-indicator" style="text-align: center; color: #666;">Loading global stats...</div> |
| |
|
| | <table id="global-table"> |
| | <thead> |
| | <tr> |
| | <th data-key="rank">#</th> |
| | <th data-key="algorithm">Algorithm <span class="arrow">β</span></th> |
| | <th data-key="mean_f1_global">Global F1 <span class="arrow">β</span></th> |
| | <th data-key="mean_auc_global">Global AUC <span class="arrow">β</span></th> |
| | <th data-key="final_score">Final Score <span class="arrow">β</span></th> |
| | </tr> |
| | </thead> |
| | <tbody> |
| | |
| | </tbody> |
| | </table> |
| | </div> |
| | </div> |
| |
|
| | <script> |
| | let rawData = []; |
| | let processedData = []; |
| | let sortKey = 'final_score'; |
| | let sortDirection = -1; |
| | |
| | const tableBody = document.querySelector("#global-table tbody"); |
| | const weightA = document.getElementById("weight-a"); |
| | const weightB = document.getElementById("weight-b"); |
| | const lastUpdatedDiv = document.getElementById("last-updated"); |
| | |
| | |
| | function fetchLastUpdated() { |
| | fetch("/api/datasets") |
| | .then(res => res.json()) |
| | .then(data => { |
| | if (data.length > 0) { |
| | |
| | |
| | |
| | |
| | const dates = data.map(d => d.last_updated).filter(d => d !== 'Unknown').sort().reverse(); |
| | if (dates.length > 0) { |
| | lastUpdatedDiv.textContent = `Data Last Updated: ${dates[0]}`; |
| | } else { |
| | lastUpdatedDiv.textContent = `Data Last Updated: Unknown`; |
| | } |
| | } |
| | }); |
| | } |
| | |
| | function fetchGlobalStats() { |
| | fetch("/api/global_stats") |
| | .then(res => res.json()) |
| | .then(data => { |
| | rawData = data; |
| | document.getElementById("loading-indicator").style.display = 'none'; |
| | calculateAndRender(); |
| | }) |
| | .catch(err => { |
| | console.error("Error:", err); |
| | document.getElementById("loading-indicator").textContent = "Error loading data."; |
| | }); |
| | } |
| | |
| | |
| | function updateWeights(changedInput) { |
| | let a = parseFloat(weightA.value) || 0; |
| | |
| | |
| | if (a < 0) a = 0; if (a > 1) a = 1; |
| | |
| | if (changedInput === 'a') { |
| | let b = 1 - a; |
| | |
| | weightA.value = parseFloat(a.toFixed(2)); |
| | weightB.value = parseFloat(b.toFixed(2)); |
| | } |
| | } |
| | |
| | weightA.addEventListener('input', () => updateWeights('a')); |
| | |
| | function calculateAndRender() { |
| | const a = parseFloat(weightA.value) || 0; |
| | const b = parseFloat(weightB.value) || 0; |
| | |
| | |
| | processedData = rawData.map(d => { |
| | const score = (a * d.mean_f1_global) + (b * d.mean_auc_global); |
| | |
| | return { |
| | ...d, |
| | final_score: score |
| | }; |
| | }); |
| | |
| | sortData(); |
| | renderTable(); |
| | } |
| | |
| | function sortData() { |
| | processedData.sort((x, y) => { |
| | const valX = x[sortKey]; |
| | const valY = y[sortKey]; |
| | |
| | if (valX < valY) return -1 * sortDirection; |
| | if (valX > valY) return 1 * sortDirection; |
| | return 0; |
| | }); |
| | } |
| | |
| | function safeFixed(val, digits=4) { |
| | if (val === undefined || val === null) return 'N/A'; |
| | return Number(val).toFixed(digits); |
| | } |
| | |
| | function renderTable() { |
| | tableBody.innerHTML = ""; |
| | |
| | if (processedData.length === 0) { |
| | tableBody.innerHTML = '<tr><td colspan="5" style="text-align:center;">No data available</td></tr>'; |
| | return; |
| | } |
| | |
| | processedData.forEach((row, index) => { |
| | const tr = document.createElement("tr"); |
| | |
| | |
| | let rankHtml = index + 1; |
| | if (index === 0) rankHtml = 'π₯ 1'; |
| | if (index === 1) rankHtml = 'π₯ 2'; |
| | if (index === 2) rankHtml = 'π₯ 3'; |
| | |
| | tr.innerHTML = ` |
| | <td>${rankHtml}</td> |
| | <td><strong>${row.algorithm}</strong> <span style="font-size:0.8em; color:#999">(${row.datasets_count} datasets)</span></td> |
| | <td>${safeFixed(row.mean_f1_global)}</td> |
| | <td>${safeFixed(row.mean_auc_global)}</td> |
| | <td> |
| | <strong>${safeFixed(row.final_score)}</strong> |
| | <div class="score-bar"><div class="score-fill" style="width: ${Math.min(row.final_score * 100, 100)}%"></div></div> |
| | </td> |
| | `; |
| | tableBody.appendChild(tr); |
| | }); |
| | |
| | updateSortArrows(); |
| | } |
| | |
| | function updateSortArrows() { |
| | document.querySelectorAll('th .arrow').forEach(span => span.textContent = 'β'); |
| | const activeHeader = document.querySelector(`th[data-key="${sortKey}"] .arrow`); |
| | if (activeHeader) activeHeader.textContent = sortDirection === 1 ? 'β' : 'β'; |
| | } |
| | |
| | document.querySelectorAll('th[data-key]').forEach(th => { |
| | th.addEventListener('click', () => { |
| | const key = th.dataset.key; |
| | if (sortKey === key) { |
| | sortDirection *= -1; |
| | } else { |
| | sortKey = key; |
| | sortDirection = (key === 'rank') ? 1 : -1; |
| | } |
| | if (key === 'rank') { |
| | sortKey = 'final_score'; |
| | sortDirection = -1; |
| | } |
| | sortData(); |
| | renderTable(); |
| | }); |
| | }); |
| | |
| | document.addEventListener("DOMContentLoaded", () => { |
| | fetchLastUpdated(); |
| | fetchGlobalStats(); |
| | }); |
| | |
| | </script> |
| |
|
| | </body> |
| | </html> |
| |
|