Download analytics.js from Hugdrigo/tradescope-latam-intelligence: direct link, hf CLI and curl.
- Browser
- Download file 4.92 kB
-
https://huggingface.co/spaces/Hugdrigo/tradescope-latam-intelligence/resolve/main/analytics.js
- Command line
-
hf download hf://spaces/Hugdrigo/tradescope-latam-intelligence/analytics.js
-
curl -L -o analytics.js https://huggingface.co/spaces/Hugdrigo/tradescope-latam-intelligence/resolve/main/analytics.js
4.92 kB
| export const finite = x => typeof x === 'number' && Number.isFinite(x); | |
| export const safeDiv = (a,b) => finite(a)&&finite(b)&&b!==0 ? a/b : null; | |
| export const validNum = v => typeof v === 'number' && Number.isFinite(v) ? v : null; | |
| export const isAdjacent = (a,b) => b.year===a.year+1; | |
| // Observations are represented as country:year records, missing values remain null. | |
| export function mergeRows(rows){ | |
| const byKey = new Map(); | |
| for(const r of rows){ | |
| if(!r || typeof r.iso!=='string' || !Number.isInteger(r.year) || !['exports','imports','gdp'].includes(r.indicator)) continue; | |
| const key=`${r.iso}:${r.year}`; | |
| if(!byKey.has(key)) byKey.set(key,{iso:r.iso,year:r.year,exports:null,imports:null,gdp:null}); | |
| byKey.get(key)[r.indicator]=validNum(r.value); | |
| } | |
| return byKey; | |
| } | |
| export function calculateRecord(byKey,iso,year){ | |
| const r=byKey.get(`${iso}:${year}`) || {iso,year,exports:null,imports:null,gdp:null}; | |
| const e=r.exports, i=r.imports, g=r.gdp; | |
| const complete=finite(e)&&finite(i); | |
| const old=byKey.get(`${iso}:${year-1}`); | |
| const eprev=old?.exports; | |
| const iprev=old?.imports; | |
| return { | |
| ...r, | |
| balance:complete ? e-i : null, | |
| turnover:complete ? e+i : null, | |
| cover:complete && i>0 ? (e/i)*100 : null, | |
| openness:complete && finite(g) && g>0 ? (e+i)/g*100 : null, | |
| exportGrowth:finite(e)&&finite(eprev)&&eprev>0 ? ((e/eprev)-1)*100 : null, | |
| importGrowth:finite(i)&&finite(iprev)&&iprev>0 ? ((i/iprev)-1)*100 : null | |
| }; | |
| } | |
| export function snapshot(byKey,countries,year){return countries.map(c=>calculateRecord(byKey,c,year));} | |
| export function periodRecords(byKey,iso,start,end){const a=[];for(let y=start;y<=end;y++)a.push(calculateRecord(byKey,iso,y));return a;} | |
| export function stdev(values){ | |
| const a=values.filter(finite); if(a.length<2)return null; | |
| const avg=a.reduce((s,x)=>s+x,0)/a.length; | |
| return Math.sqrt(a.reduce((s,x)=>s+(x-avg)**2,0)/a.length); | |
| } | |
| export function cagr(records,key){ | |
| const a=records.filter(r=>finite(r[key])&&r[key]>0); | |
| if(a.length<2) return null; | |
| const first=a[0],last=a[a.length-1]; | |
| const span=last.year-first.year; | |
| return span>0 ? (Math.pow(last[key]/first[key],1/span)-1)*100 : null; | |
| } | |
| export function volatility(records,key='exportGrowth'){return stdev(records.map(r=>r[key]));} | |
| export function coverage(byKey,countries,start,end){ | |
| const expected=countries.length*(end-start+1); | |
| const out={exports:0,imports:0,gdp:0,paired:0,allThree:0}; | |
| for(const iso of countries){ | |
| for(let y=start;y<=end;y++){ | |
| const r=calculateRecord(byKey,iso,y); | |
| for(const key of ['exports','imports','gdp']) if(finite(r[key]))out[key]++; | |
| if(finite(r.balance))out.paired++; | |
| if(finite(r.openness))out.allThree++; | |
| } | |
| } | |
| return {...out,expected}; | |
| } | |
| export function aggregate(records){ | |
| const paired=records.filter(r=>finite(r.balance)); | |
| const exports=paired.reduce((a,r)=>a+r.exports,0); | |
| const imports=paired.reduce((a,r)=>a+r.imports,0); | |
| const withGdp=paired.filter(r=>finite(r.gdp)&&r.gdp>0); | |
| const tradeGdp=withGdp.reduce((a,r)=>a+r.turnover,0); | |
| const gdp=withGdp.reduce((a,r)=>a+r.gdp,0); | |
| return { | |
| exports:paired.length?exports:null, | |
| imports:paired.length?imports:null, | |
| balance:paired.length?exports-imports:null, | |
| openness:gdp>0?tradeGdp/gdp*100:null, | |
| paired:paired.length, | |
| total:records.length, | |
| gdpPaired:withGdp.length | |
| }; | |
| } | |
| export function shock(record,exportPercent,importPercent){ | |
| if(!finite(record.exports)||!finite(record.imports))return null; | |
| const ex=record.exports*(1+exportPercent/100), im=record.imports*(1+importPercent/100); | |
| return {exports:ex,imports:im,balance:ex-im,delta:(ex-im)-(record.exports-record.imports),cover:im>0?ex/im*100:null}; | |
| } | |
| export function rankAvailable(records,metric){ | |
| return records.filter(r=>finite(r[metric])).sort((a,b)=>b[metric]-a[metric]); | |
| } | |
| export function computeInsights(records,allRecords,start,end){ | |
| const comparable=rankAvailable(records,'balance'); | |
| const trends=allRecords.map(({iso,records:r})=>({iso,cagr:cagr(r,'exports'),vol:volatility(r)})); | |
| const leads=trends.filter(x=>finite(x.cagr)).sort((a,b)=>b.cagr-a.cagr); | |
| const bullets=[]; | |
| if(comparable.length){ | |
| bullets.push({kind:'balance',iso:comparable[0].iso,title:'Highest merchandise balance',value:comparable[0].balance,unit:'usd',year:comparable[0].year}); | |
| bullets.push({kind:'balance',iso:comparable[comparable.length-1].iso,title:'Lowest merchandise balance',value:comparable[comparable.length-1].balance,unit:'usd',year:comparable[comparable.length-1].year}); | |
| } | |
| if(leads.length)bullets.push({kind:'growth',iso:leads[0].iso,title:`Fastest export CAGR (${start}–${end})`,value:leads[0].cagr,unit:'pct',note:'CAGR between first and last positive available observations'}); | |
| return bullets; | |
| } | |
| export function rowsForCsv(byKey,countries,start,end){ | |
| return countries.flatMap(iso=>periodRecords(byKey,iso,start,end)); | |
| } | |