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];
|
||||
return sorted[lo] + (sorted[hi] - sorted[lo]) * (pos - lo);
|
||||
}}
|
||||
function robustAnnualSeasonValues(points) {{
|
||||
function annualMedianValues(points) {{
|
||||
const byYear = new Map();
|
||||
for (const p of points) {{
|
||||
if (!byYear.has(p.year)) byYear.set(p.year, []);
|
||||
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);
|
||||
return quantile(sorted, 0.50);
|
||||
}}).filter(Number.isFinite).sort((a,b)=>a-b);
|
||||
return {{year:Number(year), value:quantile(sorted, 0.50)}};
|
||||
}}).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 (!useRobust) return annual;
|
||||
const center = quantile(annual, 0.50);
|
||||
const keepCount = Math.max(3, Math.ceil(annual.length * 0.65));
|
||||
return annual
|
||||
@@ -777,6 +793,8 @@ function buildNormalizedChart(years) {{
|
||||
}});
|
||||
const rawValues = rawPoints.map(p=>p.value);
|
||||
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);
|
||||
let s = std(rawValues);
|
||||
if (!Number.isFinite(s) || s === 0) s = 1;
|
||||
@@ -788,7 +806,7 @@ function buildNormalizedChart(years) {{
|
||||
}});
|
||||
const band = [];
|
||||
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;
|
||||
const tail = Math.max(0.02, Math.min(0.45, params.trim || 0.10));
|
||||
const mid = quantile(samples, 0.50);
|
||||
|
||||
Reference in New Issue
Block a user