Neroml / Templates /poly.html
deedrop1140's picture
Upload 69 files
6491927 verified
{% extends "layout.html" %}
{% block content %}
<script src="https://cdn.tailwindcss.com"></script>
<div class="container mx-auto py-10 px-4 max-w-5xl">
<h1 class="text-3xl font-bold text-center text-gray-800 mb-6"> Polynomial Regression Visualizer</h1>
<div class="bg-blue-50 border border-blue-200 rounded-xl p-6 shadow-sm mb-10">
<h2 class="text-2xl font-semibold text-blue-800 mb-4"> What is Polynomial Regression?</h2>
<p class="text-gray-700 mb-4">
Polynomial Regression is a type of regression analysis that models the relationship between the independent variable x
and the dependent variable y as an n-th degree polynomial. It extends simple linear regression by considering higher-degree terms.
</p>
<h3 class="text-xl font-semibold text-blue-700 mt-6 mb-2"> Why Polynomial Regression?</h3>
<ul class="list-disc ml-6 text-gray-700 mb-4">
<li>Linear Regression only fits straight lines. But real-world data is often curved or non-linear.</li>
<li>Polynomial Regression allows us to capture these curves while still being simple and interpretable.</li>
</ul>
<h3 class="text-xl font-semibold text-blue-700 mt-6 mb-2"> General Equation:</h3>
<p class="text-gray-700 italic mb-2">
<strong>y = θ<sub>0</sub> + θ<sub>1</sub>x + θ<sub>2</sub>x<sup>2</sup> + θ<sub>3</sub>x<sup>3</sup> + ... + θ<sub>n</sub>x<sup>n</sup></strong>
</p>
<p class="text-sm text-gray-600 mb-4">
Here, theta_0, theta_1, ..., theta_n are the coefficients learned by the model.
</p>
<div class="bg-blue-100 border border-blue-300 rounded-lg p-4 mb-6">
<h3 class="text-xl font-semibold text-blue-700 mb-3">Theoretical Examples: y = x^2 + 2x</h3>
<p class="text-gray-700 mb-3">
To illustrate how the equation y = x^2 + 2x creates a curve, let's substitute a few values for x:
</p>
<ul class="list-disc ml-6 text-gray-700">
<li class="mb-2">
<strong>When x = 0:</strong>
y = (0)^2 + 2(0) = 0 + 0 = **0**
<p class="text-sm text-gray-600 mt-1">
An input of 0 results in an output of 0.
</p>
</li>
<li class="mb-2">
<strong>When x = 1:</strong>
y = (1)^2 + 2(1) = 1 + 2 = **3**
<p class="text-sm text-gray-600 mt-1">
An input of 1 yields an output of 3.
</p>
</li>
<li class="mb-2">
<strong>When x = 3:</strong>
y = (3)^2 + 2(3) = 9 + 6 = **15**
<p class="text-sm text-gray-600 mt-1">
With an input of 3, the output becomes 15.
</p>
</li>
<li>
<strong>When x = 5:</strong>
y = (5)^2 + 2(5) = 25 + 10 = **35**
<p class="text-sm text-gray-600 mt-1">
For an input of 5, the predicted output is 35.
</p>
</li>
</ul>
<p class="text-gray-700 mt-3">
These examples show how the relationship between x and y is curved, not linear.
</p>
</div>
<h3 class="text-xl font-semibold text-blue-700 mt-6 mb-2"> How this App Works:</h3>
<ul class="list-disc ml-6 text-gray-700">
<li>The model is trained using Python's <code>PolynomialFeatures</code> from <code>sklearn.preprocessing</code>.</li>
<li>You enter a value (e.g., hours studied), and the trained model predicts the output (e.g., expected score).</li>
<li>A dynamic graph shows the curve and predicted point in real-time.</li>
</ul>
</div>
<div class="bg-white shadow-md rounded-xl p-6 mb-8">
<h2 class="text-xl font-semibold text-gray-800 mb-4"> Polynomial Curve Visualization</h2>
<div class="flex flex-col md:flex-row gap-4">
<div class="flex-grow">
<canvas id="polyCanvas" class="w-full h-80 border border-gray-300 rounded-md bg-white"></canvas>
</div>
<div class="w-full md:w-1/3 p-4 bg-gray-50 rounded-lg">
<h3 class="text-lg font-semibold text-gray-700 mb-2">Model Details:</h3>
<p class="text-gray-600 mb-2">Equation: <span class="font-mono"><strong>y = x^2 + 2x</strong></span></p>
<p class="text-gray-600 mb-2">Our model learned coefficients for:</p>
<ul class="list-disc list-inside text-gray-600 pl-4">
<li>x^0 (Intercept): <span id="coeff0">0.00</span></li>
<li>x^1: <span id="coeff1">2.00</span></li>
<li>x^2: <span id="coeff2">1.00</span></li>
</ul>
<p class="text-gray-600 mt-4 text-sm">
(These coefficients correspond to the formula <strong>y = θ₀ + θ₁x + θ₂x²</strong>)
</p>
</div>
</div>
</div>
<div class="bg-white shadow-md rounded-xl p-6">
<h2 class="text-xl font-semibold text-gray-800 mb-4"> Make a Prediction</h2>
<form method="POST" class="flex flex-col sm:flex-row items-center gap-4">
<label for="hoursInput" class="text-gray-700 font-medium">Enter Input (x):</label>
<input type="number" step="0.1" min="0" name="hours" id="hoursInput"
value="{{ input_hours if input_hours is not none else '3.0' }}"
class="border border-gray-300 rounded-md p-2 w-40" required>
<button type="submit" class="bg-blue-600 text-white px-6 py-2 rounded-md shadow hover:bg-blue-700">
Predict
</button>
</form>
{% if prediction is not none %}
<div class="mt-6 bg-green-50 border border-green-200 rounded-lg p-4">
<h3 class="text-lg font-semibold text-green-800 mb-2"> Predicted Value (y):</h3>
<p class="text-3xl font-bold text-green-900" id="predictedScore">{{ prediction }}</p>
</div>
{% endif %}
</div>
</div>
<script>
const canvas = document.getElementById("polyCanvas");
const ctx = canvas.getContext("2d");
// Data points used for training the model (y = x^2 + 2x)
const X_data = [1, 2, 3, 4, 5];
const y_data = [3, 8, 15, 24, 35]; // These match the curve y = x^2 + 2x
// Coefficients of the polynomial (y = 0*x^0 + 2*x^1 + 1*x^2)
// Manually set for display since the model generates them
const polyCoeffs = [0, 2, 1]; // [constant, x^1 coeff, x^2 coeff]
// Get initial prediction data from Flask if available
const initialHours = parseFloat("{{ input_hours if input_hours is not none else 'null' }}");
const initialPrediction = parseFloat("{{ prediction if prediction is not none else 'null' }}");
let predictedHours = initialHours !== null && !isNaN(initialHours) ? initialHours : null;
let predictedScore = initialPrediction !== null && !isNaN(initialPrediction) ? initialPrediction : null;
// Display coefficients
document.getElementById('coeff0').textContent = polyCoeffs[0].toFixed(2);
document.getElementById('coeff1').textContent = polyCoeffs[1].toFixed(2);
document.getElementById('coeff2').textContent = polyCoeffs[2].toFixed(2);
// Coordinate conversion functions
let xScale, yScale;
let xMin, xMax, yMin, yMax;
const padding = 50;
function toCanvasX(x) {
return padding + (x - xMin) * xScale;
}
function toCanvasY(y) {
return canvas.height - padding - (y - yMin) * yScale;
}
function setupScaling() {
const dpi = window.devicePixelRatio;
const rect = canvas.getBoundingClientRect();
canvas.width = rect.width * dpi;
canvas.height = rect.height * dpi;
ctx.scale(dpi, dpi);
// Determine data ranges for X and Y axes
xMin = Math.min(...X_data, 0);
xMax = Math.max(...X_data, predictedHours !== null ? predictedHours : 0, 6) + 1; // Extend slightly
yMin = Math.min(...y_data, 0);
// Calculate max Y based on the curve for xMax
const maxCurveY = polyCoeffs[0] + polyCoeffs[1] * xMax + polyCoeffs[2] * xMax * xMax;
yMax = Math.max(...y_data, predictedScore !== null ? predictedScore : 0, maxCurveY) + 10; // Extend slightly
// Calculate scaling factors
xScale = (canvas.width - 2 * padding) / (xMax - xMin);
yScale = (canvas.height - 2 * padding) / (yMax - yMin);
}
function drawGraph() {
ctx.clearRect(0, 0, canvas.width, canvas.height); // Clear the canvas
// Draw axes
ctx.beginPath();
ctx.strokeStyle = '#64748b'; // Slate gray for axes
ctx.lineWidth = 2;
ctx.moveTo(toCanvasX(xMin), toCanvasY(yMin)); // X-axis
ctx.lineTo(toCanvasX(xMax), toCanvasY(yMin));
ctx.moveTo(toCanvasX(xMin), toCanvasY(yMin)); // Y-axis
ctx.lineTo(toCanvasX(xMin), toCanvasY(yMax));
ctx.stroke();
// Draw axis labels and ticks
ctx.fillStyle = '#475569'; // Darker gray for labels
ctx.font = '14px Inter';
ctx.textAlign = 'center';
ctx.textBaseline = 'top';
// X-axis labels
const xTickStep = 1;
for (let i = Math.ceil(xMin / xTickStep) * xTickStep; i <= Math.floor(xMax); i += xTickStep) {
if (i >= 0) {
ctx.fillText(i.toFixed(0), toCanvasX(i), canvas.height - padding + 10);
ctx.beginPath();
ctx.moveTo(toCanvasX(i), canvas.height - padding);
ctx.lineTo(toCanvasX(i), canvas.height - padding - 5);
ctx.stroke();
}
}
ctx.fillText('Input (x)', canvas.width / 2, canvas.height - 20);
ctx.textAlign = 'right';
ctx.textBaseline = 'middle';
// Y-axis labels
const yTickStep = Math.max(1, Math.floor((yMax - yMin) / 50) * 10 || 5); // Dynamic step
for (let i = Math.ceil(yMin / yTickStep) * yTickStep; i <= Math.floor(yMax); i += yTickStep) {
if (i >= 0) {
ctx.fillText(i.toFixed(0), padding - 10, toCanvasY(i));
ctx.beginPath();
ctx.moveTo(padding, toCanvasY(i));
ctx.lineTo(padding + 5, toCanvasY(i));
ctx.stroke();
}
}
// Y-axis title (rotated)
ctx.save();
ctx.translate(20, canvas.height / 2);
ctx.rotate(-Math.PI / 2);
ctx.textAlign = 'center';
ctx.fillText('Output (y)', 0, 0);
ctx.restore();
// Draw data points (blue circles)
ctx.fillStyle = '#3b82f6';
X_data.forEach((x, i) => {
ctx.beginPath();
ctx.arc(toCanvasX(x), toCanvasY(y_data[i]), 5, 0, Math.PI * 2);
ctx.fill();
});
// Draw Polynomial Curve (red line)
ctx.beginPath();
ctx.strokeStyle = '#ef4444';
ctx.lineWidth = 3;
// Start from xMin, go to xMax with small steps
for (let x = xMin; x <= xMax; x += 0.1) {
const y_curve = polyCoeffs[0] + polyCoeffs[1] * x + polyCoeffs[2] * x * x;
if (x === xMin) {
ctx.moveTo(toCanvasX(x), toCanvasY(y_curve));
} else {
ctx.lineTo(toCanvasX(x), toCanvasY(y_curve));
}
}
ctx.stroke();
// Draw predicted point and lines if available (green point and dashed lines)
if (predictedHours !== null && predictedScore !== null) {
const predX_canvas = toCanvasX(predictedHours);
const predY_canvas = toCanvasY(predictedScore);
// Predicted point
ctx.fillStyle = '#22c55e';
ctx.beginPath();
ctx.arc(predX_canvas, predY_canvas, 6, 0, Math.PI * 2);
ctx.fill();
// Dotted lines to axes
ctx.strokeStyle = '#22c55e';
ctx.lineWidth = 1.5;
ctx.setLineDash([5, 5]);
// Line from predicted point to X-axis
ctx.beginPath();
ctx.moveTo(predX_canvas, predY_canvas);
ctx.lineTo(predX_canvas, toCanvasY(yMin));
ctx.stroke();
// Line from predicted point to Y-axis
ctx.beginPath();
ctx.moveTo(predX_canvas, predY_canvas);
ctx.lineTo(toCanvasX(xMin), predY_canvas);
ctx.stroke();
ctx.setLineDash([]); // Reset line dash to solid
}
}
// Initial setup and draw when the window loads
window.addEventListener('load', () => {
setupScaling(); // Set initial canvas size and scaling
drawGraph(); // Draw the graph
});
// Redraw the graph whenever the window is resized
window.addEventListener('resize', () => {
setupScaling();
drawGraph();
});
// Optional: Allow clicking on canvas to set hours input (for quick testing)
canvas.addEventListener('click', (event) => {
const rect = canvas.getBoundingClientRect();
const mouseX = (event.clientX - rect.left) / (canvas.width / rect.width);
const mouseY = (event.clientY - rect.top) / (canvas.height / rect.height);
const clickedX = xMin + (mouseX - padding) / xScale;
// Update the input field with the clicked X
document.getElementById('hoursInput').value = clickedX.toFixed(1);
// Manually trigger the form submission (or fetch call if you had one)
// For simplicity with full page reload, we'll let the user click "Predict"
// If you want instant update on click, you'd need an AJAX call and update
// predictedHours/predictedScore directly then call drawGraph().
// For now, this just pre-fills the input.
});
</script>
{% endblock %}