console.clear() d3.select('body').selectAppend('div.tooltip') var fields = [ { name: 'Education', key: 'edu', types: ["no_HS", "HS", "HS_plus", "Bachelors", "Bachelors Plus"] }, { name: 'Race', key: 'race', types: ["white", "other", "hispanic", "black", "asian"] }, { name: 'Standard', key: 'standard', types: ["0", "11799.38", "13755.42", "16026.95", "17604.41", "19181.87", "20759.33"] }, { name: 'xtot (household size?)', key: 'xtot', types: ["2", "3", "4", '5', "6", "7", "8", "9", "10", "11"] }, ] var bucketDomain = [-1500, -500, 500, 1500] var bucketRange = [-1501, -501, 0, 501, 1501] var colors = ["#e66101","#fdb863","#aaa","#b2abd2","#5e3c99"] .map(d3.color) .map(d => ('' + d).replace('rgb(', 'rgba(').replace(')', ',.2')) var color = d3.scaleThreshold() .domain(bucketDomain) .range(colors) var changeToBucket = d3.scaleThreshold() .domain(bucketDomain) .range(bucketRange) fields.forEach((d, i) => { d.typeData = d.types.map((d, i) => { var type = d var buckets = bucketRange.map(bucket => { var boxColor = color(bucket).replace('.2', '1') return {boxColor, bucket, i} }) bucketsObject = _.keyBy(buckets, d => d.bucket) return {type, buckets, bucketsObject, i} }) }) d3.loadData('test-data.csv', (err, res) => { data = res[0] data.forEach(d => { d.expanded_income = +d.expanded_income d.combinedTaxChg = +d.combinedTaxChg d.bucket = changeToBucket(d.combinedTaxChg) }) var sel = d3.select('.graph').html('') c = d3.conventions({sel, margin: {left: 30}, layers: 'sc'}) var {layers: [svg, ctx], x, y} = c x.domain(d3.extent(data, d => d.expanded_income)) x.domain([40000, 200000]) y.domain(d3.extent(data, d => d.combinedTaxChg)) d3.drawAxis(c) var fieldSel = d3.select('.filters').html('') .appendMany('div', fields) .st({display: 'inline-block', width: 500}) fieldSel.append('h3').text(d => d.name).st({marginLeft: 200}) fieldSel.each(function(field){ var sel = d3.select(this) var width = 500 var height = 100 var svg = sel.append('svg') .at({width, height}) field.typeSel = svg.appendMany('g', field.typeData) .translate(d => [200, d.i*20]) .on('click', d => { field.active = field.active == d.type ? '' : d.type console.log(field.active) drawData() }) field.typeSel.append('text').text(d => d.type) .at({x: -5, textAnchor: 'end', dy: '.33em'}) field.rectSel = field.typeSel.appendMany('rect', d => d.buckets) .at({height: 15, width: 200, fill: d => d.boxColor, y: -7}) }) function fieldFilter(field, type){ } drawData() function drawData(){ var curData = data.filter(d => { if (fields[0].active && d[fields[0].key] != fields[0].active) return false if (fields[1].active && d[fields[1].key] != fields[1].active) return false if (fields[2].active && d[fields[2].key] != fields[2].active) return false if (fields[3].active && d[fields[3].key] != fields[3].active) return false return true }) var widthScale = d3.scaleLinear().domain([0, 1]).range([0, 300]) fields.forEach(field => { var key = field.key var byType = d3.nest().key(d => d[key]).object(curData) field.typeData.forEach(type => { type.buckets.forEach(d => d.count = 0) var selectedData = byType[type.type] || [] selectedData.forEach(d => { type.bucketsObject[d.bucket].count++ }) var total = 0 type.buckets.forEach(d => { d.count = d.count/selectedData.length d.total = total total += d.count }) }) field.typeSel.st({opacity: d => !field.active || field.active == d.type ? 1 : .3}) field.rectSel.at({x: d => widthScale(d.total), width: d => widthScale(d.count)}) }) var opacity = .2*20000/curData.length ctx.clearRect(0, 0, c.width, c.height) curData.forEach(d =>{ ctx.beginPath() ctx.strokeStyle = color(d.combinedTaxChg).replace(.2, opacity) ctx.rect(x(d.expanded_income), y(d.combinedTaxChg), 2, 2) ctx.stroke() }) } })