電阻-溫度數據擬合工具(最小二乘法)
代碼(html)
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>電阻-溫度數據擬合工具</title> <script src="https://cdn.bootcdn.net/ajax/libs/echarts/5.4.3/echarts.min.js"></script> <style> body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; max-width: 1000px; margin: 0 auto; padding: 20px; background-color: #f5f7f9; color: #333; } h1 { color: #2c3e50; text-align: center; margin-bottom: 30px; } .container { display: flex; flex-wrap: wrap; gap: 20px; margin-bottom: 30px; } .data-section { flex: 1; min-width: 300px; background: white; border-radius: 8px; padding: 20px; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); } .result-section { flex: 2; min-width: 300px; background: white; border-radius: 8px; padding: 20px; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); } table { width: 100%; border-collapse: collapse; margin-bottom: 15px; } th, td { border: 1px solid #ddd; padding: 10px; text-align: center; } th { background-color: #f2f2f2; } th:first-child, th:nth-child(2) { width: 35%; } th:last-child { width: 30%; } #data-table td.action-cell { display: flex; justify-content: center; align-items: center; } input { width: 100%; box-sizing: border-box; padding: 8px; border: 1px solid #ddd; border-radius: 4px; } .btn { padding: 10px 15px; margin: 5px; border: none; border-radius: 4px; cursor: pointer; font-weight: bold; } .btn-primary { background-color: #3498db; color: white; } .btn-danger { background-color: #e74c3c; color: white; } .btn-success { background-color: #2ecc71; color: white; } .buttons { display: flex; justify-content: center; gap: 10px; margin-top: 10px; } .equation { font-size: 18px; margin: 15px 0; padding: 15px; background-color: #f9f9f9; border-left: 4px solid #3498db; border-radius: 4px; } .r-squared { font-size: 16px; margin: 15px 0; padding: 15px; background-color: #f9f9f9; border-left: 4px solid #2ecc71; border-radius: 4px; } .kb-values { font-size: 16px; margin: 10px 0; padding: 10px; background-color: #fff7f0; border-left: 4px solid #6c5ce7; border-radius: 4px; } .chart-container { margin-top: 20px; position: relative; height: 300px; width: 100%; } .instructions { background-color: #f8f9fa; padding: 15px; border-radius: 8px; margin-bottom: 20px; border-left: 4px solid #ffc107; } </style> </head> <body> <h1>電阻-溫度數據擬合工具</h1> <div class="container"> <div class="data-section"> <h2>數據輸入</h2> <table id="data-table"> <thead> <tr> <th>電阻值 (Ω)</th> <th>溫度值 (°C)</th> <th>操作</th> </tr> </thead> <tbody> <tr> <td><input type="number" class="resistance" step="0.1" value="100"></td> <td><input type="number" class="temperature" step="0.1" value="20"></td> <td class="action-cell"><button class="btn btn-danger" onclick="deleteRow(this)">刪除</button></td> </tr> <tr> <td><input type="number" class="resistance" step="0.1" value="120"></td> <td><input type="number" class="temperature" step="0.1" value="25"></td> <td class="action-cell"><button class="btn btn-danger" onclick="deleteRow(this)">刪除</button></td> </tr> <tr> <td><input type="number" class="resistance" step="0.1" value="140"></td> <td><input type="number" class="temperature" step="0.1" value="30"></td> <td class="action-cell"><button class="btn btn-danger" onclick="deleteRow(this)">刪除</button></td> </tr> </tbody> </table> <div class="buttons"> <button class="btn btn-primary" onclick="addRow()">添加行</button> <button class="btn btn-success" onclick="calculateFit()">計算擬合</button> </div> </div> <div class="result-section"> <h2>擬合結果</h2> <div class="equation" id="equation-result">擬合方程將顯示在這里</div> <div class="kb-values" id="kb-values">k = - , b = -</div> <div class="r-squared" id="r-squared-result">決定系數將顯示在這里</div> <div class="chart-container" id="fit-chart"></div> </div> </div> <script> // 全局變量存儲圖表實例 let fitChart = null; // 添加新行 function addRow() { const tbody = document.querySelector('#data-table tbody'); const newRow = document.createElement('tr'); newRow.innerHTML = ` <td><input type="number" class="resistance" step="0.1" value="0"></td> <td><input type="number" class="temperature" step="0.1" value="0"></td> <td class="action-cell"><button class="btn btn-danger" onclick="deleteRow(this)">刪除</button></td> `; tbody.appendChild(newRow); } // 刪除行 function deleteRow(button) { const tbody = document.querySelector('#data-table tbody'); const row = button.parentNode.parentNode; if (tbody.rows.length > 1) { // 確保至少保留一行 tbody.removeChild(row); } else { alert("至少需要保留一行數據"); } } // 獲取數據 function getData() { const rows = document.querySelectorAll('#data-table tbody tr'); const data = []; rows.forEach(row => { const resistance = parseFloat(row.querySelector('.resistance').value); const temperature = parseFloat(row.querySelector('.temperature').value); if (!isNaN(resistance) && !isNaN(temperature)) { data.push([resistance, temperature]); } }); return data; } // 最小二乘法擬合 function linearRegression(data) { let sumX = 0, sumY = 0, sumXY = 0, sumXX = 0; const n = data.length; data.forEach(point => { sumX += point[0]; sumY += point[1]; sumXY += point[0] * point[1]; sumXX += point[0] * point[0]; }); const slope = (n * sumXY - sumX * sumY) / (n * sumXX - sumX * sumX); const intercept = (sumY - slope * sumX) / n; return { slope, intercept }; } // 計算決定系數R2 function calculateRSquared(data, slope, intercept) { // 計算y的平均值 const yMean = data.reduce((sum, point) => sum + point[1], 0) / data.length; // 計算總平方和 const totalSumOfSquares = data.reduce((sum, point) => { return sum + Math.pow(point[1] - yMean, 2); }, 0); // 計算殘差平方和 const residualSumOfSquares = data.reduce((sum, point) => { const predictedY = slope * point[0] + intercept; return sum + Math.pow(point[1] - predictedY, 2); }, 0); // 計算R2 const rSquared = 1 - (residualSumOfSquares / totalSumOfSquares); return rSquared; } // 計算擬合 function calculateFit() { const data = getData(); if (data.length < 2) { alert("至少需要兩個有效數據點才能進行擬合"); return; } // 執行線性回歸 const { slope, intercept } = linearRegression(data); // 計算決定系數 const rSquared = calculateRSquared(data, slope, intercept); // 顯示結果:k、b單獨一行 document.getElementById('equation-result').textContent = `擬合方程: y = ${slope.toFixed(6)} x + ${intercept.toFixed(6)}`; document.getElementById('kb-values').textContent = `k = ${slope.toFixed(6)} , b = ${intercept.toFixed(6)}`; document.getElementById('r-squared-result').textContent = `決定系數 R2 = ${rSquared.toFixed(6)}`; // 繪制圖表 drawChart(data, slope, intercept); } // 繪制圖表 function drawChart(data, slope, intercept) { const chartDom = document.getElementById('fit-chart'); // 如果已有圖表實例,先銷毀 if (fitChart) { fitChart.dispose(); } // 創建新圖表 fitChart = echarts.init(chartDom); // 對x值進行排序,以便繪制平滑的擬合線 const sortedData = [...data].sort((a, b) => a[0] - b[0]); const minX = Math.min(...sortedData.map(point => point[0])); const maxX = Math.max(...sortedData.map(point => point[0])); // 生成擬合線上的點 const fitLine = [ [minX, slope * minX + intercept], [maxX, slope * maxX + intercept] ]; // 計算用于自動設置坐標軸范圍的值(包含數據點與擬合線) const allX = sortedData.map(p => p[0]).concat(fitLine.map(p => p[0])); const allY = sortedData.map(p => p[1]).concat(fitLine.map(p => p[1])); const rawMinX = Math.min(...allX); const rawMaxX = Math.max(...allX); const rawMinY = Math.min(...allY); const rawMaxY = Math.max(...allY); // 添加小的邊距(當范圍為0時提供默認邊距) const xRange = rawMaxX - rawMinX; const yRange = rawMaxY - rawMinY; const xPad = xRange === 0 ? Math.abs(rawMinX) * 0.1 + 1 : xRange * 0.06; const yPad = yRange === 0 ? Math.abs(rawMinY) * 0.1 + 1 : yRange * 0.06; const xMin = rawMinX - xPad; const xMax = rawMaxX + xPad; const yMin = rawMinY - yPad; const yMax = rawMaxY + yPad; // 配置選項 const option = { title: { text: '電阻-溫度關系擬合', left: 'center' }, tooltip: { trigger: 'axis', axisPointer: { type: 'cross' }, formatter: function (params) { // params 是一個數組,里面包含兩個 series 的數據(scatter 和 line) const parts = params.map(p => { if (p.seriesType === 'scatter') { return `${p.marker} ${p.seriesName}: 電阻 ${p.data[0]} Ω, 溫度 ${p.data[1]} °C`; } else if (p.seriesType === 'line') { return `${p.marker} ${p.seriesName}: 擬合值 ${p.data[1].toFixed(2)} °C`; } else { return ''; } }); return parts.join('<br/>'); } }, legend: { data: ['原始數據', '擬合直線'], top: 30 }, xAxis: { type: 'value', name: '電阻值 (Ω)', nameLocation: 'middle', nameGap: 30, min: xMin, max: xMax }, yAxis: { type: 'value', name: '溫度值 (°C)', nameLocation: 'middle', nameGap: 30, min: yMin, max: yMax }, series: [ { name: '原始數據', type: 'scatter', data: sortedData, symbolSize: 8 }, { name: '擬合直線', type: 'line', data: fitLine, lineStyle: { width: 2 }, symbol: 'none' } ] }; // 使用配置項和數據顯示圖表 fitChart.setOption(option); } // 頁面加載時初始化圖表 window.onload = function() { calculateFit(); }; // 窗口大小變化時調整圖表大小 window.addEventListener('resize', function() { if (fitChart) { fitChart.resize(); } }); </script> </body> </html>


浙公網安備 33010602011771號