Public
Edited
Aug 16, 2022
Insert cell
Insert cell
Insert cell
usaGeo = FileAttachment('gz_2010_us_040_00_20m.json').json()
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
extent = d3.extent(unemployment, d => d.rate)
Insert cell
Insert cell
color = d3.scaleSequential()
.domain(extent)
.interpolator(d3.interpolateBlues)
Insert cell
stateColor = d3.scaleOrdinal().domain(stateAbbr).range(d3.schemeTableau10)
Insert cell
Insert cell
Insert cell
stateAbbr = unemploymentState.map(d => d.stateAbbr)
Insert cell
unemploymentState = unemployment.map(d => ({
stateAbbr : stateToAbbr[d.state],
state: d.state,
rate: d.rate}))
Insert cell
Insert cell
Insert cell
gridCartogram
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
unemploymentStateGrid = unemploymentWithGrid.map(d => ({
stateAbbr : stateToAbbr[d.state],
state: d.state,
rate: d.rate,
col: d.col,
row: d.row}))
Insert cell
Insert cell
width =800
Insert cell
height = 400
Insert cell
//Create the margin object
margin = ({top:40,bottom:40,left:40, right:20})
Insert cell
xPointScale = d3.scalePoint().domain(stateAbbr).range([0, width - margin.left - margin.right])
Insert cell
xBandScale = d3.scaleBand().domain(stateAbbr).range([0, width - margin.left - margin.right]).padding(0.1)
Insert cell
visHeight = height - margin.top - margin.bottom
Insert cell
visWidth = width - margin.left -margin.right
Insert cell
yScale = d3.scaleLinear()
.domain([0,6.4])
.nice() // make the domain start and end on round values
// changed from [0, visWidth] to [visHeight, 0]
.range([visHeight, 0])
Insert cell
stateColor(unemploymentState[1].stateAbbr)
Insert cell
Insert cell
Insert cell
//We can observe some of the states have near 0 rate..
unemploymentState.sort((a,b) => d3.ascending(a.stateAbbr, b.stateAbbr))
Insert cell
line = d3.line()
.x(d => xPointScale(d.stateAbbr))
.y(d => yScale(d.rate))
Insert cell
xPointScale('AL')
Insert cell
yScale(2)
Insert cell
line(unemploymentState)
Insert cell
md`## Solution 2:`
Insert cell
Insert cell
Insert cell
scaleRad = d3.scaleLinear().domain([0,6.5]).range([0,20])
Insert cell
bubbleChart = {
//Create the base svg
const svg = d3.create('svg').attr('width',width).attr('height',height)
//Create g-element to position the marks and channels
const g = svg.append('g').attr('transform',`translate(${margin.left},${margin.top})`)

//Create the X-axis g-element and attach the X scale already created
const xAxis = svg.append('g').attr('transform',`translate(${margin.left},${height - margin.bottom})`)
.call(d3.axisBottom(xPointScale))

//Create the Y-axis g-element and attach the Y scale already created
const yAxis = svg.append('g').attr('transform',`translate(${margin.left},${margin.top})`)
.call(d3.axisLeft(yScale))

//create the g-element
const circles = g.selectAll('g')
.data(unemploymentState)
.join('g')
.attr('transform', d => `translate(${xPointScale(d.stateAbbr)},${yScale(d.rate)})`)
circles.append('circle')
.attr('r',d => scaleRad(d.rate))
.attr('fill',d => stateColor(d.stateAbbr))
.attr('id','marks')
.on('mouseover',mouseEnter)
.on('click',circleClicked)
circles.append('text')
.attr('dy',2)
.text(d => d.rate)
.attr('id','rates')
.attr('visibility','hidden')
function circleClicked(event){
const shape = d3.select(this);
const radius = shape.attr('r');
shape
.attr('r',radius/2)
tooltip.attr('visibility','hidden')
}
//Idea of tooltip is creating completely seperate variable and attach the g-element to the main g element
const tooltip = g.append('g')
.attr('visibility', 'hidden');
const tooltipHeight = 16;
// add a rectangle to the tooltip to serve as a background
const tooltipRect = tooltip.append('rect')
.attr('fill', 'black')
.attr('rx', 5)
.attr('height', tooltipHeight);
// add a text element to the tooltip to contain the label
const tooltipText = tooltip.append('text')
.attr('fill', 'white')
.attr('font-family', 'sans-serif')
.attr('font-size', 12)
.attr('y', 2) // offset it from the edge of the rectangle
.attr('x', 3) // offset it from the edge of the rectangle
.attr('dominant-baseline', 'hanging')
function mouseEnter(event, d) {
// make the circle larger
const selection = d3.select(this)
const radius = selection.attr('r')
//d3.select(this)
// .attr('r', radius * 2);
// update the label's text and get its width
tooltipText.text(d.rate);
const labelWidth = tooltipText.node().getComputedTextLength();
// set the width of the tooltip's background rectangle
// to match the width of the label, plus some extra space
tooltipRect.attr('width', labelWidth + 6);
// move the tooltip to the position of the circle (offset by a bit)
// and make the tooltip visible
const xPos = xPointScale(d.stateAbbr) + radius * 3;
const yPos = yScale(d.rate) - tooltipHeight / 2;

tooltip.attr('transform', `translate(${xPos},${yPos})`)
.attr('visibility', 'visible');
}
return svg.node()
}
Insert cell
### Marking with Unconventional SVG symbols
Insert cell
Insert cell
//speedo chart

column = d3.scaleBand()
.domain(d3.range(numCols))
.range([0,visWidth])
Insert cell
rows = d3.scaleBand()
.domain(d3.range(numRows))
.range([0,visHeight])
Insert cell
d3.range(numCols)
Insert cell
rows.bandwidth()
Insert cell
column(5 % numCols)
Insert cell
9 % numRows
Insert cell
speedo = {
const speedChart = d3.create('svg').attr('width',width).attr('height',height)
//Chart Area
const chartArea = speedChart.append('g')
.attr('transform',`translate(${margin.left},${margin.top})`)

//Chart Heading
chartArea.append('text')
.attr('x',visWidth / 2)
.attr('y', -margin.top + 15)
.text('Speedo Chart for Unemployment Rate')
.attr('fill','black')

const angle = d3.scaleLinear().range([0,Math.PI]).domain([0, Math.ceil(extent[1])])
const radius = Math.min(column.bandwidth(), rows.bandwidth()) / 2;
//Creating the cells for the marks with the data
const cells = chartArea.selectAll('g')
.data(unemploymentStateGrid)
.join('g')
//transforming the g-elements to the grid on chart area
.attr('transform',(d,i) =>`translate(${column(d.col) + radius},${rows(d.row) + radius})`)
//radius provides a way to create bounding box for the marks of each state
const stateName = cells
//appending the text to cells
.append('text')
.attr('fill',d => stateColor(d.stateAbbr))
.text(d => d.stateAbbr)
.attr('y',20)
.attr('visibility','visible')

const arc = d3.arc()
.innerRadius(radius - 1)
.outerRadius(radius)
.startAngle(-Math.PI/ 2)
.endAngle(Math.PI/ 2)

cells.append('path')
.attr('d',arc())
.attr('fill',lightgray)

const line = d3.line()

cells.append('path')
.attr('d',d => line([[-radius,0],[radius,0]]))
.attr('fill','none')
.attr('stroke',lightgray)
.attr('stroke-width',1)

//bringing in the sloped line
cells.append('path')
.attr('d', d =>{
const start = [0,0];
const end =[-Math.cos(angle(d.rate)) * radius,
-Math.sin(angle(d.rate)) * radius]
return line([start, end])
})
.attr('fill','none')
.attr('stroke',d => stateColor(d.stateAbbr))
.attr('stroke-linecap','round')
.attr('stroke-width',2)

cells.append('text')
.attr('font-size','12px')
.attr('fill',lightgray)
.attr('x', radius)
.attr('y',10)
.text(Math.ceil(extent[1]))
cells.append('text')
.attr('font-size','12px')
.attr('fill',lightgray)
.attr('x', radius)
.attr('y',10)
.text(Math.ceil(extent[0]))

return speedChart.node()
}
Insert cell
colors = ["black"]
.concat(d3.schemeCategory10)
.concat(d3.schemePaired)
.concat(d3.schemePastel1)
.concat(d3.schemePastel2)
Insert cell
Swatches(d3.scaleOrdinal(stateAbbr,colors, {
columns: "180px"
}))
Insert cell
Legend(d3.scaleOrdinal(stateAbbr, d3.schemeSpectral[10]), {
title: "States",
tickSize: 0,
width : visWidth}
)
Insert cell
stateRateMap = stateToRate['North Dakota']
Insert cell
mapMargin = ({top:0,right:125,left:0,bottom:0})
Insert cell
mapWidth = 725 - mapMargin.right - mapMargin.left
Insert cell
mapHeight = 500 - mapMargin.top - mapMargin.bottom
Insert cell
//Creating the Projection
usaProjection = d3.geoAlbersUsa().fitSize([mapWidth,mapHeight],usaGeo)
Insert cell
exampleProje = d3.geoMercator().fitSize([mapWidth,mapHeight],usaGeo)
Insert cell
usaPath = d3.geoPath().projection(usaProjection)
Insert cell
examplePath = d3.geoPath().projection(exampleProje)
Insert cell
usaPath.centroid(usaGeo.features[1])
Insert cell
examplePath.centroid(usaGeo.features[1])
Insert cell
radCirScale = d3.scaleSqrt()
.domain(extent)
.range([5,12])
Insert cell
Insert cell
//locating the states on Map and the placing the bubbles
albersProjection = {
const albers = d3.create('svg').attr('width', mapWidth + margin.right).attr('height',mapHeight)

const g = albers.append('g').attr('transform',`translate(${margin.left},${margin.top})`)

const states = g.selectAll('path')
.data(usaGeo.features)
.join('path')
.attr('d', d => usaPath(d))
.attr('fill',d => stateColor(stateToAbbr[d.properties.NAME]))
.attr('fill-opacity', 0.7)

const stateRate = g.selectAll('circle')
.data(usaGeo.features)
.join('circle')
.attr('cx',d => usaPath.centroid(d)[0])
.attr('cy',d => usaPath.centroid(d)[1])
.attr('fill', 'steelblue')
.attr('r', d => radCirScale(stateToRate[d.properties.NAME])) //Need to get the rate from the full name...

return albers.node()
}
Insert cell
Insert cell
//locating the states on Map and the placing the bubbles
albersProjection1 = {
const albers = d3.create('svg').attr('width', mapWidth + margin.right).attr('height',mapHeight)

const g = albers.append('g').attr('transform',`translate(${margin.left},${margin.top})`)

const states = g.selectAll('path')
.data(usaGeo.features)
.join('path')
.attr('d', d => usaPath(d))
.attr('fill',d => stateColor(stateToAbbr[d.properties.NAME]))
.attr('fill-opacity', 0.7)

const stateRate = g.selectAll('circle')
.data(usaGeo.features)
.join('circle')
.attr('cx',d => examplePath.centroid(d)[0])
.attr('cy',d => examplePath.centroid(d)[1])
.attr('fill', 'steelblue')
.attr('r', d => radCirScale(stateToRate[d.properties.NAME])) //Need to get the rate from the full name...

return albers.node()
}
Insert cell
unemploymentStateGrid
Insert cell
Insert cell
viewof b = Inputs.select(d3.range(10),{label:"Bins"})
Insert cell
histoRate = d3.bin().thresholds(b)
Insert cell
rateBins = histoRate(unemploymentState.map(d => d.rate))
Insert cell
histoHeight = 200
Insert cell
histoMargin = ({top:20,right:20,left:30,bottom:30})
Insert cell
data =unemploymentState.map(d => d.rate)
Insert cell
count = data.length
Insert cell
// Final mark is Histogram
histoGram = {
const histo = d3.create('svg').attr('width',histoWidth).attr('height',histoHeight)

const chart = histo.append('g')
.attr('transform',`translate(${histoMargin.left},${10})`)

const xAxis = histo.append('g')
.attr('transform',`translate(${histoMargin.right},${180})`)
.call(d3.axisBottom(histoX))

const svg = chart.selectAll('rect')
.data(rateBins)
.join('rect')
.attr('fill', d => binColor(d.x0))
.attr('x', d => histoX(d.x0))
.attr('width', d => Math.max(0, histoX(d.x1) - histoX(d.x0) - 2))
.attr('y', d => histoY(d.length))
.attr('height', d => histoY(0) - histoY(d.length))
const labels = chart
.append("g")
.selectAll("text")
.data(rateBins)
.join("text")
.attr("x", d => ((histoX(d.x0) + histoX(d.x1)) / 2) + 5 | 0)
.attr("y", d => histoY(d.length) - 2)
.style("fill", "black")
.style("font-size", 10)
.style("text-anchor", "middle");
labels.text(d => format(d.length / count));
return histo.node()
}
Insert cell
maxBins = d3.max(rateBins, d => d.length)
Insert cell
histoY = d3.scaleLinear().domain([0,maxBins]).range([histoHeight - histoMargin.bottom, histoMargin.top])
Insert cell
histoY(0)
Insert cell
hisWidth = histoWidth - histoMargin.left
Insert cell
histoX = d3.scaleLinear()
.domain([0, 10])
.range([30, hisWidth])
.clamp(false)
Insert cell
histoWidth = 400
Insert cell
frequency = d3.scaleLinear()
.domain([0, maxBins / count])
.nice()
.range([height - margin.bottom, margin.top])
Insert cell
binColor = d3.scaleThreshold()
.domain(rateBins.map(d =>d.x0))
.range(colors)
Insert cell
Insert cell
unemploymentState[0]
Insert cell
Insert cell
Insert cell
Insert cell
import {Legend, Swatches} from "@d3/color-legend"
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