Download AutoFS/Webapp/templates/index.html from Fancy-yousa/FeatureSelect-Methods-Leaderboard: direct link, hf CLI and curl.
- Browser
- Download file 20.7 kB
-
https://huggingface.co/spaces/Fancy-yousa/FeatureSelect-Methods-Leaderboard/resolve/main/AutoFS/Webapp/templates/index.html
- Command line
-
hf download hf://spaces/Fancy-yousa/FeatureSelect-Methods-Leaderboard/AutoFS/Webapp/templates/index.html
-
curl -L -o index.html https://huggingface.co/spaces/Fancy-yousa/FeatureSelect-Methods-Leaderboard/resolve/main/AutoFS/Webapp/templates/index.html
20.7 kB
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>AutoFS Leaderboard</title> | |
| <script src="https://cdn.jsdelivr.net/npm/chart.js"></script> | |
| <style> | |
| :root { | |
| --primary-color: #3498db; | |
| --secondary-color: #2c3e50; | |
| --background-color: #f8f9fa; | |
| --text-color: #333; | |
| --border-color: #dee2e6; | |
| --hover-color: #f1f1f1; | |
| } | |
| body { | |
| font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | |
| margin: 0; | |
| padding: 20px; | |
| background-color: var(--background-color); | |
| color: var(--text-color); | |
| } | |
| .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); | |
| } | |
| .controls { | |
| display: flex; | |
| gap: 10px; | |
| align-items: center; | |
| } | |
| select { | |
| padding: 8px 12px; | |
| border: 1px solid var(--border-color); | |
| border-radius: 4px; | |
| font-size: 14px; | |
| } | |
| 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); | |
| } | |
| .features-cell { | |
| max-width: 200px; | |
| white-space: nowrap; | |
| overflow: hidden; | |
| text-overflow: ellipsis; | |
| color: #666; | |
| font-size: 0.9em; | |
| cursor: pointer; | |
| } | |
| .features-cell:hover { | |
| text-decoration: underline; | |
| color: var(--primary-color); | |
| } | |
| /* Modal styles */ | |
| .modal { | |
| display: none; | |
| position: fixed; | |
| z-index: 1000; | |
| left: 0; | |
| top: 0; | |
| width: 100%; | |
| height: 100%; | |
| background-color: rgba(0,0,0,0.5); | |
| } | |
| .modal-content { | |
| background-color: white; | |
| margin: 10% auto; | |
| padding: 20px; | |
| border-radius: 8px; | |
| width: 50%; | |
| max-width: 600px; | |
| box-shadow: 0 4px 6px rgba(0,0,0,0.1); | |
| } | |
| .close { | |
| color: #aaa; | |
| float: right; | |
| font-size: 28px; | |
| font-weight: bold; | |
| cursor: pointer; | |
| } | |
| .close:hover { | |
| color: black; | |
| } | |
| .feature-tag { | |
| display: inline-block; | |
| background-color: #e1ecf4; | |
| color: #2c3e50; | |
| padding: 4px 8px; | |
| border-radius: 4px; | |
| margin: 2px; | |
| font-size: 0.9em; | |
| } | |
| .loading { | |
| text-align: center; | |
| padding: 20px; | |
| color: #666; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <header> | |
| <h1>🏆 AutoFS Leaderboard</h1> | |
| <div class="controls"> | |
| <label for="dataset-select">Dataset:</label> | |
| <select id="dataset-select"> | |
| <option value="" disabled selected>Loading...</option> | |
| </select> | |
| </div> | |
| </header> | |
| <div id="loading-indicator" class="loading" style="display: none;">Loading data...</div> | |
| <div class="chart-controls" style="text-align:center; margin-top: 20px; margin-bottom: 15px;"> | |
| <label style="margin-right:15px; font-weight:bold;">View Mode:</label> | |
| <input type="radio" id="view-overall" name="chart-view" value="overall" checked onchange="updateView()"> | |
| <label for="view-overall" style="margin-right:10px;">Overall (Mean)</label> | |
| <input type="radio" id="view-classifiers-f1" name="chart-view" value="classifiers-f1" onchange="updateView()"> | |
| <label for="view-classifiers-f1" style="margin-right:10px;">F1 by Classifier</label> | |
| <input type="radio" id="view-classifiers-auc" name="chart-view" value="classifiers-auc" onchange="updateView()"> | |
| <label for="view-classifiers-auc">AUC by Classifier</label> | |
| </div> | |
| <div class="charts-container" style="display: flex; gap: 20px; margin-bottom: 20px;"> | |
| <div style="flex: 1; background: white; padding: 15px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);"> | |
| <canvas id="scoreChart"></canvas> | |
| </div> | |
| <div style="flex: 1; background: white; padding: 15px; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);"> | |
| <canvas id="timeChart"></canvas> | |
| </div> | |
| </div> | |
| <table id="result-table"> | |
| <thead> | |
| <!-- Headers generated dynamically --> | |
| </thead> | |
| <tbody> | |
| <!-- Data rows will be populated here --> | |
| </tbody> | |
| </table> | |
| </div> | |
| <!-- Modal for details --> | |
| <div id="details-modal" class="modal"> | |
| <div class="modal-content"> | |
| <span class="close">×</span> | |
| <h2 id="modal-title">Algorithm Details</h2> | |
| <div id="modal-body"></div> | |
| </div> | |
| </div> | |
| <script> | |
| let currentResults = []; | |
| let sortDirection = 1; // 1 for asc, -1 for desc | |
| let lastSortKey = ''; | |
| const VIEW_CONFIG = { | |
| 'overall': [ | |
| { key: 'mean_f1', label: 'Mean F1' }, | |
| { key: 'mean_auc', label: 'Mean AUC' } | |
| ], | |
| 'classifiers-f1': [ | |
| { key: 'metrics.nb.f1', label: 'NB F1' }, | |
| { key: 'metrics.svm.f1', label: 'SVM F1' }, | |
| { key: 'metrics.rf.f1', label: 'RF F1' } | |
| ], | |
| 'classifiers-auc': [ | |
| { key: 'metrics.nb.auc', label: 'NB AUC' }, | |
| { key: 'metrics.svm.auc', label: 'SVM AUC' }, | |
| { key: 'metrics.rf.auc', label: 'RF AUC' } | |
| ] | |
| }; | |
| const tableHead = document.querySelector("#result-table thead"); | |
| const tableBody = document.querySelector("#result-table tbody"); | |
| const datasetSelect = document.getElementById("dataset-select"); | |
| const loadingIndicator = document.getElementById("loading-indicator"); | |
| const modal = document.getElementById("details-modal"); | |
| const closeModal = document.querySelector(".close"); | |
| // Close modal | |
| closeModal.onclick = () => modal.style.display = "none"; | |
| window.onclick = (event) => { | |
| if (event.target == modal) modal.style.display = "none"; | |
| } | |
| // Global chart instances | |
| let scoreChartInstance = null; | |
| let timeChartInstance = null; | |
| function updateCharts(results) { | |
| if (!Array.isArray(results) || results.length === 0) return; | |
| // Limit to top 15 for readability | |
| const topResults = results.slice(0, 15); | |
| const labels = topResults.map(r => r.algorithm || 'Unknown'); | |
| const times = topResults.map(r => r.time || 0); | |
| const viewMode = document.querySelector('input[name="chart-view"]:checked').value; | |
| let datasets = []; | |
| if (viewMode === 'overall') { | |
| const f1Scores = topResults.map(r => r.mean_f1 || 0); | |
| const aucScores = topResults.map(r => r.mean_auc || 0); | |
| datasets = [ | |
| { | |
| label: 'Mean F1', | |
| data: f1Scores, | |
| backgroundColor: 'rgba(52, 152, 219, 0.7)', | |
| borderColor: 'rgba(52, 152, 219, 1)', | |
| borderWidth: 1 | |
| }, | |
| { | |
| label: 'Mean AUC', | |
| data: aucScores, | |
| backgroundColor: 'rgba(46, 204, 113, 0.7)', | |
| borderColor: 'rgba(46, 204, 113, 1)', | |
| borderWidth: 1 | |
| } | |
| ]; | |
| } else if (viewMode === 'classifiers-f1') { | |
| const classifiers = ['nb', 'svm', 'rf']; | |
| const colors = ['rgba(255, 206, 86, 0.5)', 'rgba(75, 192, 192, 0.5)', 'rgba(153, 102, 255, 0.5)']; | |
| const borderColors = ['rgba(255, 206, 86, 1)', 'rgba(75, 192, 192, 1)', 'rgba(153, 102, 255, 1)']; | |
| datasets = classifiers.map((cls, idx) => ({ | |
| label: cls.toUpperCase() + ' F1', | |
| data: topResults.map(r => (r.metrics && r.metrics[cls]) ? r.metrics[cls].f1 : 0), | |
| backgroundColor: colors[idx], | |
| borderColor: borderColors[idx], | |
| borderWidth: 1 | |
| })); | |
| } else if (viewMode === 'classifiers-auc') { | |
| const classifiers = ['nb', 'svm', 'rf']; | |
| const colors = ['rgba(255, 206, 86, 0.5)', 'rgba(75, 192, 192, 0.5)', 'rgba(153, 102, 255, 0.5)']; | |
| const borderColors = ['rgba(255, 206, 86, 1)', 'rgba(75, 192, 192, 1)', 'rgba(153, 102, 255, 1)']; | |
| datasets = classifiers.map((cls, idx) => ({ | |
| label: cls.toUpperCase() + ' AUC', | |
| data: topResults.map(r => (r.metrics && r.metrics[cls]) ? r.metrics[cls].auc : 0), | |
| backgroundColor: colors[idx], | |
| borderColor: borderColors[idx], | |
| borderWidth: 1 | |
| })); | |
| } | |
| // Score Chart | |
| const scoreCtx = document.getElementById('scoreChart').getContext('2d'); | |
| if (scoreChartInstance) scoreChartInstance.destroy(); | |
| scoreChartInstance = new Chart(scoreCtx, { | |
| type: 'bar', | |
| data: { | |
| labels: labels, | |
| datasets: datasets | |
| }, | |
| options: { | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| plugins: { | |
| title: { | |
| display: true, | |
| text: viewMode === 'overall' ? 'Top Algorithms Performance (Mean)' : | |
| (viewMode === 'classifiers-f1' ? 'F1-Score by Classifier' : 'AUC by Classifier') | |
| } | |
| }, | |
| scales: { | |
| y: { | |
| beginAtZero: false, | |
| // min: 0.8 | |
| } | |
| } | |
| } | |
| }); | |
| // Time Chart | |
| const timeCtx = document.getElementById('timeChart').getContext('2d'); | |
| if (timeChartInstance) timeChartInstance.destroy(); | |
| timeChartInstance = new Chart(timeCtx, { | |
| type: 'line', | |
| data: { | |
| labels: labels, | |
| datasets: [{ | |
| label: 'Time (s)', | |
| data: times, | |
| backgroundColor: 'rgba(231, 76, 60, 0.2)', | |
| borderColor: 'rgba(231, 76, 60, 1)', | |
| borderWidth: 2, | |
| tension: 0.3, | |
| fill: true | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| plugins: { | |
| title: { display: true, text: 'Execution Time' } | |
| }, | |
| scales: { | |
| y: { beginAtZero: true } | |
| } | |
| } | |
| }); | |
| } | |
| function showDetails(result) { | |
| const title = document.getElementById("modal-title"); | |
| const body = document.getElementById("modal-body"); | |
| title.textContent = `${result.algorithm} Details`; | |
| let featuresHtml = result.selected_features.map(f => | |
| `<span class="feature-tag">${f}</span>` | |
| ).join(''); | |
| let metricsHtml = '<div style="margin-top: 15px;"><h3>Metrics Breakdown</h3>'; | |
| for (const [clf, m] of Object.entries(result.metrics || {})) { | |
| metricsHtml += ` | |
| <div style="margin-bottom: 10px;"> | |
| <strong>${clf.toUpperCase()}:</strong> | |
| F1: ${m.f1.toFixed(4)}, AUC: ${m.auc.toFixed(4)} | |
| </div>`; | |
| } | |
| metricsHtml += '</div>'; | |
| body.innerHTML = ` | |
| <p><strong>Time:</strong> ${result.time.toFixed(4)}s</p> | |
| <p><strong>Num Features:</strong> ${result.num_features}</p> | |
| <p><strong>Selected Features (${result.selected_features.length}):</strong></p> | |
| <div>${featuresHtml}</div> | |
| ${metricsHtml} | |
| `; | |
| modal.style.display = "block"; | |
| } | |
| function getValue(obj, path) { | |
| if (!path) return undefined; | |
| return path.split('.').reduce((acc, part) => (acc && acc[part] !== undefined) ? acc[part] : undefined, obj); | |
| } | |
| function safeFixed(value, digits=4) { | |
| if (value === undefined || value === null) return 'N/A'; | |
| return Number(value).toFixed(digits); | |
| } | |
| function renderTableHeader() { | |
| const viewMode = document.querySelector('input[name="chart-view"]:checked').value; | |
| const dynamicCols = VIEW_CONFIG[viewMode] || VIEW_CONFIG['overall']; | |
| let html = '<tr>'; | |
| html += '<th data-key="rank" style="width: 60px;">#</th>'; | |
| html += '<th data-key="algorithm">Algorithm <span class="arrow">↕</span></th>'; | |
| dynamicCols.forEach(col => { | |
| html += `<th data-key="${col.key}">${col.label} <span class="arrow">↕</span></th>`; | |
| }); | |
| html += '<th data-key="time">Time (s) <span class="arrow">↕</span></th>'; | |
| html += '<th data-key="selected_features">Selected Features</th>'; | |
| html += '</tr>'; | |
| tableHead.innerHTML = html; | |
| // Re-attach sort listeners | |
| tableHead.querySelectorAll('th[data-key]').forEach(th => { | |
| th.addEventListener('click', () => sortTable(th.dataset.key)); | |
| }); | |
| } | |
| function updateTable(results) { | |
| tableBody.innerHTML = ""; | |
| // Safety check | |
| if (!Array.isArray(results)) { | |
| tableBody.innerHTML = '<tr><td colspan="10" style="text-align:center; color:red;">Error: Invalid data format</td></tr>'; | |
| return; | |
| } | |
| if (results.length === 0) { | |
| tableBody.innerHTML = '<tr><td colspan="10" style="text-align:center;">No results found</td></tr>'; | |
| return; | |
| } | |
| const viewMode = document.querySelector('input[name="chart-view"]:checked').value; | |
| const dynamicCols = VIEW_CONFIG[viewMode] || VIEW_CONFIG['overall']; | |
| results.forEach((r, idx) => { | |
| const row = document.createElement("tr"); | |
| // Format features for preview | |
| const featurePreview = (r.selected_features && Array.isArray(r.selected_features)) | |
| ? r.selected_features.slice(0, 5).join(', ') + (r.selected_features.length > 5 ? '...' : '') | |
| : 'N/A'; | |
| let html = `<td>${idx + 1}</td>`; | |
| html += `<td><strong>${r.algorithm || 'Unknown'}</strong></td>`; | |
| dynamicCols.forEach(col => { | |
| const val = getValue(r, col.key); | |
| const score = val !== undefined ? val : 0; | |
| html += ` | |
| <td> | |
| ${safeFixed(val)} | |
| <div class="score-bar"><div class="score-fill" style="width: ${Math.min(score * 100, 100)}%"></div></div> | |
| </td>`; | |
| }); | |
| const time = r.time || 0; | |
| html += `<td>${safeFixed(time, 2)}</td>`; | |
| html += ` | |
| <td class="features-cell" onclick="showDetails(currentResults[${idx}])" title="Click for details"> | |
| ${featurePreview} <span style="font-size:0.8em; color:#999;">(Click for details)</span> | |
| </td>`; | |
| row.innerHTML = html; | |
| tableBody.appendChild(row); | |
| }); | |
| } | |
| function sortTable(key) { | |
| if (lastSortKey === key) { | |
| sortDirection *= -1; | |
| } else { | |
| sortDirection = key === 'time' || key === 'rank' ? 1 : -1; | |
| lastSortKey = key; | |
| } | |
| // We don't call renderTableHeader here because it resets the sort indicators if we rebuild entirely. | |
| // Instead, we just update the arrows. | |
| document.querySelectorAll('th .arrow').forEach(span => span.textContent = '↕'); | |
| const activeHeader = document.querySelector(`th[data-key="${key}"] .arrow`); | |
| if (activeHeader) activeHeader.textContent = sortDirection === 1 ? '↑' : '↓'; | |
| const sorted = [...currentResults].sort((a, b) => { | |
| let valA = getValue(a, key); | |
| let valB = getValue(b, key); | |
| if (key === 'rank') return 0; | |
| if (valA === undefined) valA = -Infinity; | |
| if (valB === undefined) valB = -Infinity; | |
| if (valA < valB) return -1 * sortDirection; | |
| if (valA > valB) return 1 * sortDirection; | |
| return 0; | |
| }); | |
| // Don't update currentResults global if it breaks things, but here it's fine. | |
| // Actually, let's keep currentResults as the master list? | |
| // No, currentResults should be the sorted list for consistent subsequent sorts. | |
| currentResults = sorted; | |
| updateTable(sorted); | |
| } | |
| function updateView() { | |
| renderTableHeader(); | |
| updateTable(currentResults); | |
| updateCharts(currentResults); | |
| } | |
| function fetchResults(dataset) { | |
| loadingIndicator.style.display = 'block'; | |
| tableBody.innerHTML = ''; | |
| console.log("Fetching results for:", dataset); | |
| fetch(`/api/results?dataset=${dataset}`) | |
| .then(res => { | |
| if (!res.ok) throw new Error("Network response was not ok"); | |
| return res.json(); | |
| }) | |
| .then(data => { | |
| console.log("Data received:", data); | |
| currentResults = data; | |
| updateView(); | |
| loadingIndicator.style.display = 'none'; | |
| }) | |
| .catch(err => { | |
| console.error("Error fetching results:", err); | |
| loadingIndicator.textContent = "Error loading data. Make sure the server is running."; | |
| }); | |
| } | |
| // Initialize | |
| document.addEventListener("DOMContentLoaded", () => { | |
| // Setup sort listeners | |
| document.querySelectorAll('th[data-key]').forEach(th => { | |
| th.addEventListener('click', () => sortTable(th.dataset.key)); | |
| }); | |
| // Load datasets | |
| fetch("/api/datasets") | |
| .then(res => res.json()) | |
| .then(datasets => { | |
| datasetSelect.innerHTML = ""; | |
| datasets.forEach(ds => { | |
| const option = document.createElement("option"); | |
| option.value = ds; | |
| option.textContent = ds; | |
| datasetSelect.appendChild(option); | |
| }); | |
| if (datasets.includes("Authorship")) { | |
| datasetSelect.value = "Authorship"; | |
| fetchResults("Authorship"); | |
| } else if (datasets.length > 0) { | |
| datasetSelect.value = datasets[0]; | |
| fetchResults(datasets[0]); | |
| } | |
| }) | |
| .catch(err => { | |
| console.error("Error fetching datasets:", err); | |
| datasetSelect.innerHTML = "<option>Error loading datasets</option>"; | |
| }); | |
| datasetSelect.addEventListener('change', (e) => { | |
| fetchResults(e.target.value); | |
| }); | |
| }); | |
| </script> | |
| </body> | |
| </html> | |