Published unlisted
Edited
Jan 15, 2020
1 star
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
Insert cell
binned_predictions = {
let new_data = predictions_data.map(d => {
let new_datum = {score:d.score, index:d.index, gt:d.gt, pred:d.pred}
new_datum.bin = Math.min(n_bins-1,Math.floor(n_bins*d.score))
return new_datum
})
return new_data
}
Insert cell
Insert cell
nested_data = d3.nest()
.key(d => d.pred)
.key(d => d.bin)
.sortValues((a,b) => {
let a_gt_ind = a.gt==a.pred ? cats.length+1 : cats.indexOf(a.gt)
let b_gt_ind = b.gt==b.pred ? cats.length+1 : cats.indexOf(b.gt)
return a_gt_ind - b_gt_ind;
})
.entries(binned_predictions)
Insert cell
Insert cell
cat_scale_color = {
let hue_scale = d3.scalePoint().domain(cats).range([0,360]).padding(0.05)
let the_scale = {}
cats.forEach((cat,i) => {
let chroma_val = (i%2)==0 ? 80 : 60
let lightness_val = (i%2)==0 ? 40 : 70
the_scale[cat] = d3.hcl(hue_scale(cat),chroma_val,lightness_val)
})
return the_scale
}
Insert cell
Insert cell
cat_scale_x = d3.scaleBand()
.domain(cats)
.range([0,width])
.padding(0.05)
Insert cell
bin_scale_y = d3.scaleBand()
.domain(d3.range(n_bins))
.range([height,0])
.paddingOuter(0.02).paddingInner(0.12)
Insert cell
square_scale_y = d3.scaleBand()
.domain(d3.range(squares_per_row))
.range([0,bin_scale_y.bandwidth()])
.paddingInner(0.2)
Insert cell
Insert cell
Insert cell
{
let svg = DOM.svg(width+left_margin+right_margin, height+top_margin+bottom_margin)
let svg_elem = d3.select(svg)
let main_plot = svg_elem.append('g').attr('transform', 'translate('+left_margin+','+top_margin+')')
category_guides(main_plot)
return svg
}
Insert cell
function category_guides(main_plot) {
let prediction_groups = main_plot.selectAll('g').data(nested_data)
.enter().append('g').attr('transform', d => 'translate('+cat_scale_x(d.key)+',0)')
prediction_groups.append('line')
.attr('x1',0).attr('x2',0)
.attr('y1',0).attr('y2',height)
.attr('stroke', d => cat_scale_color[d.key])
.attr('stroke-width', 3)
prediction_groups.append('text')
.text(d => d.key)
.attr('class', 'thecat')
.attr('x', 0).attr('y', -3)
.attr('text-anchor', 'middle').attr('font-size', '14px').attr('font-family', 'serif').attr('font-weight', 'bold')
.attr('fill', d => cat_scale_color[d.key])
let scores_scale = d3.scaleLinear().domain([0,1]).range([height,0]).nice()
let scores_axis = d3.axisLeft(scores_scale)
.tickValues(d3.range(0,1.00001,1/(n_bins))).tickFormat(d3.format('.2'))
main_plot.append('g').attr('transform', 'translate(-5,0)').call(scores_axis)
return prediction_groups
}
Insert cell
Insert cell
{
let svg = DOM.svg(width+left_margin+right_margin, height+top_margin+bottom_margin)
let svg_elem = d3.select(svg)
let main_plot = svg_elem.append('g').attr('transform', 'translate('+left_margin+','+top_margin+')')
let prediction_groups = category_guides(main_plot)
let bin_data_groups = bin_rows(prediction_groups)
bin_data_groups.lower().append('rect')
.attr('width', cat_scale_x.bandwidth()).attr('height', bin_scale_y.bandwidth())
.attr('fill', d3.hcl(0,0,70))
return svg
}
Insert cell
function bin_rows(prediction_groups) {
let bin_data_join = prediction_groups.selectAll('g').data(d => d.values)
let bin_data_groups = bin_data_join.enter().append('g')
.attr('transform', d => 'translate('+0+','+bin_scale_y(d.key)+')')
return bin_data_groups
}
Insert cell
Insert cell
{
let svg = DOM.svg(width+left_margin+right_margin, height+top_margin+bottom_margin)
let svg_elem = d3.select(svg)
let main_plot = svg_elem.append('g').attr('transform', 'translate('+left_margin+','+top_margin+')')
let prediction_groups = category_guides(main_plot)
let bin_data_groups = bin_rows(prediction_groups)
square_creation(bin_data_groups)
return svg
}
Insert cell
function square_creation(bin_groups) {
let rect_data_join = bin_groups.selectAll('g').data(d => d.values)
let all_square_groups = rect_data_join.enter().append('g')
all_square_groups.attr('transform', (d,i) => {
var x_off = Math.floor(i/squares_per_row)
var square_offset_x = 3+x_off*square_scale_y.step()
var y_off = i % squares_per_row
var square_offset_y = square_scale_y(y_off)
return 'translate('+square_offset_x+','+square_offset_y+')'
})
all_square_groups.attr('class', d => { return d.pred==d.gt ? 'tp' : 'fp' })
all_square_groups.append('rect')
.attr('width', square_scale_y.bandwidth())
.attr('height', square_scale_y.bandwidth())
.attr('fill', d => cat_scale_color[d.gt])
return all_square_groups
}
Insert cell
Insert cell
{
let svg = DOM.svg(width+left_margin+right_margin, height+top_margin+bottom_margin)
let svg_elem = d3.select(svg)
let main_plot = svg_elem.append('g').attr('transform', 'translate('+left_margin+','+top_margin+')')
let prediction_groups = category_guides(main_plot)
let bin_data_groups = bin_rows(prediction_groups)
square_creation(bin_data_groups)
false_positive_annotations(bin_data_groups.selectAll('.fp'))
return svg
}
Insert cell
function false_positive_annotations(fp_selection) {
let rel_stripe_position_1 = 7*square_scale_y.bandwidth()/10;
let rel_stripe_position_2 = 3*square_scale_y.bandwidth()/10;

fp_selection.append('line')
.attr('stroke', 'white').attr('stroke-width', .7)
.attr('x1', 0).attr('x2', rel_stripe_position_1)
.attr('y1', rel_stripe_position_1).attr('y2', 0)
fp_selection.append('line')
.attr('stroke', 'white').attr('stroke-width', .7)
.attr('x1', rel_stripe_position_2).attr('x2', square_scale_y.bandwidth())
.attr('y1', square_scale_y.bandwidth()).attr('y2', rel_stripe_position_2)
}
Insert cell
Insert cell
{
let svg = DOM.svg(width+left_margin+right_margin, height+top_margin+bottom_margin)
let svg_elem = d3.select(svg)
let main_plot = svg_elem.append('g').attr('transform', 'translate('+left_margin+','+top_margin+')')
let prediction_groups = category_guides(main_plot)
let bin_data_groups = bin_rows(prediction_groups)
square_creation(bin_data_groups)
false_positive_annotations(bin_data_groups.selectAll('.fp'))
setup_square_interactions(main_plot)
return svg
}
Insert cell
function setup_square_interactions(main_plot) {
let squares_selection = main_plot.selectAll('.tp,.fp')
squares_selection.on('mouseover', function(square_datum) {
let scores = scores_data[square_datum.index];
var line_datum = cats.map(cat => [cat,scores[cat]])

let line_shape = d3.line()
.x(d => cat_scale_x(d[0]))
.y(d => score_scale_y(d[1]))

main_plot.append('path').datum(line_datum)
.attr('class', 'scoreline')
.attr('d', d => line_shape(d))
.attr('fill', 'none').attr('stroke', cat_scale_color[square_datum.pred]).attr('stroke-width', '2')
})
squares_selection.on('mouseout', function(d,i) {
main_plot.selectAll('.scoreline').remove()
})
}
Insert cell
score_scale_y = d3.scaleLinear().domain([0,1]).range([height,0])
Insert cell
Insert cell
{
let svg = DOM.svg(width+left_margin+right_margin, height+top_margin+bottom_margin)
let svg_elem = d3.select(svg)
let main_plot = svg_elem.append('g').attr('transform', 'translate('+left_margin+','+top_margin+')')
let prediction_groups = category_guides(main_plot)
let bin_data_groups = bin_rows(prediction_groups)
square_creation(bin_data_groups)
false_positive_annotations(bin_data_groups.selectAll('.fp'))
setup_square_interactions(main_plot)
setup_category_interactions(main_plot)
return svg
}
Insert cell
function setup_category_interactions(main_plot) {
main_plot.selectAll('.thecat').on('mouseover', function(cat_d,i) {
let selected_score_inds = predictions_data.filter(d => d.pred==cat_d.key)
let selected_scores = selected_score_inds.map(d => scores_data[d.index])

let line_shape = d3.line()
.x(d => cat_scale_x(d[0]))
.y(d => score_scale_y(d[1]))

main_plot.selectAll('nolines').data(selected_scores).enter().append('path')
.attr('class', 'scoreline')
.attr('d', d => {
let score_line = cats.map(cat => [cat, d[cat]])
return line_shape(score_line)
})
.attr('fill', 'none').attr('stroke', cat_scale_color[cat_d.key]).attr('stroke-width', '.4')
.attr('stroke-opacity', '0.7')
})
main_plot.selectAll('.thecat').on('mouseout', function(d,i) {
main_plot.selectAll('.scoreline').remove()
})
}
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

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