Spaces:
Restarting
Restarting
import { useRef, useEffect } from 'react' | |
import * as Plot from '@observablehq/plot' | |
const SpeakerPlot = ({ data }) => { | |
const containerRef = useRef() | |
const allSpeakers = data.language_table.reduce( | |
(sum, curr) => sum + curr.speakers, | |
0 | |
) | |
const languages = data.language_table | |
.sort((a, b) => b.speakers - a.speakers) | |
.slice(0, 100) | |
.reduce((acc, d) => { | |
acc.push({ | |
...d, | |
rank: acc.length + 1, | |
cumSpeakers: | |
acc.reduce((sum, curr) => sum + curr.speakers, 0) + d.speakers, | |
cumSpeakersPercent: | |
(acc.reduce((sum, curr) => sum + curr.speakers, 0) + d.speakers) / | |
allSpeakers | |
}) | |
return acc | |
}, []) | |
useEffect(() => { | |
const plot = Plot.plot({ | |
width: 750, | |
height: 500, | |
subtitle: 'Number of languages vs speakers covered', | |
x: { | |
label: 'Languages', | |
ticks: [] | |
}, | |
y: { | |
label: 'Number of Speakers (millions)' | |
}, | |
color: { | |
legend: true, | |
domain: ['Speakers', 'Cumulative Speakers'], | |
range: ['green', 'lightgrey'] | |
}, | |
marks: [ | |
Plot.barY(languages, { | |
x: 'rank', | |
y: d => d.cumSpeakers / 1e6, | |
fill: d => 'Cumulative Speakers', | |
sort: { x: 'y' }, | |
title: d => | |
`The ${ | |
d.rank | |
} most spoken languages cover\n${d.cumSpeakersPercent.toLocaleString( | |
'en-US', | |
{ style: 'percent' } | |
)} of all speakers`, | |
tip: true // {y: d => d.cumSpeakers / 1e6 * 2} | |
}), | |
Plot.barY(languages, { | |
x: 'rank', | |
y: d => d.speakers / 1e6, | |
title: d => | |
`${d.language_name}\n(${d.speakers.toLocaleString('en-US', { | |
notation: 'compact', | |
compactDisplay: 'long' | |
})} speakers)`, | |
tip: true, | |
fill: d => 'Speakers', | |
sort: { x: '-y' } | |
}), | |
Plot.crosshairX(languages, { | |
x: 'rank', | |
y: d => d.cumSpeakers / 1e6, | |
textStrokeOpacity: 0, | |
textFillOpacity: 0 | |
}), | |
Plot.tip(['The 40 most spoken languages cover 80% of all speakers.'], { | |
x: 40, | |
y: languages[39].cumSpeakers / 1e6 | |
}) | |
] | |
}) | |
containerRef.current.append(plot) | |
return () => plot.remove() | |
}, []) | |
return ( | |
<div | |
ref={containerRef} | |
style={{ | |
width: '100%', | |
height: '100%', | |
display: 'flex', | |
alignItems: 'center', | |
justifyContent: 'center' | |
}} | |
/> | |
) | |
} | |
export default SpeakerPlot | |