Public
Edited
Mar 10, 2023
Insert cell
Type Markdown, then Shift-Enter. Ctrl-space for more options. Arrow ↑/↓ to switch modes.

Insert cell
md`# Pokemon`
Insert cell
chart = {
const height = width * 9/16
const margin = 15
const svg = d3.select(DOM.svg(width, height))
const r = 15
const imageSize = r * 2
const strokeWidth = 2
const xScale = d3.scaleLinear()
.range([margin, width - margin])
.domain(d3.extent(pokemonBaseStats.map(d => d.x)))
const yScale = d3.scaleLinear()
.range([margin, height - margin])
.domain(d3.extent(pokemonBaseStats.map(d => -d.y)))
const rScale = d3.scaleLog()
.range([0, r])
.domain(d3.extent(pokemonBaseStats.map(d => d.base_total)))
const data = pokemonBaseStats.map(d => ({...d, x: xScale(d.x), y: yScale(-d.y), xtsne: xScale(d.x), ytsne: yScale(-d.y)}))
const g = svg.selectAll('g')
// .data(data.slice(0, 50))
.data(data)
.enter()
.append('g')
.attr('transform', d => `translate(${xScale(d.x)}, ${yScale(d.y)})`)
const title = g
.append('title')
.text(d => `Name: ${d.name}(${d.pokedex_number})
Attack: ${d.attack}
Defense: ${d.defense}
S.Att: ${d.sp_attack}
S.Def: ${d.sp_defense}
Speed: ${d.speed}
HP: ${d.hp}
Base: ${d.base_total}`)
const pokeballs = g
.append('use')
.attr('href', d => +d.is_legendary ? '#ultraball' : '#pokeball')
.attr('transform', d => `scale(${rScale(d.base_total) / 50})`)
.attr('opacity', 0.7)
const images = g
.append('image')
.attr('href', d => getPokemonPNG(d.pokedex_number))
.attr('height', d => rScale(d.base_total) * 2)
.attr('width', d => rScale(d.base_total) * 2)
const strengthScale = d3.scaleLog()
.range([0.1, 0.1])
.domain([0, d3.max(data, d => d.base_total)])
let tick = 0
const simulation = d3.forceSimulation(data)
.force('collide', d3.forceCollide().radius(d => rScale(d.base_total)))
.force('x', d3.forceX().x(d => d.xtsne))
.force('y', d3.forceY().y(d => d.ytsne))
.on('tick', () => {
data.forEach(d => {
const r = rScale(d.base_total)
d.x = Math.max(r + margin, Math.min(width - margin - r, d.x))
d.y = Math.max(r + margin, Math.min(height - margin - r, d.y))
})
g.attr('transform', d => `translate(${d.x}, ${d.y})`)
tick++
if (tick > 100)
simulation.stop()
})
return svg.node()
}
Insert cell
md`## Radar chart version`
Insert cell
chart2 = {
const r = 30
const height = 600
const svg = d3.select(DOM.svg(width, height))
const margin = { left: 30, top: 10, right: 10, bottom: 20 }
const xScale = d3.scaleLinear()
.range([margin.left + r, width - margin.right - r])
.domain(d3.extent(pokemonBaseStats.map(d => d.x)))
const yScale = d3.scaleLinear()
.range([height - margin.bottom - r, margin.top + r])
.domain(d3.extent(pokemonBaseStats.map(d => d.y)))
const radialLine = d3.lineRadial()
const radialScale = d3.scaleLinear()
.range([0, r])
.domain([0, 255])
svg.selectAll('circle')
.data(pokemonBaseStats)
.enter()
// For each Pokemon
.each(function (d) {
d3.select(this)
.append('g')
.selectAll('path')
.data([d])
.enter()
.append('path')
.attr('d', d => radialLine([
d.hp,
d.attack,
d.sp_attack,
d.defense,
d.sp_defense,
d.speed,
d.hp
].map((v, i) => [Math.PI * i / 3, radialScale(v)])) )
.attr('transform', `translate(${xScale(d.x)}, ${yScale(d.y)})`)
.attr('stroke', 'black')
.attr('stroke-width', d => d.is_legendary ? 3 : 2)
.attr('fill', d => d.is_legendary ? 'DarkGoldenRod' : 'Crimson')
.attr('opacity', 0.4)
})

return svg.node()
}
Insert cell
md`## Pokeballs in SVG`
Insert cell
pokemonBaseStats = rawData.map(d => _.mapValues(d, s => isNaN(parseFloat(s)) ? s : parseFloat(s)))
Insert cell
rawData = await d3.csv('https://cdn.statically.io/gh/leoyuholo/100vis/9929be6a/20190122_pokemon-base-stats-t-sne/pokemon_tsne.csv')
Insert cell
getPokemonPNG = (pokedex) => `https://rawcdn.githack.com/PokeAPI/sprites/f33025ee68fb6a90e5e005902865dd7ba82716e0/sprites/pokemon/${pokedex}.png`
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more