International Talent in the U.S.

F-1 enrollment trends & H-1B sponsorship dynamics · 2011–2017

+ value + 'K', }, }, }, }), }, ); const growth = yearly.map((d, i) => { if (i === 0) return { f1: null, h1b: null }; const prev = yearly[i - 1]; return { f1: +( ((d.f1_total - prev.f1_total) / prev.f1_total) * 100 ).toFixed(1), h1b: +( ((d.certified - prev.certified) / prev.certified) * 100 ).toFixed(1), }; }); if (charts.growth) charts.growth.destroy(); charts.growth = new Chart( document.getElementById('growthChart'), { type: 'line', data: { labels: years.slice(1), datasets: [ { label: 'F-1 % change', data: growth.slice(1).map((d) => d.f1), borderColor: '#1D9E75', tension: 0.3, pointRadius: 3, }, { label: 'H-1B certified % change', data: growth.slice(1).map((d) => d.h1b), borderColor: '#7F77DD', borderDash: [5, 3], tension: 0.3, pointRadius: 3, }, ], }, options: chartOptions({ scales: { y: { ticks: { callback: (value) => value + '%', }, }, }, }), }, ); } function initTopStates() { if (charts.states) charts.states.destroy(); charts.states = new Chart( document.getElementById('statesChart'), { type: 'bar', data: { labels: top10.map((d) => d.state), datasets: [ { label: 'Certified', data: top10.map((d) => d.certified), backgroundColor: '#378ADD', }, { label: 'Denied', data: top10.map((d) => d.denied), backgroundColor: '#E24B4A', }, ], }, options: chartOptions({ indexAxis: 'y', scales: { x: { stacked: true, ticks: { callback: (value) => fmt(value), }, }, y: { stacked: true, }, }, }), }, ); if (charts.wage) charts.wage.destroy(); charts.wage = new Chart( document.getElementById('wageChart'), { type: 'bar', data: { labels: top10.map((d) => d.state), datasets: [ { label: 'Average wage', data: top10.map((d) => d.wage), backgroundColor: '#378ADD', }, ], }, options: chartOptions({ scales: { y: { ticks: { callback: (value) => ' + fmt(value), }, }, }, }), }, ); } window.mapLoaded = false; function loadMap() { window.mapLoaded = true; d3.json( 'https://cdn.jsdelivr.net/npm/us-atlas@3/states-10m.json', ).then((us) => { window.usTopoData = us; renderMap(); }); } function renderMap() { if (!window.usTopoData) return; const metric = document.getElementById('mapMetric').value; const svg = d3.select('#mapSvg'); svg.selectAll('*').remove(); const vals = Object.values(stateData2017) .map((d) => d[metric]) .filter((v) => !isNaN(v)); const colorScheme = metric === 'rate' ? d3.schemeGreens[6] : metric === 'wage' ? d3.schemeOranges[6] : d3.schemeBlues[6]; const color = d3.scaleQuantize( [Math.min(...vals), Math.max(...vals)], colorScheme, ); const projection = d3 .geoAlbersUsa() .scale(1100) .translate([480, 300]); const path = d3.geoPath(projection); const tip = document.getElementById('mapTip'); const container = document.querySelector( '.map-container', ); svg .selectAll('path') .data( topojson.feature( window.usTopoData, window.usTopoData.objects.states, ).features, ) .join('path') .attr('d', path) .attr('stroke', '#ffffff') .attr('fill', (d) => { const id = String(d.id).padStart(2, '0'); const name = stateNames[id]; const abbr = stateAbbr[name]; const info = stateData2017[abbr]; return info ? color(info[metric]) : '#ddd'; }) .on('mousemove', function (event, d) { const id = String(d.id).padStart(2, '0'); const name = stateNames[id]; const abbr = stateAbbr[name]; const info = stateData2017[abbr]; if (!info) { tip.style.display = 'none'; return; } const rect = container.getBoundingClientRect(); tip.style.display = 'block'; tip.style.left = Math.min( event.clientX - rect.left + 10, rect.width - 190, ) + 'px'; tip.style.top = Math.max(event.clientY - rect.top - 10, 0) + 'px'; tip.innerHTML = ` ${name} Petitions: ${info.total.toLocaleString()}
Certified: ${info.certified.toLocaleString()}
Denied: ${info.denied.toLocaleString()}
Approval rate: ${info.rate}%
Avg wage: ${info.wage.toLocaleString()} `; }) .on('mouseleave', () => { tip.style.display = 'none'; }); document.getElementById('mapLegendBar').innerHTML = colorScheme .map( (c) => ``, ) .join(''); document.getElementById('mapLegendLabel').textContent = metric === 'total' ? 'Petition volume' : metric === 'rate' ? 'Approval rate' : 'Average wage'; } window.showTab = showTab; window.updateOverview = updateOverview; window.renderMap = renderMap; loadDataFromGitHub().then(() => { buildYearFilter(); updateOverview(); initOverviewChart(); initTrendsCharts(); initTopStates(); });