Перейти к основному содержимому
clawam tools
Инструменты Документы Daoark EN Главная Главная / Преобразование данных › Главная manifest.json 0; }); var groups = []; var current = null; for (var i = 0; i < lines.length; i++) { var parts = lines[i].split(/[\s,;\t]+/).filter(function(x) { return x.length > 0; }); if (parts.length === 0) continue; var label = parts[0]; var nums = []; for (var j = 1; j < parts.length; j++) { var v = parseFloat(parts[j]); if (!isNaN(v)) nums.push(v); } if (nums.length > 0) { groups.push({ label: label, values: nums }); } else if (parts.length === 1) { // 单值,加入上一组 if (groups.length > 0) groups[groups.length - 1].values.push(parseFloat(label)); } } return groups; } function quantile(sorted, q) { var n = sorted.length; if (n === 0) return 0; var pos = (n - 1) * q; var base = Math.floor(pos); var rest = pos - base; if (sorted[base + 1] !== undefined) return sorted[base] + rest * (sorted[base + 1] - sorted[base]); return sorted[base]; } function boxStats(values) { var sorted = values.slice().sort(function(a, b) { return a - b; }); var q1 = quantile(sorted, 0.25); var median = quantile(sorted, 0.5); var q3 = quantile(sorted, 0.75); var iqr = q3 - q1; var lower = q1 - 1.5 * iqr; var upper = q3 + 1.5 * iqr; var inliers = sorted.filter(function(v) { return v >= lower && v <= upper; }); var min = inliers.length > 0 ? inliers[0] : sorted[0]; var max = inliers.length > 0 ? inliers[inliers.length - 1] : sorted[sorted.length - 1]; var outliers = sorted.filter(function(v) { return v < lower || v > upper; }); return { q1: q1, median: median, q3: q3, min: min, max: max, outliers: outliers }; } function escape(s) { return String(s).replace(/[&<>"']/g, function(c) { return {'&':'&','<':'<','>':'>','"':'"',"'":'''}[c]; }); } function renderBoxPlot(groups, title, ylabel) { if (groups.length === 0) return ''; var W = 720, H = 420; var M = { top: 50, right: 40, bottom: 60, left: 70 }; var innerW = W - M.left - M.right; var innerH = H - M.top - M.bottom; var allValues = []; for (var i = 0; i < groups.length; i++) allValues = allValues.concat(groups[i].values); var yMin = Math.min.apply(null, allValues); var yMax = Math.max.apply(null, allValues); if (yMin === yMax) { yMin -= 1; yMax += 1; } function sy(y) { return M.top + innerH - (y - yMin) / (yMax - yMin) * innerH; } var svg = ''; if (title) svg += '' + escape(title) + ''; // Y 刻度 for (var i = 0; i <= 5; i++) { var v = yMin + (yMax - yMin) * (5 - i) / 5; var y = M.top + innerH * i / 5; svg += ''; svg += '' + v.toFixed(1) + ''; } svg += ''; // 每个箱线图 var boxW = innerW / groups.length * 0.5; for (var g = 0; g < groups.length; g++) { var stats = boxStats(groups[g].values); var cx = M.left + (g + 0.5) * (innerW / groups.length); // 须线 svg += ''; svg += ''; // 上下帽 svg += ''; svg += ''; // 箱体 svg += ''; // 中位数 svg += ''; // 离群点 for (var o = 0; o < stats.outliers.length; o++) { svg += ''; } // 标签 svg += '' + escape(groups[g].label) + ''; svg += '中位 ' + stats.median.toFixed(1) + ' (n=' + groups[g].values.length + ')'; } if (ylabel) svg += '' + escape(ylabel) + ''; svg += ''; return svg; } var currentSVG = ''; function doProcess() { try { var textA = getVal('text-a'); if (!textA) { showError('请输入'); return; } var title = getVal('title'); var ylabel = getVal('ylabel'); var groups = parseGroups(textA); if (groups.length === 0) { showError('无有效数据'); return; } var svg = renderBoxPlot(groups, title, ylabel); currentSVG = svg; showResult(svg, '组数: ' + groups.length + ', 数据点总数: ' + groups.reduce(function(s,g){return s+g.values.length;}, 0)); } catch (e) { showError('处理失败: ' + (e.message || e)); } } $('btn-main').addEventListener('click', function(e) { e.preventDefault(); doProcess(); }); $('btn-clear').addEventListener('click', function() { var inputs = document.querySelectorAll('input,textarea,select'); for (var i = 0; i < inputs.length; i++) { if (inputs[i].tagName === 'SELECT') inputs[i].selectedIndex = 0; else inputs[i].value = inputs[i].defaultValue; } var ra = $('result-area'); ra.hidden = true; ra.style.display = 'none'; var em = $('error-msg'); em.hidden = true; em.style.display = 'none'; $('btn-download').style.display = 'none'; }); $('btn-download').addEventListener('click', function() { if (!currentSVG) return; var blob = new Blob([currentSVG], { type: 'image/svg+xml' }); var url = URL.createObjectURL(blob); var a = document.createElement('a'); a.href = url; a.download = 'box-plot.svg'; a.click(); URL.revokeObjectURL(url); }); })();