d3.csv('text-editing-methods-speed.csv') .then(data => data.map(d => { d['Average Speed for adult [wpm]'] = Number( d['Average Speed for adult [wpm]'] ) d['Lower bound [wpm]'] = Number(d['Lower bound [wpm]']) d['Upper bound [wpm]'] = Number(d['Upper bound [wpm]']) return d }) ) .then(data => data.filter(d => d['Input Category'] !== 'reading')) .then(data => data.sort( (a, b) => a['Average Speed for adult [wpm]'] - b['Average Speed for adult [wpm]'] ) ) .then(data => draw({ data })) function draw({ data }) { console.log('data', data) const outerWidth = 960 const outerHeight = 500 const margin = { left: 100, top: 10, right: 50, bottom: 75 } // colors const themeDarkGray = '#444444' const themeBookEmojiGray = '#dfdfe8' const blogBlue = '#367da2' const blogGray = '#eaeaea' const blogDarkGray = '#333333' const selector = 'body' d3.select(selector) .append('div') .attr('id', 'vis') .append('svg') .attr('width', outerWidth) .attr('height', outerHeight) const parent = document.getElementById('vis') const svg = d3.select(parent).select('svg') // background svg .append('rect') .attr('x', 0) .attr('y', 0) .attr('width', outerWidth) .attr('height', outerHeight) .style('fill', blogGray) const g = svg .append('g') .attr('transform', `translate(${margin.left},${margin.top})`) const innerWidth = outerWidth - margin.left - margin.right const innerHeight = outerHeight - margin.top - margin.bottom const meanVariable = 'Average Speed for adult [wpm]' const minVariable = 'Lower bound [wpm]' const maxVariable = 'Upper bound [wpm]' const yVariable = 'emoji' const xAxisLabelText = 'input speed in words per minute (more is faster)' const xAxisLabelOffset = 50 console.log('width', outerWidth) console.log('height', outerHeight) const xAxisG = g .append('g') .attr('class', 'x axis') .attr('transform', `translate(0,${innerHeight})`) const xAxisLabel = xAxisG .append('text') .style('text-anchor', 'middle') .style('fill', themeDarkGray) .style('font-size', 20) .attr('x', innerWidth / 2) .attr('y', xAxisLabelOffset) .attr('class', 'label') .text(xAxisLabelText) const yAxisG = g.append('g').attr('class', 'y axis') const xScale = d3 .scaleLinear() .domain([0, d3.max(data, d => d[maxVariable])]) .range([0, innerWidth]) const yScale = d3 .scaleBand() .domain(data.map(d => d[yVariable]).reverse()) .range([innerHeight, 0]) const xAxis = d3.axisBottom(xScale).ticks(5) const yAxis = d3.axisLeft(yScale) xAxisG.call(xAxis) yAxisG.call(yAxis) const yOffset = yScale.bandwidth() / 2 const lines = g.selectAll('.line').data(data) lines .enter() .append('line') .attr('class', '.line') .attr('x1', d => xScale(d[minVariable])) .attr('x2', d => xScale(d[maxVariable])) .attr('y1', d => yScale(d[yVariable]) + yOffset) .attr('y2', d => yScale(d[yVariable]) + yOffset) .style('stroke', blogBlue) .style('stroke-width', '2px') const radius = 5 const circles = g.selectAll('.circle').data(data) circles .enter() .append('circle') .attr('class', '.circle') .attr('cx', d => xScale(d[meanVariable])) .attr('cy', d => yScale(d[yVariable]) + yOffset) .attr('r', radius) .style('fill', blogBlue) }