{"id":121,"date":"2013-01-26T05:08:51","date_gmt":"2013-01-26T05:08:51","guid":{"rendered":"http:\/\/tedcoe.com\/math\/?page_id=121"},"modified":"2024-07-24T15:48:04","modified_gmt":"2024-07-24T22:48:04","slug":"line-of-best-fit","status":"publish","type":"page","link":"https:\/\/www.tedcoe.com\/math\/algebra\/line-of-best-fit\/","title":{"rendered":"Line of Best Fit"},"content":{"rendered":"<p><iframe loading=\"lazy\" style=\"border: 0px;\" src=\"https:\/\/www.geogebratube.org\/material\/iframe\/id\/131588\/width\/923\/height\/344\/border\/888888\/rc\/false\/ai\/false\/sdz\/true\/smb\/false\/stb\/false\/stbh\/true\/ld\/false\/sri\/true\/at\/preferhtml5\" width=\"923px\" height=\"344px\" scrolling=\"no\"> <\/iframe><\/p>\n<p>From Claude Sonnet 3.5:<\/p>\n\n\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>Find the Line of Best Fit<\/title>\n    <script src=\"https:\/\/cdnjs.cloudflare.com\/ajax\/libs\/d3\/7.8.5\/d3.min.js\"><\/script>\n    <style>\n        #graph {\n            width: 600px;\n            height: 600px;\n            border: 1px solid #ccc;\n        }\n        .point {\n            cursor: move;\n        }\n        #toggleButton, #presetSelector {\n            margin-top: 10px;\n        }\n        #errorSum, #regressionErrorSum {\n            margin-top: 10px;\n            font-weight: bold;\n        }\n    <\/style>\n<\/head>\n<body>\n    <h1>Find the Line of Best Fit<\/h1>\n    <select id=\"presetSelector\">\n        <option value=\"0\">Preset 1<\/option>\n        <option value=\"1\">Preset 2<\/option>\n        <option value=\"2\">Preset 3<\/option>\n        <option value=\"3\">Preset 4<\/option>\n        <option value=\"4\">Preset 5<\/option>\n    <\/select>\n    <div id=\"graph\"><\/div>\n    <button id=\"toggleButton\">Show\/hide line of best fit<\/button>\n    <div id=\"errorSum\">Sum of Squared Errors (Purple Line): 0<\/div>\n    <div id=\"regressionErrorSum\">Sum of Squared Errors (Regression Line): 0<\/div>\n\n    <script>\n        const width = 600;\n        const height = 600;\n        const margin = { top: 30, right: 30, bottom: 45, left: 60 };\n        const graphWidth = width - margin.left - margin.right;\n        const graphHeight = height - margin.top - margin.bottom;\n\n        const svg = d3.select(\"#graph\")\n            .append(\"svg\")\n            .attr(\"width\", width)\n            .attr(\"height\", height)\n            .append(\"g\")\n            .attr(\"transform\", `translate(${margin.left},${margin.top})`);\n\n        const xScale = d3.scaleLinear().domain([0, 20]).range([0, graphWidth]);\n        const yScale = d3.scaleLinear().domain([0, 20]).range([graphHeight, 0]);\n\n        svg.append(\"g\")\n            .attr(\"transform\", `translate(0,${graphHeight})`)\n            .call(d3.axisBottom(xScale));\n\n        svg.append(\"g\")\n            .call(d3.axisLeft(yScale));\n\n        const presets = [\n            [\n                { x: 2, y: 15 },\n                { x: 7, y: 6 },\n                { x: 11, y: 12 },\n                { x: 18, y: 9 }\n            ],\n            [\n                { x: 1, y: 3 },\n                { x: 8, y: 17 },\n                { x: 13, y: 5 },\n                { x: 19, y: 14 }\n            ],\n            [\n                { x: 4, y: 18 },\n                { x: 9, y: 7 },\n                { x: 14, y: 16 },\n                { x: 17, y: 2 }\n            ],\n            [\n                { x: 3, y: 11 },\n                { x: 6, y: 4 },\n                { x: 12, y: 19 },\n                { x: 16, y: 8 }\n            ],\n            [\n                { x: 5, y: 1 },\n                { x: 10, y: 13 },\n                { x: 15, y: 6 },\n                { x: 20, y: 18 }\n            ]\n        ];\n\n        let points = [...presets[0]];\n\n        let additionalPoints = [\n            { x: 4, y: 8 },\n            { x: 16, y: 12 }\n        ];\n\n        let bestFitLine = { slope: 0, intercept: 0 };\n        let lineA = { slope: 0, intercept: 0 };\n        let showBestFit = false;\n\n        const drag = d3.drag()\n            .on(\"drag\", dragged);\n\n        function dragged(event, d) {\n            const [x, y] = d3.pointer(event, svg.node());\n            d.x = Math.max(0, Math.min(20, xScale.invert(x)));\n            d.y = Math.max(0, Math.min(20, yScale.invert(y)));\n            updateGraph();\n        }\n\n        function updateGraph() {\n            updateBestFitLine();\n            updateLineA();\n            updateSquaredErrorSquares();\n            updateErrorLines();\n            updatePoints();\n            updateAdditionalPoints();\n            updateLineAErrorsAndSquares();\n            updateErrorSums();\n        }\n\n        function updatePoints() {\n            svg.selectAll(\".point\")\n                .data(points)\n                .join(\"circle\")\n                .attr(\"class\", \"point\")\n                .attr(\"cx\", d => xScale(d.x))\n                .attr(\"cy\", d => yScale(d.y))\n                .attr(\"r\", 5)\n                .attr(\"fill\", \"blue\")\n                .call(drag);\n        }\n\n        function updateAdditionalPoints() {\n            svg.selectAll(\".additional-point\")\n                .data(additionalPoints)\n                .join(\"circle\")\n                .attr(\"class\", \"additional-point point\")\n                .attr(\"cx\", d => xScale(d.x))\n                .attr(\"cy\", d => yScale(d.y))\n                .attr(\"r\", 5)\n                .attr(\"fill\", \"purple\")\n                .call(drag)\n                .raise();\n        }\n\n        function updateBestFitLine() {\n            const xMean = d3.mean(points, d => d.x);\n            const yMean = d3.mean(points, d => d.y);\n            const ssXX = d3.sum(points, d => Math.pow(d.x - xMean, 2));\n            const ssXY = d3.sum(points, d => (d.x - xMean) * (d.y - yMean));\n            bestFitLine.slope = ssXY \/ ssXX;\n            bestFitLine.intercept = yMean - bestFitLine.slope * xMean;\n\n            const lineData = [\n                { x: 0, y: bestFitLine.intercept },\n                { x: 20, y: 20 * bestFitLine.slope + bestFitLine.intercept }\n            ];\n\n            const lineGenerator = d3.line()\n                .x(d => xScale(d.x))\n                .y(d => yScale(d.y));\n\n            svg.selectAll(\".best-fit-line\")\n                .data([lineData])\n                .join(\"path\")\n                .attr(\"class\", \"best-fit-line\")\n                .attr(\"d\", lineGenerator)\n                .attr(\"stroke\", \"red\")\n                .attr(\"stroke-width\", 2)\n                .attr(\"fill\", \"none\")\n                .style(\"display\", showBestFit ? null : \"none\");\n        }\n\n        function updateLineA() {\n            const [p1, p2] = additionalPoints;\n            lineA.slope = (p2.y - p1.y) \/ (p2.x - p1.x);\n            lineA.intercept = p1.y - lineA.slope * p1.x;\n\n            const lineData = [\n                { x: 0, y: lineA.intercept },\n                { x: 20, y: 20 * lineA.slope + lineA.intercept }\n            ];\n\n            const lineGenerator = d3.line()\n                .x(d => xScale(d.x))\n                .y(d => yScale(d.y));\n\n            svg.selectAll(\".line-a\")\n                .data([lineData])\n                .join(\"path\")\n                .attr(\"class\", \"line-a\")\n                .attr(\"d\", lineGenerator)\n                .attr(\"stroke\", \"purple\")\n                .attr(\"stroke-width\", 2)\n                .attr(\"fill\", \"none\");\n        }\n\n        function updateErrorLines() {\n            const errorLines = points.map(point => {\n                const yPredicted = bestFitLine.slope * point.x + bestFitLine.intercept;\n                return [\n                    { x: point.x, y: point.y },\n                    { x: point.x, y: yPredicted }\n                ];\n            });\n\n            const errorLineGenerator = d3.line()\n                .x(d => xScale(d.x))\n                .y(d => yScale(d.y));\n\n            svg.selectAll(\".error-line\")\n                .data(errorLines)\n                .join(\"path\")\n                .attr(\"class\", \"error-line\")\n                .attr(\"d\", errorLineGenerator)\n                .attr(\"stroke\", \"green\")\n                .attr(\"stroke-width\", 2)\n                .attr(\"stroke-dasharray\", \"5,5\")\n                .attr(\"fill\", \"none\")\n                .style(\"display\", showBestFit ? null : \"none\");\n        }\n\n        function updateSquaredErrorSquares() {\n            const squaredErrors = points.map(point => {\n                const yPredicted = bestFitLine.slope * point.x + bestFitLine.intercept;\n                const error = point.y - yPredicted;\n                return {\n                    x: point.x,\n                    y: point.y,\n                    yPredicted: yPredicted,\n                    size: Math.abs(error),\n                    error: error\n                };\n            });\n\n            svg.selectAll(\".squared-error\")\n                .data(squaredErrors)\n                .join(\"rect\")\n                .attr(\"class\", \"squared-error\")\n                .attr(\"x\", d => xScale(d.x))\n                .attr(\"y\", d => Math.min(yScale(d.y), yScale(d.yPredicted)))\n                .attr(\"width\", d => Math.abs(yScale(0) - yScale(d.size)))\n                .attr(\"height\", d => Math.abs(yScale(d.y) - yScale(d.yPredicted)))\n                .attr(\"fill\", \"orange\")\n                .attr(\"fill-opacity\", 0.3)\n                .attr(\"stroke\", \"orange\")\n                .attr(\"stroke-width\", 1)\n                .style(\"display\", showBestFit ? null : \"none\");\n        }\n\n        function updateLineAErrorsAndSquares() {\n            const errorLines = points.map(point => {\n                const yPredicted = lineA.slope * point.x + lineA.intercept;\n                return [\n                    { x: point.x, y: point.y },\n                    { x: point.x, y: yPredicted }\n                ];\n            });\n\n            const errorLineGenerator = d3.line()\n                .x(d => xScale(d.x))\n                .y(d => yScale(d.y));\n\n            svg.selectAll(\".error-line-a\")\n                .data(errorLines)\n                .join(\"path\")\n                .attr(\"class\", \"error-line-a\")\n                .attr(\"d\", errorLineGenerator)\n                .attr(\"stroke\", \"purple\")\n                .attr(\"stroke-width\", 2)\n                .attr(\"stroke-dasharray\", \"5,5\")\n                .attr(\"fill\", \"none\");\n\n            const squaredErrors = points.map(point => {\n                const yPredicted = lineA.slope * point.x + lineA.intercept;\n                const error = point.y - yPredicted;\n                return {\n                    x: point.x,\n                    y: point.y,\n                    yPredicted: yPredicted,\n                    size: Math.abs(error),\n                    error: error\n                };\n            });\n\n            svg.selectAll(\".squared-error-a\")\n                .data(squaredErrors)\n                .join(\"rect\")\n                .attr(\"class\", \"squared-error-a\")\n                .attr(\"x\", d => xScale(d.x))\n                .attr(\"y\", d => Math.min(yScale(d.y), yScale(d.yPredicted)))\n                .attr(\"width\", d => Math.abs(yScale(0) - yScale(d.size)))\n                .attr(\"height\", d => Math.abs(yScale(d.y) - yScale(d.yPredicted)))\n                .attr(\"fill\", \"purple\")\n                .attr(\"fill-opacity\", 0.3)\n                .attr(\"stroke\", \"purple\")\n                .attr(\"stroke-width\", 1);\n        }\n\n        function updateErrorSums() {\n            const purpleSquaredErrors = points.map(point => {\n                const yPredicted = lineA.slope * point.x + lineA.intercept;\n                const error = point.y - yPredicted;\n                return error * error;\n            });\n            const purpleSum = d3.sum(purpleSquaredErrors);\n            d3.select(\"#errorSum\").text(`Sum of Squared Errors (Purple Line): ${purpleSum.toFixed(2)}`);\n\n            const regressionSquaredErrors = points.map(point => {\n                const yPredicted = bestFitLine.slope * point.x + bestFitLine.intercept;\n                const error = point.y - yPredicted;\n                return error * error;\n            });\n            const regressionSum = d3.sum(regressionSquaredErrors);\n            d3.select(\"#regressionErrorSum\").text(`Sum of Squared Errors (Regression Line): ${regressionSum.toFixed(2)}`);\n        }\n\n        function toggleBestFit() {\n            showBestFit = !showBestFit;\n            updateGraph();\n        }\n\n        function changePreset() {\n            const presetIndex = parseInt(this.value);\n            points = [...presets[presetIndex]];\n            updateGraph();\n        }\n\n        d3.select(\"#toggleButton\").on(\"click\", toggleBestFit);\n        d3.select(\"#presetSelector\").on(\"change\", changePreset);\n\n        updateGraph();\n    <\/script>\n<\/body>\n<\/html>\n","protected":false},"excerpt":{"rendered":"<p>From Claude Sonnet 3.5: Find the Line of Best Fit Find the Line of Best Fit Preset 1Preset 2Preset 3Preset 4Preset 5 Show\/hide line of best fit Sum of Squared Errors (Purple Line): 0 Sum of Squared Errors (Regression Line): 0<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":11,"menu_order":0,"comment_status":"closed","ping_status":"open","template":"","meta":{"_coblocks_attr":"","_coblocks_dimensions":"","_coblocks_responsive_height":"","_coblocks_accordion_ie_support":"","neve_meta_sidebar":"","neve_meta_container":"","neve_meta_enable_content_width":"off","neve_meta_content_width":100,"neve_meta_title_alignment":"","neve_meta_author_avatar":"","neve_post_elements_order":"","neve_meta_disable_header":"","neve_meta_disable_footer":"","neve_meta_disable_title":"","footnotes":""},"class_list":["post-121","page","type-page","status-publish","hentry"],"featured_image_src":null,"_links":{"self":[{"href":"https:\/\/www.tedcoe.com\/math\/wp-json\/wp\/v2\/pages\/121","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.tedcoe.com\/math\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/www.tedcoe.com\/math\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/www.tedcoe.com\/math\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.tedcoe.com\/math\/wp-json\/wp\/v2\/comments?post=121"}],"version-history":[{"count":5,"href":"https:\/\/www.tedcoe.com\/math\/wp-json\/wp\/v2\/pages\/121\/revisions"}],"predecessor-version":[{"id":3132,"href":"https:\/\/www.tedcoe.com\/math\/wp-json\/wp\/v2\/pages\/121\/revisions\/3132"}],"up":[{"embeddable":true,"href":"https:\/\/www.tedcoe.com\/math\/wp-json\/wp\/v2\/pages\/11"}],"wp:attachment":[{"href":"https:\/\/www.tedcoe.com\/math\/wp-json\/wp\/v2\/media?parent=121"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}