web ui
This commit is contained in:
parent
aa508a43cb
commit
8aaf95c374
5 changed files with 20479 additions and 0 deletions
240
static/poopGraph.js
Normal file
240
static/poopGraph.js
Normal file
|
@ -0,0 +1,240 @@
|
|||
fetch('/data.json')
|
||||
.then(resp => resp.json())
|
||||
.then(plot)
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
});
|
||||
|
||||
function average(window) {
|
||||
let y_sum = 0;
|
||||
let count = 0;
|
||||
for (const sample of window) {
|
||||
if (sample['y'] !== undefined) {
|
||||
y_sum += sample['y'];
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
if (count === 0) {
|
||||
return undefined;
|
||||
}
|
||||
return y_sum / count;
|
||||
}
|
||||
|
||||
function localLinearRegression(slopeWindowSize, averageWindowSize) {
|
||||
return (window) => {
|
||||
let x_sum = 0;
|
||||
let y_sum = 0;
|
||||
let count = 0;
|
||||
for (const sample of window.slice(-slopeWindowSize)) {
|
||||
if (sample['y'] !== undefined) {
|
||||
x_sum += sample['x'];
|
||||
y_sum += sample['y'];
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
if (count === 0) {
|
||||
return undefined;
|
||||
}
|
||||
const x_avg = x_sum / count;
|
||||
const y_avg = y_sum / count;
|
||||
|
||||
let numerator = 0;
|
||||
let denominator = 0;
|
||||
for (const sample of window) {
|
||||
if (sample['y'] !== undefined) {
|
||||
const x_err = sample['x'] - x_avg;
|
||||
const y_err = sample['y'] - y_avg;
|
||||
numerator += x_err * y_err;
|
||||
denominator += x_err * x_err;
|
||||
}
|
||||
}
|
||||
if (denominator === 0) {
|
||||
return window.at(-1)['y'];
|
||||
}
|
||||
const slope = numerator / denominator;
|
||||
|
||||
let short_x_sum = 0;
|
||||
let short_y_sum = 0;
|
||||
let short_count = 0;
|
||||
for (const sample of window.slice(-averageWindowSize)) {
|
||||
if (sample['y'] !== undefined) {
|
||||
short_x_sum += sample['x'];
|
||||
short_y_sum += sample['y'];
|
||||
short_count += 1;
|
||||
}
|
||||
}
|
||||
if (short_count === 0) {
|
||||
return y_avg + (window.at(-1)['x'] - x_avg) * slope;
|
||||
}
|
||||
const short_x_avg = short_x_sum / short_count;
|
||||
const short_y_avg = short_y_sum / short_count;
|
||||
return short_y_avg + (window.at(-1)['x'] - short_x_avg) * slope;
|
||||
};
|
||||
}
|
||||
|
||||
function smooth(data, column, windowSize, smoothFunc) {
|
||||
const smoothed = [];
|
||||
const window = [];
|
||||
for (const row of data) {
|
||||
window.push({ x: new Date(row['x']).getTime(), y: row[column] });
|
||||
if (window.length > windowSize) {
|
||||
window.shift();
|
||||
}
|
||||
smoothed.push({ x: row['x'], y: smoothFunc(window) });
|
||||
}
|
||||
return smoothed;
|
||||
}
|
||||
|
||||
function extractWithErrorBars(data, region) {
|
||||
const extracted = [];
|
||||
for (const row of data['rows']) {
|
||||
const y = row[region + ' (copies/mL)'];
|
||||
if (y !== undefined) {
|
||||
extracted.push({
|
||||
x: row['Date'],
|
||||
y: y,
|
||||
yMin: y - (row[region + ' Low Confidence Interval'] || 0),
|
||||
yMax: y + (row[region + ' High Confidence Interval'] || 0),
|
||||
});
|
||||
}
|
||||
}
|
||||
return extracted;
|
||||
}
|
||||
|
||||
function plot(data) {
|
||||
const northData = extractWithErrorBars(data, 'Northern');
|
||||
const southData = extractWithErrorBars(data, 'Southern');
|
||||
const northCtx = document.getElementById('northCanvas');
|
||||
const southCtx = document.getElementById('southCanvas');
|
||||
let options = {
|
||||
maintainAspectRatio: false,
|
||||
interaction: {
|
||||
intersect: false,
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
type: 'timeseries',
|
||||
min: document.getElementById('startDate').value,
|
||||
max: document.getElementById('endDate').value,
|
||||
},
|
||||
y: {
|
||||
min: 0,
|
||||
},
|
||||
},
|
||||
plugins: {
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Wastewater COVID RNA Signal (copies/mL)',
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const northChart = new Chart(northCtx, {
|
||||
data: {
|
||||
datasets: [
|
||||
{
|
||||
type: 'scatterWithErrorBars',
|
||||
label: 'North System',
|
||||
data: northData,
|
||||
backgroundColor: 'rgba(0,0,255,0.5)',
|
||||
color: 'rgba(0,0,255,0.5)',
|
||||
borderColor: 'rgba(0,0,255,0.5)',
|
||||
spanGaps: true,
|
||||
},
|
||||
/* {
|
||||
type: 'line',
|
||||
label: 'smoothing 18/14',
|
||||
data: smooth(northData, 'y', 18, localLinearRegression(18, 14)),
|
||||
pointRadius: 0,
|
||||
borderColor: 'rgba(0,0,255,0.6)',
|
||||
},
|
||||
{
|
||||
type: 'line',
|
||||
label: 'smoothing 14/14',
|
||||
data: smooth(northData, 'y', 14, localLinearRegression(14, 14)),
|
||||
pointRadius: 0,
|
||||
borderColor: 'rgba(255,0,255,0.6)',
|
||||
},*/
|
||||
{
|
||||
type: 'line',
|
||||
label: 'North System 7-day average (low)',
|
||||
data: smooth(northData, 'yMin', 7, average),
|
||||
pointRadius: 0,
|
||||
borderColor: 'rgba(0,255,0,0.6)',
|
||||
fill: '+1',
|
||||
backgroundColor: 'rgba(0,255,0,0.2)',
|
||||
},
|
||||
{
|
||||
type: 'line',
|
||||
label: 'North System 7-day average',
|
||||
data: smooth(northData, 'y', 7, average),
|
||||
pointRadius: 0,
|
||||
borderColor: 'rgba(0,128,0,0.9)',
|
||||
backgroundColor: 'rgba(0,128,0,0.9)',
|
||||
},
|
||||
{
|
||||
type: 'line',
|
||||
label: 'North System 7-day average (high)',
|
||||
data: smooth(northData, 'yMax', 7, average),
|
||||
pointRadius: 0,
|
||||
borderColor: 'rgba(0,255,0,0.6)',
|
||||
fill: '-1',
|
||||
backgroundColor: 'rgba(0,255,0,0.2)',
|
||||
},
|
||||
]
|
||||
},
|
||||
options,
|
||||
});
|
||||
const southChart = new Chart(southCtx, {
|
||||
data: {
|
||||
datasets: [
|
||||
{
|
||||
type: 'scatterWithErrorBars',
|
||||
label: 'South System',
|
||||
data: southData,
|
||||
backgroundColor: 'rgba(255,0,0,0.5)',
|
||||
color: 'rgba(255,0,0,0.5)',
|
||||
borderColor: 'rgba(255,0,0,0.5)',
|
||||
spanGaps: true,
|
||||
},
|
||||
{
|
||||
type: 'line',
|
||||
label: 'South System 7-day average (low)',
|
||||
data: smooth(southData, 'yMin', 7, average),
|
||||
pointRadius: 0,
|
||||
borderColor: 'rgba(255,127,0,0.6)',
|
||||
fill: '+1',
|
||||
backgroundColor: 'rgba(255,127,0,0.2)',
|
||||
},
|
||||
{
|
||||
type: 'line',
|
||||
label: 'South System 7-day average',
|
||||
data: smooth(southData, 'y', 7, average),
|
||||
pointRadius: 0,
|
||||
borderColor: 'rgba(127,63,0,0.9)',
|
||||
backgroundColor: 'rgba(127,63,0,0.9)',
|
||||
},
|
||||
{
|
||||
type: 'line',
|
||||
label: 'South System 7-day average (high)',
|
||||
data: smooth(southData, 'yMax', 7, average),
|
||||
pointRadius: 0,
|
||||
borderColor: 'rgba(255,127,0,0.6)',
|
||||
fill: '-1',
|
||||
backgroundColor: 'rgba(255,127,0,0.2)',
|
||||
},
|
||||
],
|
||||
},
|
||||
options,
|
||||
});
|
||||
document.getElementById('startDate').addEventListener('input', (e) => {
|
||||
options.scales.x.min = e.target.value;
|
||||
northChart.update();
|
||||
southChart.update();
|
||||
});
|
||||
document.getElementById('endDate').addEventListener('input', (e) => {
|
||||
options.scales.x.max = e.target.value;
|
||||
northChart.update();
|
||||
southChart.update();
|
||||
});
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue