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
 
 
 
 
 
 
 
 
 
 
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 41 most spoken languages cover 80% of all speakers.'], {
          x: 41,
          y: languages[40].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