File size: 3,151 Bytes
867080c
 
b4a0c57
7cdde63
 
 
 
 
 
 
92d8154
 
 
7cdde63
 
 
3680a5f
 
92d8154
 
 
7cdde63
 
 
3680a5f
 
 
 
71ab1e9
3680a5f
7cdde63
71ab1e9
92b2164
 
7c06aef
867080c
00e463a
b4a0c57
 
00e463a
867080c
00e463a
867080c
b4a0c57
867080c
92d8154
 
 
 
 
7c06aef
 
 
 
 
 
 
 
 
 
 
 
 
 
867080c
7c06aef
941d5c5
 
867080c
 
92d8154
 
 
00e463a
867080c
00e463a
 
abd65a6
 
00e463a
 
abd65a6
 
7cdde63
 
 
00e463a
867080c
 
 
e8341d2
867080c
7cdde63
 
 
 
 
 
 
 
92d8154
7cdde63
 
 
867080c
b4a0c57
867080c
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
102
103
104
105
106
107
108
import { useRef, useEffect, useState } from 'react'
import * as Plot from '@observablehq/plot'

const smoothProgressBar = fraction => {
  const blocks = ['▏', 'β–Ž', '▍', 'β–Œ', 'β–‹', 'β–Š', 'β–‰', 'β–ˆ']
  const width = 10
  const totalUnits = width * 8
  const filledUnits = Math.round(fraction * totalUnits)
  const fullBlocks = Math.floor(filledUnits / 8)
  const remainder = filledUnits % 8
  return (
    'β–ˆ'.repeat(fullBlocks) + (remainder > 0 ? blocks[remainder - 1] : '') || '▏'
  )
}

const makeTitle = data => d => {
  const cData = data[d.properties?.ISO_A2_EH]
  const languages = cData?.languages.toSorted(
    (a, b) => b.population - a.population
  )
  const pop = languages?.map(a => a.population).reduce((prev, a) => prev + a, 0)
  const langstring =
    languages
      ?.slice(0, 10)
      .map(
        a =>
          `${smoothProgressBar(a.population / pop)} ${
            a.name
          } – ${a.score === null || a.score === undefined ? "n/a" : a.score.toFixed(2)}`
      )
      .join('\n\n') + (languages?.length > 10 ? `\n\n...` : '')
  return `${d.properties.ADMIN} – ${cData?.score === null || cData?.score === undefined ? "n/a" : cData.score.toFixed(2)}\n\n${langstring}`
}

const WorldMap = ({ data, width = 750, height = 500, allLanguages = [] }) => {
  const containerRef = useRef()
  const [mapData, setMapData] = useState()

  useEffect(() => {
    fetch('/world.geo.json')
      .then(res => res.json())
      .then(setMapData)
  }, [])

  useEffect(() => {
    if (mapData === undefined || data === undefined) return
    const countriesDict = data.reduce((acc, country) => {
      acc[country.iso2] = country
      return acc
    }, {})
    // Count languages that have any evaluation data
    const evaluatedLanguagesCount = allLanguages.filter(lang => {
      const hasAnyScores = [
        'translation_from_bleu',
        'translation_to_bleu', 
        'classification_accuracy',
        'mmlu_accuracy',
        'arc_accuracy',
        'truthfulqa_accuracy',
        'mgsm_accuracy'
      ].some(metric => lang[metric] !== null && lang[metric] !== undefined)
      return hasAnyScores
    }).length

    const plot = Plot.plot({
      subtitle: `Language Proficiency Score by Country (Coverage: ~${evaluatedLanguagesCount} languages evaluated)`,
      width: width,
      height: height,
      projection: 'equal-earth',
      marks: [
        Plot.geo(mapData, {
          fill: d => countriesDict[d.properties?.ISO_A2_EH]?.score,
          title: makeTitle(countriesDict),
          tip: true
        })
      ],
      color: {
        scheme: 'RdYlGn',
        unknown: '#d0d0d0',
        label: 'Score',
        legend: true,
        domain: [0, 1],
        pivot: 0.5
      },
      style: {
        fontFamily: 'monospace'
      }
    })
    containerRef.current.append(plot)
    return () => plot.remove()
  }, [mapData, data, width, height])

  return (
    <div
      ref={containerRef}
      style={{
        width: '100%',
        height: '100%',
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'center'
      }}
    />
  )
}

export default WorldMap