markdown-magic-maker / d3-ml-metrics.html
tfrere's picture
tfrere HF Staff
fais moi un chart cool sur le machine learning en lisant les direxctives dans le readme
d8f167c verified
<div class="d3-ml-metrics"></div>
<style>
.d3-ml-metrics {
font-family: -apple-system, BlinkMacSystemFont, sans-serif;
position: relative;
}
.d3-ml-metrics .chart-container {
width: 100%;
overflow: visible;
}
.d3-ml-metrics .axis path,
.d3-ml-metrics .axis line {
stroke: var(--axis-color);
shape-rendering: crispEdges;
}
.d3-ml-metrics .axis text {
fill: var(--tick-color);
font-size: 11px;
}
.d3-ml-metrics .grid line {
stroke: var(--grid-color);
stroke-dasharray: 2,2;
shape-rendering: crispEdges;
}
.d3-ml-metrics .line {
fill: none;
stroke-width: 2px;
stroke-linejoin: round;
stroke-linecap: round;
}
.d3-ml-metrics .legend {
display: flex;
flex-direction: column;
gap: 8px;
margin-top: 16px;
}
.d3-ml-metrics .legend-title {
font-size: 12px;
font-weight: 700;
color: var(--text-color);
}
.d3-ml-metrics .legend-items {
display: flex;
flex-wrap: wrap;
gap: 12px;
}
.d3-ml-metrics .legend-item {
display: flex;
align-items: center;
gap: 6px;
cursor: pointer;
}
.d3-ml-metrics .legend-swatch {
width: 14px;
height: 14px;
border-radius: 3px;
border: 1px solid var(--border-color);
}
.d3-ml-metrics .controls {
display: flex;
justify-content: flex-end;
margin-bottom: 16px;
}
.d3-ml-metrics .control-group {
display: flex;
flex-direction: column;
gap: 6px;
}
.d3-ml-metrics .control-group label {
font-size: 12px;
font-weight: 700;
color: var(--text-color);
}
.d3-ml-metrics .control-group select {
font-size: 12px;
padding: 6px 24px 6px 8px;
border: 1px solid var(--border-color);
border-radius: 6px;
background: var(--surface-bg);
color: var(--text-color);
appearance: none;
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e");
background-repeat: no-repeat;
background-position: right 8px center;
background-size: 14px;
}
.d3-ml-metrics .d3-tooltip {
position: absolute;
padding: 8px 12px;
background: var(--surface-bg);
border: 1px solid var(--border-color);
border-radius: 6px;
pointer-events: none;
font-size: 12px;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
opacity: 0;
transition: opacity 0.2s;
}
.d3-ml-metrics .d3-tooltip strong {
display: block;
margin-bottom: 4px;
}
</style>
<script>
(() => {
const ensureD3 = (cb) => {
if (window.d3 && typeof window.d3.select === 'function') return cb();
let s = document.getElementById('d3-cdn-script');
if (!s) { s = document.createElement('script'); s.id = 'd3-cdn-script'; s.src = 'https://cdn.jsdelivr.net/npm/d3@7/dist/d3.min.js'; document.head.appendChild(s); }
const onReady = () => { if (window.d3 && typeof window.d3.select === 'function') cb(); };
s.addEventListener('load', onReady, { once: true });
if (window.d3) onReady();
};
const bootstrap = () => {
const scriptEl = document.currentScript;
let container = scriptEl ? scriptEl.previousElementSibling : null;
if (!(container && container.classList && container.classList.contains('d3-ml-metrics'))) {
const candidates = Array.from(document.querySelectorAll('.d3-ml-metrics'))
.filter((el) => !(el.dataset && el.dataset.mounted === 'true'));
container = candidates[candidates.length - 1] || null;
}
if (!container) return;
if (container.dataset) {
if (container.dataset.mounted === 'true') return;
container.dataset.mounted = 'true';
}
// Create UI elements
const controls = document.createElement('div');
controls.className = 'controls';
const controlGroup = document.createElement('div');
controlGroup.className = 'control-group';
const label = document.createElement('label');
label.htmlFor = 'metric-select';
label.textContent = 'Metric';
const select = document.createElement('select');
select.id = 'metric-select';
controlGroup.appendChild(label);
controlGroup.appendChild(select);
controls.appendChild(controlGroup);
container.appendChild(controls);
// Create chart elements
const svg = d3.select(container).append('svg')
.attr('class', 'chart-container')
.attr('width', '100%')
.style('display', 'block');
const g = svg.append('g')
.attr('class', 'chart-content');
// Create tooltip
const tooltip = document.createElement('div');
tooltip.className = 'd3-tooltip';
container.appendChild(tooltip);
// Sample ML metrics data
const metricsData = [
{ epoch: 1, accuracy: 0.65, loss: 1.2, val_accuracy: 0.62, val_loss: 1.3 },
{ epoch: 2, accuracy: 0.72, loss: 0.9, val_accuracy: 0.68, val_loss: 1.1 },
{ epoch: 3, accuracy: 0.78, loss: 0.7, val_accuracy: 0.74, val_loss: 0.95 },
{ epoch: 4, accuracy: 0.82, loss: 0.6, val_accuracy: 0.79, val_loss: 0.8 },
{ epoch: 5, accuracy: 0.85, loss: 0.5, val_accuracy: 0.82, val_loss: 0.7 },
{ epoch: 6, accuracy: 0.88, loss: 0.45, val_accuracy: 0.84, val_loss: 0.65 },
{ epoch: 7, accuracy: 0.89, loss: 0.4, val_accuracy: 0.86, val_loss: 0.6 },
{ epoch: 8, accuracy: 0.91, loss: 0.35, val_accuracy: 0.87, val_loss: 0.55 },
{ epoch: 9, accuracy: 0.92, loss: 0.3, val_accuracy: 0.88, val_loss: 0.5 },
{ epoch: 10, accuracy: 0.93, loss: 0.25, val_accuracy: 0.89, val_loss: 0.45 }
];
// Available metrics
const metrics = [
{ id: 'accuracy', name: 'Accuracy', color: '#4e79a7' },
{ id: 'loss', name: 'Loss', color: '#e15759' },
{ id: 'val_accuracy', name: 'Val Accuracy', color: '#76b7b2' },
{ id: 'val_loss', name: 'Val Loss', color: '#f28e2b' }
];
// Populate select
metrics.forEach(metric => {
const option = document.createElement('option');
option.value = metric.id;
option.textContent = metric.name;
select.appendChild(option);
});
// Initial selection
select.value = 'accuracy';
// Create legend
const legend = document.createElement('div');
legend.className = 'legend';
const legendTitle = document.createElement('div');
legendTitle.className = 'legend-title';
legendTitle.textContent = 'Legend';
const legendItems = document.createElement('div');
legendItems.className = 'legend-items';
metrics.forEach(metric => {
const item = document.createElement('div');
item.className = 'legend-item';
item.dataset.metric = metric.id;
item.addEventListener('click', () => {
select.value = metric.id;
render();
});
const swatch = document.createElement('div');
swatch.className = 'legend-swatch';
swatch.style.backgroundColor = metric.color;
const name = document.createElement('span');
name.textContent = metric.name;
item.appendChild(swatch);
item.appendChild(name);
legendItems.appendChild(item);
});
legend.appendChild(legendTitle);
legend.appendChild(legendItems);
container.appendChild(legend);
// Chart dimensions and margins
let width, height;
const margin = { top: 20, right: 30, bottom: 40, left: 50 };
// Scales
const x = d3.scaleLinear();
const y = d3.scaleLinear();
// Line generator
const line = d3.line()
.x(d => x(d.epoch))
.y(d => y(d[select.value]));
// Update dimensions
function updateDimensions() {
width = container.clientWidth;
height = Math.max(300, width * 0.5);
svg.attr('height', height);
x.range([margin.left, width - margin.right])
.domain([1, d3.max(metricsData, d => d.epoch)]);
const selectedMetric = metrics.find(m => m.id === select.value);
y.range([height - margin.bottom, margin.top])
.domain([0, d3.max(metricsData, d => d[select.value]) * 1.1]);
}
// Render chart
function render() {
updateDimensions();
const selectedMetric = metrics.find(m => m.id === select.value);
// Clear previous elements
g.selectAll('*').remove();
// Add grid
g.append('g')
.attr('class', 'grid')
.attr('transform', `translate(0,${height - margin.bottom})`)
.call(d3.axisBottom(x)
.tickSize(-(height - margin.top - margin.bottom))
.tickFormat('')
);
g.append('g')
.attr('class', 'grid')
.attr('transform', `translate(${margin.left},0)`)
.call(d3.axisLeft(y)
.tickSize(-(width - margin.left - margin.right))
.tickFormat('')
);
// Add axes
g.append('g')
.attr('class', 'axis x-axis')
.attr('transform', `translate(0,${height - margin.bottom})`)
.call(d3.axisBottom(x));
g.append('g')
.attr('class', 'axis y-axis')
.attr('transform', `translate(${margin.left},0)`)
.call(d3.axisLeft(y));
// Add line
g.append('path')
.datum(metricsData)
.attr('class', 'line')
.attr('stroke', selectedMetric.color)
.attr('d', line);
// Add points
g.selectAll('.point')
.data(metricsData)
.enter()
.append('circle')
.attr('class', 'point')
.attr('cx', d => x(d.epoch))
.attr('cy', d => y(d[select.value]))
.attr('r', 4)
.attr('fill', selectedMetric.color)
.on('mouseover', (event, d) => {
tooltip.innerHTML = `
<strong>Epoch ${d.epoch}</strong>
${selectedMetric.name}: ${d[select.value].toFixed(3)}
`;
tooltip.style.left = `${event.pageX + 10}px`;
tooltip.style.top = `${event.pageY - 10}px`;
tooltip.style.opacity = 1;
})
.on('mouseout', () => {
tooltip.style.opacity = 0;
});
}
// Handle select change
select.addEventListener('change', render);
// Initial render
render();
// Handle resize
const ro = window.ResizeObserver ? new ResizeObserver(render) : null;
if (ro) ro.observe(container);
else window.addEventListener('resize', render);
};
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => ensureD3(bootstrap), { once: true });
} else {
ensureD3(bootstrap);
}
})();
</script>