Adjust shadow band for trend-like indicators
This commit is contained in:
@@ -721,17 +721,33 @@ function quantile(sorted, q) {{
|
|||||||
if (lo === hi) return sorted[lo];
|
if (lo === hi) return sorted[lo];
|
||||||
return sorted[lo] + (sorted[hi] - sorted[lo]) * (pos - lo);
|
return sorted[lo] + (sorted[hi] - sorted[lo]) * (pos - lo);
|
||||||
}}
|
}}
|
||||||
function robustAnnualSeasonValues(points) {{
|
function annualMedianValues(points) {{
|
||||||
const byYear = new Map();
|
const byYear = new Map();
|
||||||
for (const p of points) {{
|
for (const p of points) {{
|
||||||
if (!byYear.has(p.year)) byYear.set(p.year, []);
|
if (!byYear.has(p.year)) byYear.set(p.year, []);
|
||||||
byYear.get(p.year).push(p.value);
|
byYear.get(p.year).push(p.value);
|
||||||
}}
|
}}
|
||||||
const annual = [...byYear.values()].map(values => {{
|
return [...byYear.entries()].map(([year, values]) => {{
|
||||||
const sorted = values.filter(Number.isFinite).sort((a,b)=>a-b);
|
const sorted = values.filter(Number.isFinite).sort((a,b)=>a-b);
|
||||||
return quantile(sorted, 0.50);
|
return {{year:Number(year), value:quantile(sorted, 0.50)}};
|
||||||
}}).filter(Number.isFinite).sort((a,b)=>a-b);
|
}}).filter(x => Number.isFinite(x.value)).sort((a,b)=>a.year-b.year);
|
||||||
|
}}
|
||||||
|
function trendLikeAnnualLevel(points) {{
|
||||||
|
const annual = annualMedianValues(points);
|
||||||
|
if (annual.length < 4) return false;
|
||||||
|
const xs = annual.map(x=>x.year);
|
||||||
|
const ys = annual.map(x=>x.value);
|
||||||
|
const mx = mean(xs), my = mean(ys);
|
||||||
|
const cov = xs.reduce((s,x,i)=>s+(x-mx)*(ys[i]-my),0);
|
||||||
|
const vx = xs.reduce((s,x)=>s+(x-mx)*(x-mx),0);
|
||||||
|
const vy = ys.reduce((s,y)=>s+(y-my)*(y-my),0);
|
||||||
|
const corr = vx > 0 && vy > 0 ? cov / Math.sqrt(vx * vy) : 0;
|
||||||
|
return Math.abs(corr) >= 0.70;
|
||||||
|
}}
|
||||||
|
function robustAnnualSeasonValues(points, useRobust=true) {{
|
||||||
|
const annual = annualMedianValues(points).map(x=>x.value).sort((a,b)=>a-b);
|
||||||
if (annual.length < 4) return annual;
|
if (annual.length < 4) return annual;
|
||||||
|
if (!useRobust) return annual;
|
||||||
const center = quantile(annual, 0.50);
|
const center = quantile(annual, 0.50);
|
||||||
const keepCount = Math.max(3, Math.ceil(annual.length * 0.65));
|
const keepCount = Math.max(3, Math.ceil(annual.length * 0.65));
|
||||||
return annual
|
return annual
|
||||||
@@ -777,6 +793,8 @@ function buildNormalizedChart(years) {{
|
|||||||
}});
|
}});
|
||||||
const rawValues = rawPoints.map(p=>p.value);
|
const rawValues = rawPoints.map(p=>p.value);
|
||||||
if (!rawValues.length) return {{years:{{}}, band:[]}};
|
if (!rawValues.length) return {{years:{{}}, band:[]}};
|
||||||
|
const bandUniverse = rawPoints.filter(p => Number(p.year) >= params.startYear && Number(p.year) < params.excludeYear);
|
||||||
|
const trendLike = trendLikeAnnualLevel(bandUniverse);
|
||||||
const m = mean(rawValues);
|
const m = mean(rawValues);
|
||||||
let s = std(rawValues);
|
let s = std(rawValues);
|
||||||
if (!Number.isFinite(s) || s === 0) s = 1;
|
if (!Number.isFinite(s) || s === 0) s = 1;
|
||||||
@@ -788,7 +806,7 @@ function buildNormalizedChart(years) {{
|
|||||||
}});
|
}});
|
||||||
const band = [];
|
const band = [];
|
||||||
for (let day=1; day<=366; day++) {{
|
for (let day=1; day<=366; day++) {{
|
||||||
const samples = robustAnnualSeasonValues(rawPoints.filter(p => Number(p.year) >= params.startYear && Number(p.year) < params.excludeYear && circularDiff(p.day, day) <= params.window));
|
const samples = robustAnnualSeasonValues(bandUniverse.filter(p => circularDiff(p.day, day) <= params.window), !trendLike);
|
||||||
if (samples.length < 3) continue;
|
if (samples.length < 3) continue;
|
||||||
const tail = Math.max(0.02, Math.min(0.45, params.trim || 0.10));
|
const tail = Math.max(0.02, Math.min(0.45, params.trim || 0.10));
|
||||||
const mid = quantile(samples, 0.50);
|
const mid = quantile(samples, 0.50);
|
||||||
|
|||||||
+24
-6
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user