barbara-asbell-site/attendance-variance.html
2026-07-19 20:08:23 +00:00

452 lines
17 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!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&amp;cs=tinysrgb&amp;dpr=2&amp;h=650&amp;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: 8896% 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 (114 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: 46 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>