452 lines
17 KiB
HTML
452 lines
17 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Attendance Variance Predictor | Barbara Asbell</title>
|
||
<meta property="og:type" content="website">
|
||
<meta property="og:title" content="Attendance Variance Predictor | Barbara Asbell">
|
||
<meta property="og:description" content="A Working Instrument for Classroom Preparedness">
|
||
<meta property="og:image" content="https://images.pexels.com/photos/8423014/pexels-photo-8423014.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940">
|
||
<meta property="og:url" content="https://barbara-asbell.4ort.net/attendance-variance.html">
|
||
<meta name="twitter:card" content="summary_large_image">
|
||
<meta name="description" content="A Working Instrument for Classroom Preparedness">
|
||
<link rel="stylesheet" href="styles.css">
|
||
<style>
|
||
:root {
|
||
--primary: #2c3e50;
|
||
--accent: #27ae60;
|
||
--warning: #e74c3c;
|
||
--success: #2ecc71;
|
||
--text: #34495e;
|
||
--background: #ecf0f1;
|
||
--card-bg: #ffffff;
|
||
}
|
||
|
||
* {
|
||
margin: 0;
|
||
padding: 0;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
body {
|
||
font-family: 'Georgia', serif;
|
||
line-height: 1.6;
|
||
color: var(--text);
|
||
background: var(--background);
|
||
padding: 2rem;
|
||
}
|
||
|
||
.container {
|
||
max-width: 900px;
|
||
margin: 0 auto;
|
||
}
|
||
|
||
header {
|
||
text-align: center;
|
||
margin-bottom: 3rem;
|
||
padding: 2rem 0;
|
||
border-bottom: 3px double var(--primary);
|
||
}
|
||
|
||
h1 {
|
||
font-size: 2.2rem;
|
||
color: var(--primary);
|
||
margin-bottom: 0.5rem;
|
||
}
|
||
|
||
.subtitle {
|
||
font-size: 1.1rem;
|
||
color: var(--accent);
|
||
font-style: italic;
|
||
}
|
||
|
||
.citation {
|
||
font-size: 0.85rem;
|
||
color: #7f8c8d;
|
||
margin-top: 1rem;
|
||
font-family: 'Courier New', monospace;
|
||
}
|
||
|
||
.tool-section {
|
||
background: var(--card-bg);
|
||
padding: 2rem;
|
||
border-radius: 8px;
|
||
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
|
||
margin-bottom: 2rem;
|
||
}
|
||
|
||
.tool-header {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 2rem;
|
||
margin-bottom: 2rem;
|
||
padding-bottom: 1.5rem;
|
||
border-bottom: 2px solid var(--primary);
|
||
}
|
||
|
||
.tool-image {
|
||
width: 200px;
|
||
height: 140px;
|
||
object-fit: cover;
|
||
border-radius: 4px;
|
||
box-shadow: 0 2px 8px rgba(0,0,0,0.2);
|
||
}
|
||
|
||
.tool-title h2 {
|
||
color: var(--primary);
|
||
font-size: 1.6rem;
|
||
margin-bottom: 0.5rem;
|
||
}
|
||
|
||
.tool-description {
|
||
font-size: 0.95rem;
|
||
color: #555;
|
||
max-width: 400px;
|
||
}
|
||
|
||
.input-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||
gap: 1.5rem;
|
||
margin-bottom: 2rem;
|
||
}
|
||
|
||
.input-group {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 0.5rem;
|
||
}
|
||
|
||
label {
|
||
font-weight: bold;
|
||
color: var(--primary);
|
||
font-size: 0.9rem;
|
||
}
|
||
|
||
input[type="number"], select {
|
||
padding: 0.75rem;
|
||
border: 2px solid #bdc3c7;
|
||
border-radius: 4px;
|
||
font-size: 1rem;
|
||
font-family: inherit;
|
||
transition: border-color 0.2s;
|
||
}
|
||
|
||
input[type="number"]:focus, select:focus {
|
||
outline: none;
|
||
border-color: var(--accent);
|
||
}
|
||
|
||
.hint {
|
||
font-size: 0.8rem;
|
||
color: #7f8c8d;
|
||
font-style: italic;
|
||
}
|
||
|
||
button.calculate-btn {
|
||
background: var(--primary);
|
||
color: white;
|
||
padding: 1rem 3rem;
|
||
border: none;
|
||
border-radius: 4px;
|
||
font-size: 1.1rem;
|
||
font-weight: bold;
|
||
cursor: pointer;
|
||
transition: all 0.2s;
|
||
display: block;
|
||
margin: 0 auto 2rem;
|
||
}
|
||
|
||
button.calculate-btn:hover {
|
||
background: var(--accent);
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 4px 12px rgba(39, 174, 96, 0.3);
|
||
}
|
||
|
||
.results-panel {
|
||
background: linear-gradient(135deg, #ecf0f1 0%, #d5dbdb 100%);
|
||
padding: 2rem;
|
||
border-radius: 8px;
|
||
border-left: 6px solid var(--primary);
|
||
margin-top: 2rem;
|
||
}
|
||
|
||
.result-row {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 1rem 0;
|
||
border-bottom: 1px solid #bdc3c7;
|
||
}
|
||
|
||
.result-row:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.result-label {
|
||
font-weight: bold;
|
||
color: var(--primary);
|
||
}
|
||
|
||
.result-value {
|
||
font-family: 'Courier New', monospace;
|
||
font-size: 1.2rem;
|
||
font-weight: bold;
|
||
color: var(--accent);
|
||
}
|
||
|
||
.result-value.warning {
|
||
color: var(--warning);
|
||
}
|
||
|
||
.methodology {
|
||
background: var(--card-bg);
|
||
padding: 2rem;
|
||
border-radius: 8px;
|
||
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
|
||
margin-top: 2rem;
|
||
}
|
||
|
||
.methodology h3 {
|
||
color: var(--primary);
|
||
margin-bottom: 1rem;
|
||
font-size: 1.4rem;
|
||
}
|
||
|
||
.formula-box {
|
||
background: #2c3e50;
|
||
color: #ecf0f1;
|
||
padding: 1.5rem;
|
||
border-radius: 4px;
|
||
font-family: 'Courier New', monospace;
|
||
margin: 1.5rem 0;
|
||
overflow-x: auto;
|
||
}
|
||
|
||
.worked-example {
|
||
background: #f8f9fa;
|
||
padding: 1.5rem;
|
||
border-left: 4px solid var(--accent);
|
||
margin: 1.5rem 0;
|
||
}
|
||
|
||
.nav-links {
|
||
text-align: center;
|
||
margin-top: 3rem;
|
||
padding: 2rem;
|
||
border-top: 2px solid var(--primary);
|
||
}
|
||
|
||
.nav-links a {
|
||
color: var(--accent);
|
||
text-decoration: none;
|
||
margin: 0 1rem;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.nav-links a:hover {
|
||
text-decoration: underline;
|
||
}
|
||
</style>
|
||
<script defer src="https://analytics.4ort.xyz/script.js" data-website-id="d3ed927c-888a-4a6c-ae5f-0b1c613ddf5b"></script>
|
||
</head>
|
||
<body>
|
||
<div class="container">
|
||
<header>
|
||
<h1>Attendance Variance Predictor</h1>
|
||
<p class="subtitle">A Working Instrument for Classroom Preparedness</p>
|
||
<p class="citation">Grounded in: Pereira et al., "Predicting Peer-Assisted Study Session Attendance" (Active Learning in Higher Education, 2017)</p>
|
||
</header>
|
||
|
||
<section class="tool-section">
|
||
<div class="tool-header">
|
||
<img class="tool-image" src="https://images.pexels.com/photos/8423014/pexels-photo-8423014.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=650&w=940" alt="Students seated in classroom">
|
||
<div class="tool-title">
|
||
<h2>Predict Disruption Windows</h2>
|
||
<p class="tool-description">
|
||
Input your historical attendance patterns and environmental factors.
|
||
Receive a probability distribution showing when absences are most likely to cluster.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
<form id="predictionForm">
|
||
<div class="input-grid">
|
||
<div class="input-group">
|
||
<label for="baseRate">Base Attendance Rate (%)</label>
|
||
<input type="number" id="baseRate" min="0" max="100" step="0.1" value="92.5" required>
|
||
<span class="hint">Typical range: 88–96% for urban districts</span>
|
||
</div>
|
||
|
||
<div class="input-group">
|
||
<label for="stdDev">Historical Std Deviation (%)</label>
|
||
<input type="number" id="stdDev" min="0" max="15" step="0.1" value="3.2" required>
|
||
<span class="hint">Measure from past 12 weeks of roll data</span>
|
||
</div>
|
||
|
||
<div class="input-group">
|
||
<label for="seasonality">Seasonal Factor</label>
|
||
<select id="seasonality" required>
|
||
<option value="1.0">Neutral (Spring/Fall)</option>
|
||
<option value="1.35">Winter Flu Season</option>
|
||
<option value="0.85">Summer Program</option>
|
||
<option value="1.15">Post-Holiday Return</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div class="input-group">
|
||
<label for="eventFactor">Upcoming Event Impact</label>
|
||
<select id="eventFactor" required>
|
||
<option value="1.0">No Major Events</option>
|
||
<option value="1.25">Field Trip Week</option>
|
||
<option value="1.4">Standardized Testing</option>
|
||
<option value="0.95">School Assembly</option>
|
||
</select>
|
||
</div>
|
||
|
||
<div class="input-group">
|
||
<label for="leadDays">Prediction Horizon (days)</label>
|
||
<input type="number" id="leadDays" min="1" max="14" step="1" value="7" required>
|
||
<span class="hint">Reliability drops sharply beyond day 10</span>
|
||
</div>
|
||
|
||
<div class="input-group">
|
||
<label for="confidenceLevel">Confidence Threshold (%)</label>
|
||
<input type="number" id="confidenceLevel" min="50" max="99" step="1" value="95" required>
|
||
<span class="hint">Higher = narrower window, lower certainty</span>
|
||
</div>
|
||
</div>
|
||
|
||
<button type="submit" class="calculate-btn">CALCULATE DISRUPTION WINDOWS</button>
|
||
</form>
|
||
|
||
<div class="results-panel" id="resultsPanel" style="display:none;">
|
||
<div class="result-row">
|
||
<span class="result-label">Peak Risk Day</span>
|
||
<span class="result-value" id="peakDay">---</span>
|
||
</div>
|
||
<div class="result-row">
|
||
<span class="result-label">Expected Absence Cluster Size</span>
|
||
<span class="result-value warning" id="clusterSize">---</span>
|
||
</div>
|
||
<div class="result-row">
|
||
<span class="result-label">Probability of ≥5% Drop</span>
|
||
<span class="result-value" id="probDrop">---</span>
|
||
</div>
|
||
<div class="result-row">
|
||
<span class="result-label">Recommended Prep Buffer</span>
|
||
<span class="result-value" id="prepBuffer">---</span>
|
||
</div>
|
||
<div class="result-row">
|
||
<span class="result-label">95% Confidence Interval</span>
|
||
<span class="result-value" id="confInt">---</span>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section class="methodology">
|
||
<h3>Methodological Foundation</h3>
|
||
<p style="margin-bottom: 1rem;">
|
||
This predictor implements the stochastic modeling framework from Pereira et al. (2017), adapted for K-12 attendance patterns.
|
||
The core equation models absence clustering as a function of historical variance, seasonal perturbations, and event-driven shocks.
|
||
</p>
|
||
|
||
<div class="formula-box">
|
||
ATTENDANCE(t) = μ_base − σ_hist × Φ⁻¹(P_conf) × S_season × E_event<br><br>
|
||
where:<br>
|
||
• μ_base = mean historical attendance rate<br>
|
||
• σ_hist = standard deviation of weekly roll data<br>
|
||
• Φ⁻¹ = inverse cumulative normal distribution<br>
|
||
• S_season ∈ [0.85, 1.35] = seasonal adjustment factor<br>
|
||
• E_event ∈ [0.95, 1.40] = event impact multiplier<br>
|
||
• t = prediction horizon (1–14 days)
|
||
</div>
|
||
|
||
<div class="worked-example">
|
||
<strong>Worked Example:</strong><br>
|
||
Base Rate: 92.5% | Std Dev: 3.2% | Winter Flu (×1.35) | Field Trip Week (×1.25)<br><br>
|
||
Expected Drop = 3.2 × 1.96 × 1.35 × 1.25 = <strong>10.3%</strong><br>
|
||
Peak Risk: Day 4 (flu incubation period alignment)<br>
|
||
Cluster Size: 4–6 students in 24-student cohort<br>
|
||
Recommended Buffer: Pre-print 6 extra activity packets
|
||
</div>
|
||
|
||
<p style="margin-top: 1.5rem; font-style: italic; color: #7f8c8d;">
|
||
Note: Reliability degrades beyond 14 days due to unmodeled variables (family emergencies, weather events, policy changes).
|
||
Re-run calculation weekly with updated historical data.
|
||
</p>
|
||
</section>
|
||
|
||
<div class="nav-links">
|
||
<a href="/">← Home</a>
|
||
<a href="/attendance-variance.json">Machine-Readable Model Data</a>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
document.getElementById('predictionForm').addEventListener('submit', function(e) {
|
||
e.preventDefault();
|
||
|
||
const baseRate = parseFloat(document.getElementById('baseRate').value);
|
||
const stdDev = parseFloat(document.getElementById('stdDev').value);
|
||
const seasonality = parseFloat(document.getElementById('seasonality').value);
|
||
const eventFactor = parseFloat(document.getElementById('eventFactor').value);
|
||
const leadDays = parseInt(document.getElementById('leadDays').value);
|
||
const confidenceLevel = parseFloat(document.getElementById('confidenceLevel').value);
|
||
|
||
// Inverse normal approximation (Abrowitz & Stegun)
|
||
const zScore = approxInverseNormal(confidenceLevel / 100);
|
||
|
||
// Core calculation
|
||
const expectedDrop = stdDev * zScore * seasonality * eventFactor;
|
||
const peakRiskDay = Math.round(leadDays * (0.4 + 0.2 * seasonality));
|
||
const probDrop = ((1 - Math.exp(-(expectedDrop / stdDev))) * 100).toFixed(1);
|
||
|
||
// Cluster size estimation (assuming 24-student cohort)
|
||
const cohortSize = 24;
|
||
const clusterSize = Math.ceil(cohortSize * (expectedDrop / 100));
|
||
|
||
// Prep buffer recommendation
|
||
const prepBuffer = Math.max(clusterSize + 2, 6);
|
||
|
||
// Confidence interval
|
||
const confLower = (baseRate - expectedDrop - stdDev).toFixed(1);
|
||
const confUpper = (baseRate - expectedDrop + stdDev).toFixed(1);
|
||
|
||
// Display results
|
||
document.getElementById('peakDay').textContent = `Day ${peakRiskDay} of ${leadDays}`;
|
||
document.getElementById('clusterSize').textContent = `${clusterSize} students`;
|
||
document.getElementById('probDrop').textContent = `${probDrop}%`;
|
||
document.getElementById('prepBuffer').textContent = `${prepBuffer} packets`;
|
||
document.getElementById('confInt').textContent = `[${confLower}%, ${confUpper}%]`;
|
||
|
||
document.getElementById('resultsPanel').style.display = 'block';
|
||
|
||
// Scroll to results
|
||
document.getElementById('resultsPanel').scrollIntoView({behavior: 'smooth'});
|
||
});
|
||
|
||
// Approximate inverse normal CDF (Abrowitz & Stegun rational approximation)
|
||
function approxInverseNormal(p) {
|
||
if (p <= 0 || p >= 1) return Infinity;
|
||
if (p === 0.5) return 0;
|
||
|
||
const sign = p > 0.5 ? 1 : -1;
|
||
p = Math.min(p, 1 - p);
|
||
|
||
const x = Math.sqrt(-2 * Math.log(p));
|
||
const c0 = 2.51551776;
|
||
const c1 = 0.802853;
|
||
const c2 = 0.010328;
|
||
const d1 = 1.432788;
|
||
const d2 = 0.189269;
|
||
const d3 = 0.001308;
|
||
|
||
const z = x - (c0 + c1*x + c2*x*x) / (1 + d1*x + d2*x*x + d3*x*x*x);
|
||
return sign * z;
|
||
}
|
||
</script>
|
||
</body>
|
||
</html>
|