Skip to content

Instantly share code, notes, and snippets.

@borgar
Created March 21, 2016 11:11
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save borgar/cd32f1d804951034b224 to your computer and use it in GitHub Desktop.
Save borgar/cd32f1d804951034b224 to your computer and use it in GitHub Desktop.
Will Burtin’s Antibiotics
license: mit
height: 750
border: no
name penicillin streptomycin neomycin gram
Mycobacterium tuberculosis 800 5 2 negative
Salmonella schottmuelleri 10 0.8 0.09 negative
Proteus vulgaris 3 0.1 0.1 negative
Klebsiella pneumoniae 850 1.2 1 negative
Brucella abortus 1 2 0.02 negative
Pseudomonas aeruginosa 850 2 0.4 negative
Escherichia coli 100 0.4 0.1 negative
Salmonella (Eberthella) typhosa 1 0.4 0.008 negative
Aerobacter aerogenes 870 1 1.6 negative
Brucella antracis 0.001 0.01 0.007 positive
Streptococcus fecalis 1 1 0.1 positive
Staphylococcus aureus 0.03 0.03 0.001 positive
Staphylococcus albus 0.007 0.1 0.001 positive
Streptococcus hemolyticus 0.001 14 10 positive
Streptococcus viridans 0.005 10 40 positive
Diplococcus pneumoniae 0.005 11 10 positive
<!DOCTYPE html>
<meta charset='utf-8'>
<style>
body {
background: rgb(240, 225, 210);
text-align : center;
}
svg {
font: italic 10.5px serif;
}
.positive { fill: rgba(174, 174, 184, .8); }
.negative { fill: rgba(230, 130, 110, .8); }
.penicillin { fill: rgb(10, 50, 100); }
.streptomycin { fill: rgb(200, 70, 50); }
.neomycin { fill: rgb(0, 0, 0); }
.tick circle {
fill: none;
stroke: #eee;
stroke-width: .75;
}
.tick text {
fill: black;
font: 10px sans-serif;
}
.separator {
stroke-width : 1.1;
stroke: black;
}
.label {
fill: black;
font-size: 11px;
}
</style>
<script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script>
<body>
<script>
var antibiotics = [ "penicillin", "streptomycin", "neomycin" ];
// Basic dimensions.
var margin = { top: 0, right: 0, bottom: 50, left: 0 },
width = 700,
height = 700,
innerRadius = 90,
outerRadius = 300 - 10;
function parseRow ( d ) {
antibiotics.forEach( a => d[ a ] = +d[ a ] );
return d;
}
d3.csv( 'bacteria.csv', parseRow, function ( data ) {
// Burtin's radius encoding is, as far as I can tell, sqrt(log(mic)).
var min = Math.sqrt( Math.log( .001 * 1E4 ) ),
max = Math.sqrt( Math.log( 1000 * 1E4 ) ),
a = ( outerRadius - innerRadius ) / ( min - max ),
b = innerRadius - a * max,
radius = function ( mic ) {
return a * Math.sqrt( Math.log( mic * 1E4 ) ) + b
};
// The pie is split into equal sections for each bacteria, with a blank
// section at the top for the grid labels. Each wedge is further
// subdivided to make room for the three antibiotics, equispaced.
var bigAngle = Math.PI * 2 / ( data.length + 1 ),
smallAngle = bigAngle / 7,
bigDeg = 360 / ( data.length + 1 );
// The root panel.
var svg = d3.select( 'body' ).append( 'svg' )
.attr( 'width', width + margin.left + margin.right )
.attr( 'height', height + margin.top + margin.bottom )
.append( 'g' )
.attr( 'transform', `translate(${margin.left+width/2},${margin.top+height/2})` );
var wedge = svg.selectAll( '.bacteria' )
.data( data ) // assumes Burtin's order
.enter()
.append( 'g' )
.attr( 'class', 'bacteria' )
.attr( 'transform', (d,i) => `rotate(${bigDeg * (i + .5)})` )
// Background wedges to indicate gram staining color.
var bgWedge = d3.svg.arc()
.innerRadius( innerRadius )
.outerRadius( outerRadius )
.startAngle( 0 )
.endAngle( bigAngle );
wedge.append( 'path' )
.attr( 'class', d => d.gram )
.attr( 'd', bgWedge );
// Antibiotics.
var abWedge = d3.svg.arc()
.innerRadius( innerRadius )
.outerRadius( d => radius( d ) )
.startAngle( (d,i) => smallAngle * ( i * 2 + 1 ) )
.endAngle( (d,i) => smallAngle * ( i * 2 + 2 ) );
wedge.selectAll( '.antibiotic' )
.data( antibiotics ).enter()
.append( 'path' )
.attr( 'class', d => 'antibiotic ' + d )
.attr( 'd', (d,i,p) => abWedge( wedge.data()[ p ][ d ], i ) );
// Circular grid lines.
var ticks = svg.append( 'g' )
.attr( 'class', 'axis' )
.selectAll( '.tick' )
.data( d3.range( -3, 4 ) )
.enter()
.append( 'g' )
.attr( 'class', 'tick' );
ticks.append( 'circle' )
.attr( 'r', d => radius( Math.pow( 10, d ) ) );
ticks.append( 'text' )
.style( 'text-anchor', 'middle' )
.attr( 'dy', '.32em' )
.attr( 'y', d => -radius( Math.pow( 10, d ) ) )
.text( d => (d < 3) ? Math.pow( 10, d ).toFixed( (d > 0) ? 0 : -d ) : '' );
// Radial grid lines.
svg.selectAll( '.separator' )
.data( d3.range( data.length + 1 ) )
.enter()
.append( 'line' )
.attr( 'class', 'separator' )
.attr( 'transform', i => `rotate(${180 + bigDeg * (i + .5)})` )
.attr( 'y1', innerRadius * 0.84 )
.attr( 'y2', outerRadius * 1.1 );
svg.selectAll( '.label' )
.data( data )
.enter()
.append( 'text' )
.attr( 'class', 'label' )
.attr( 'transform', (d,i) => `rotate(${((i + 1 > data.length / 2) ? 90 : -90) + bigDeg * (i + 1)})` )
.style( 'text-anchor', (d,i) => (i + 1 > data.length / 2) ? 'start' : 'end' )
.attr( 'dy', '.32em' )
.attr( 'x', (d,i) => ( i + 1 > data.length / 2 ) ? -outerRadius * 1.1 : outerRadius * 1.1 )
.text( d => d.name );
// Antibiotic legend.
var ab_legend = svg.append( 'g' )
.attr( 'class', 'antibiotic legend' )
.selectAll( 'g' )
.data( antibiotics )
.enter()
.append( 'g' )
.attr( 'class', String )
.attr( 'transform', (d,i) => `translate(-8,${-18 + i * 18})` );
ab_legend.append( 'text' )
.attr( 'class', 'label' )
.attr( 'dy', '.32em' )
.attr( 'x', 3 )
.text( d => d[0].toUpperCase() + d.slice(1) );
ab_legend.append( 'rect' )
.attr( 'x', -40 )
.attr( 'y', -4 )
.attr( 'width', 36 )
.attr( 'height', 8 );
// Gram-stain legend.
var stain_legend = svg.append( 'g' )
.attr( 'class', 'gram legend' )
.selectAll( 'g' )
.data([ 'negative', 'positive' ])
.enter()
.append( 'g' )
.attr( 'class', String )
.attr( 'transform', (d,i) => `translate(0,${height/2 + i * 18})` );
stain_legend.append( 'text' )
.attr( 'class', 'label' )
.attr( 'dy', '.32em' )
.attr( 'x', 3 )
.text( d => 'Gram-' + d );
stain_legend.append( 'circle' )
.attr( 'cx', -12 )
.attr( 'r', 5 );
});
</script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment