Widget:Monte Carlo Regression
<style>
body {
font-family: Arial, sans-serif;
margin: 40px;
text-align: center;
}
.input-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 15px;
max-width: 900px;
margin: 0 auto 30px;
}
.input-grid strong {
font-size: 16px;
grid-column: span 3;
text-align: left;
padding-top: 10px;
}
input[type="text"] {
padding: 8px;
font-size: 14px;
width: 100%;
}
button {
padding: 10px 20px;
margin: 10px;
font-size: 16px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background-color: #45a049;
}
#plot {
width: 100%;
height: 75vh;
}
</style>
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
function parseValues(id) {
const val = document.getElementById(id).value.trim();
return val === "" ? [] : val.split(/\s+/).map(Number);
}
function plotData() {
const x = parseValues('xValues');
const y = parseValues('yValues');
const xErr = parseValues('xErrors');
const yErr = parseValues('yErrors');
const xLabel = document.getElementById('xLabel').value || 'X Axis';
const yLabel = document.getElementById('yLabel').value || 'Y Axis';
if (x.length !== y.length) {
alert('X and Y values must be the same length.');
return;
}
const N = 10000;
const slopes = [], intercepts = [];
function randn_bm() {
let u = 0, v = 0;
while (u === 0) u = Math.random();
while (v === 0) v = Math.random();
return Math.sqrt(-2.0 * Math.log(u)) * Math.cos(2.0 * Math.PI * v);
}
function linregress(xs, ys) {
const n = xs.length;
const xMean = xs.reduce((a, b) => a + b, 0) / n;
const yMean = ys.reduce((a, b) => a + b, 0) / n;
let num = 0, den = 0;
for (let i = 0; i < n; i++) {
num += (xs[i] - xMean) * (ys[i] - yMean);
den += (xs[i] - xMean) ** 2;
}
const slope = num / den;
const intercept = yMean - slope * xMean;
return { slope, intercept };
}
for (let i = 0; i < N; i++) {
const xSim = x.map((xi, j) => xi + randn_bm() * (xErr[j] || 0));
const ySim = y.map((yi, j) => yi + randn_bm() * (yErr[j] || 0));
const { slope, intercept } = linregress(xSim, ySim);
if (isFinite(slope) && isFinite(intercept)) {
slopes.push(slope);
intercepts.push(intercept);
}
}
const xRange = [];
const xmin = Math.min(...x), xmax = Math.max(...x);
const steps = 100;
const step = (xmax - xmin) / steps;
for (let i = 0; i <= steps; i++) {
xRange.push(xmin + i * step);
}
const lower = [], upper = [], meanLine = [];
for (let xi of xRange) {
const ySamples = slopes.map((m, i) => m * xi + intercepts[i]);
ySamples.sort((a, b) => a - b);
const lo = ySamples[Math.floor(0.025 * ySamples.length)];
const hi = ySamples[Math.floor(0.975 * ySamples.length)];
const avg = ySamples.reduce((a, b) => a + b, 0) / ySamples.length;
lower.push(lo);
upper.push(hi);
meanLine.push(avg);
}
const mean = arr => arr.reduce((a, b) => a + b, 0) / arr.length;
const std = arr => {
const m = mean(arr);
return Math.sqrt(arr.reduce((s, v) => s + (v - m) ** 2, 0) / (arr.length - 1));
};
const slopeStat = { mean: mean(slopes), std: std(slopes) };
const interceptStat = { mean: mean(intercepts), std: std(intercepts) };
const annotationText = `y = (${slopeStat.mean.toExponential(3)} ± ${slopeStat.std.toExponential(3)})x + (${interceptStat.mean.toExponential(3)} ± ${interceptStat.std.toExponential(3)})`;
const data = [];
// Original data points with error bars
data.push({
x: x,
y: y,
mode: 'markers',
type: 'scatter',
name: 'Data',
marker: { color: 'black', size: 7, symbol: 'x-thin-open' },
error_x: xErr.length === x.length ? {
type: 'data',
array: xErr,
visible: true
} : undefined,
error_y: yErr.length === y.length ? {
type: 'data',
array: yErr,
visible: true
} : undefined
});
// Confidence band
data.push({
x: [...xRange, ...xRange.slice().reverse()],
y: [...upper, ...lower.slice().reverse()],
fill: 'toself',
fillcolor: 'rgba(255, 0, 0, 0.2)',
line: { color: 'transparent' },
name: '95% Confidence Band',
type: 'scatter',
showlegend: false
});
// Mean regression line
data.push({
x: xRange,
y: meanLine,
mode: 'lines',
type: 'scatter',
name: 'Mean Fit',
line: { color: 'red', width: 2 }
});
const layout = {
title: ,
xaxis: { title: xLabel },
yaxis: { title: yLabel },
showlegend: false,
annotations: [{
x: 0.05,
y: 0.95,
xref: 'paper',
yref: 'paper',
text: annotationText,
showarrow: false,
font: { color: 'black', size: 14 }
}]
};
Plotly.newPlot('plot', data, layout);
}
function downloadPlot() {
Plotly.downloadImage('plot', { format: 'png', filename: 'monte_carlo_regression' });
}
</script>