Published unlisted
Edited
Sep 9, 2020
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
cars = FileAttachment("cars.json").json()
Insert cell
Insert cell
mpg_extent = d3.extent(cars, d => d.Miles_per_Gallon)
Insert cell
displacement_extents = d3.extent(cars, d => d.Displacement)
Insert cell
Insert cell
x_scale = d3.scaleLinear().domain(mpg_extent).range([0,300])
Insert cell
y_scale = d3.scaleLinear().domain(displacement_extents).range([300,0])
Insert cell
Insert cell
{
let svg = d3.create('svg').attr('width', 400).attr('height', 400)
// center our plot, leaving some padding on the sides
let g = svg.append('g') // create just a single group element
.attr('transform', 'translate(50,50)')
// create a rectangle, to frame our plot
g.append('rect')
.attr('width', 300)
.attr('height', 300)
.attr('fill', 'none')
.attr('stroke', d3.hcl(0,0,30))
// the data join!
g.selectAll('circle') // empty: no circles exist
.data(cars) // our car data
.enter() // grab the enter selection - placeholder elements, one for each car
.append('circle') // replace placeholders with circles
.attr('cx', d => x_scale(d.Miles_per_Gallon)) // for a car, get its MPG, apply the scale -> x coordinate
.attr('cy', d => y_scale(d.Displacement)) // likewise for the y coordinate
.attr('r', 4) // radius does not encode data, so fix this to some amount - not too big, not too small
.attr('fill', d3.hcl(120,20,70)) // provide a nice fill
.attr('stroke', d3.hcl(0,0,20)) // a subtle stroke, to make the circles "pop"
.attr('stroke-width', .5)
return svg.node()
}
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
point_scale = d3.scalePoint()
.domain(ps_ordinal_domain)
.range(quantitative_range)
.padding(ps_padding)
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
band_scale = d3.scaleBand()
.domain(bs_ordinal_domain)
.range(quantitative_range)
.paddingInner(bs_paddingInner)
.paddingOuter(bs_paddingOuter)
Insert cell
Insert cell
Insert cell
{
let svg = d3.create('svg').attr('width', 300).attr('height', 400)
let g = svg.append('g')
.attr('transform', 'translate(25,25)')
let ordinal_values = ['A','B','C','D']
let makeup_data = ordinal_values.map(d => { return {O:d,Q:Math.random()}})
let x_scale = d3.scaleBand()
.domain(ordinal_values)
.range([0,250])
.paddingInner(0.2)
let height_scale = d3.scaleLinear()
.domain([0,d3.max(makeup_data, d => d.Q)])
.range([0,350])
g.append('rect')
.attr('width', 250).attr('height', 350)
.attr('fill', 'none').attr('stroke', d3.hcl(0,0,45))
g.selectAll('bar')
.data(makeup_data)
.enter()
.append('rect')
.attr('x', d => x_scale(d.O)) // use the band scale to return a quantitative value the ordinal value
.attr('y', d => 350-height_scale(d.Q)) // hmm, why is this being done?
.attr('width', x_scale.bandwidth()) // use the band scale's bandwidth to determine each bar's width
.attr('height', d => height_scale(d.Q)) // encode quantitative value with bar height
.attr('fill', d3.hcl(120,30,66)).attr('stroke', d3.hcl(0,0,30)).attr('stroke-width', .4)
return svg.node()
}
Insert cell
Insert cell
Insert cell
Insert cell
full_scatterplot = {
let svg = d3.create('svg').attr('width', 400).attr('height', 400)
let g = svg.append('g')
.attr('transform', 'translate(50,50)')
g.append('g')
.attr('id', 'scatter') // this is to distinguish our scatterplot below...
.selectAll('circle')
.data(cars)
.enter()
.append('circle')
.attr('cx', d => x_scale(d.Miles_per_Gallon))
.attr('cy', d => y_scale(d.Displacement))
.attr('r', 4)
.attr('fill', d3.hcl(120,30,66))
.attr('stroke', d3.hcl(0,0,20))
.attr('stroke-width', .5)
g.append('g').lower()
.attr('class', 'axis y') // useful for uniquely identifying our axes!
.call(d3.axisLeft(y_scale)) // create an axis for our y scale, on the left side of our plot
g.append('g').lower()
.attr('class', 'axis x') // useful for uniquely identifying our axes!
.attr('transform', `translate(0,${y_scale.range()[0]})`) // translate down for the x scale
.call(d3.axisBottom(x_scale)) // create an axis for the x scale, on the bottom

//d3.axisBottom(x_scale)(g.select('.x'))
return svg.node()
}
Insert cell
Insert cell
viewof full_scatterplot_vis = drawdom(full_scatterplot,10)
Insert cell
full_scatterplot_vis
Insert cell
{
let axis_tweak = (axis_g) => {
axis_g.selectAll('path').attr('stroke', d3.hcl(0,0,70))
axis_g.selectAll('.tick').selectAll('line').attr('stroke', d3.hcl(0,0,45))
axis_g.selectAll('.tick').selectAll('text').attr('fill', d3.hcl(0,0,45))
}
// d3.select(full_scatterplot).selectAll('.axis').call(axis_tweak)
}
Insert cell
Insert cell
{
// d3.select(full_scatterplot).selectAll('.x').selectAll('.tick')
// .append('line') // vertical guides
// .attr('y1', -y_scale.range()[0]).attr('y2', y_scale.range()[1]) // use scale to position guides
// .attr('stroke-width', .7).attr('stroke', d3.hcl(0,0,90))
// d3.select(full_scatterplot).select('.y').selectAll('.tick')
// .append('line') // horizontal guides
// .attr('x1', x_scale.range()[0]).attr('x2', x_scale.range()[1]) // use scale to position guides
// .attr('stroke-width', .7).attr('stroke', d3.hcl(0,0,90))
}
Insert cell
Insert cell
Insert cell
{
// d3.select(full_scatterplot)
// .selectAll('.x')
// .append('text')
// .text('Miles Per Gallon')
// .attr('transform', 'translate(150,30)')
// .attr('text-anchor', 'middle')
// .attr('fill', d3.hcl(0,0,30))
// .attr('font-weight', 'bold')
// d3.select(full_scatterplot)
// .selectAll('.y')
// .append('text')
// .text('Displacement')
// .attr('transform', 'translate(-34,150) rotate(270)')
// .attr('text-anchor', 'middle')
// .attr('fill', d3.hcl(0,0,30))
// .attr('font-weight', 'bold')
}
Insert cell
Insert cell
Insert cell
Insert cell
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