File size: 2,642 Bytes
b54f543
 
 
 
 
a9e6b9b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b54f543
 
 
 
 
a9e6b9b
b54f543
 
a9e6b9b
b54f543
 
a9e6b9b
b54f543
 
 
a9e6b9b
 
b54f543
 
a9e6b9b
 
b54f543
a9e6b9b
b54f543
a9e6b9b
 
 
 
 
 
 
b54f543
 
a9e6b9b
 
b54f543
a9e6b9b
 
 
 
 
b54f543
a9e6b9b
b54f543
 
a9e6b9b
 
 
 
 
 
260c1a3
 
 
a9e6b9b
 
b54f543
 
 
 
 
 
 
 
 
 
 
 
 
a9e6b9b
b54f543
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
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