|
// function to pick colors of bubbles |
|
function colorPicker(d) { |
|
return "rgb(0, 0, 0)"; |
|
} |
|
|
|
// comparator function for sorting numeric arrays |
|
function sortNumber(a,b) { |
|
return a - b; |
|
} |
|
|
|
// input parameters |
|
var filename = "https://cdn.rawgit.com/nielshanson/d3/master/data/WL_Species.csv"; |
|
var orders_bars = { |
|
hallam: new Array(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0), |
|
abundance: new Array(36,108,108,264,348,288,300,300,264,132,156,432,24,300,504,252,492,-192,480,-84,24,-144,48,-96,-72,-228,-156,-324,-276,-96,-168,-348,-180,-312,60,60,-336,0,-228,-144,-432,120,204,12,12,180,-144,-276,0,-84,24,-60,-36,0,0,-144,-72,-264,-36,-216,-384,0) |
|
}; |
|
|
|
var orders_rows = { |
|
hallam: new Array(0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68), |
|
abundance: new Array(3,10,11,25,33,29,31,32,30,20,23,47,14,38,56,36,57,1,58,12,22,9,26,15,18,6,13,0,5,21,16,2,17,7,39,40,8,37,19,27,4,51,59,44,45,60,34,24,48,42,52,46,49,53,54,43,50,35,55,41,28,61,59,64,60,42,66,37,62) |
|
}; |
|
|
|
// size parameters |
|
var h_spacing = 20; // horizontal spacing |
|
var v_spacing = 12; // vertical spacing |
|
var margin = {top: 100, right: 160, bottom: 20, left: 160}; |
|
var circle_factor = 0.75; //radius of circles relative to data point magnitude |
|
var circle_opacity = 0.70; |
|
|
|
var x = d3.scale.ordinal().rangeBands([0, 300]); |
|
|
|
d3.text(filename, function(datasetText) { |
|
|
|
var parsedCSV = d3.csv.parseRows(datasetText); |
|
|
|
// parse out x and y axis labels |
|
var xlab = parsedCSV[0].slice(1,parsedCSV[0].length); |
|
var ylab = []; |
|
for(var i = 1; i < parsedCSV.length; i++){ |
|
var temp = parsedCSV[i]; |
|
ylab = ylab.concat(temp[0]); |
|
} |
|
|
|
// parse out the datamatrix |
|
var data_matrix = []; |
|
for(var i = 1; i < parsedCSV.length; i++){ |
|
var temp = parsedCSV[i].slice(1,parsedCSV[i].length ); |
|
data_matrix = data_matrix.concat([temp]); |
|
} |
|
|
|
// calculate marginal sums |
|
var sum_rows = []; // sum of the rows |
|
for(var i = 0; i < data_matrix.length; i++){ |
|
var temp_sum = 0; |
|
for(var j = 0; j < data_matrix[i].length; j++){ |
|
temp_sum = parseFloat(temp_sum) + parseFloat(data_matrix[i][j]); |
|
} |
|
sum_rows = sum_rows.concat([temp_sum]); |
|
} |
|
|
|
original_ranks = []; |
|
for(var i = 0; i < data_matrix.length; i++) { |
|
original_ranks[i] = i+1; |
|
} |
|
|
|
sum_rows_sorted = sum_rows.slice(); |
|
sum_rows_sorted.sort(sortNumber); |
|
sum_rows_sorted.reverse(); |
|
|
|
ranks = sum_rows.slice().map(function(v){ return sum_rows_sorted.indexOf(v)+1 }); |
|
|
|
// shift ranks |
|
already_seen = []; |
|
|
|
for (var i = 0; i < ranks.length; i++) { |
|
if (already_seen[ranks[i]] == undefined) { |
|
already_seen[ranks[i]] = ranks[i]; |
|
|
|
} else { |
|
var seen_val = ranks[i] |
|
while (already_seen[seen_val] != undefined) { |
|
seen_val += 1; |
|
} |
|
ranks[i] = seen_val; |
|
already_seen[seen_val] = seen_val; |
|
} |
|
} |
|
|
|
delta_ranks = []; |
|
delta_points = []; |
|
for (var i = 0; i < data_matrix.length; i++) { |
|
delta_ranks[i] = (ranks[i] - original_ranks[i]); |
|
delta_points[i] = (ranks[i] - original_ranks[i]) * 12; |
|
} |
|
|
|
// shift ranks down by one |
|
for (var i = 0; i < ranks.length; i++) { |
|
ranks[i] = ranks[i] - 1; |
|
} |
|
orders_rows['abundance'] = ranks; |
|
orders_bars['abundance'] = delta_points; |
|
|
|
var sum_col = []; // sum of the columns |
|
for(var i = 0; i < data_matrix[0].length; i++){ |
|
var temp_sum = 0; |
|
for(var j = 0; j < data_matrix.length; j++){ |
|
temp_sum = parseFloat(temp_sum) + parseFloat(data_matrix[j][i]); |
|
} |
|
sum_col = sum_col.concat([temp_sum]); |
|
} |
|
|
|
// find marginal averages |
|
var avg_rows = []; // avg of columns |
|
for(var i = 0; i < data_matrix.length; i++){ |
|
var temp_sum = 0; |
|
for(var j = 0; j < data_matrix[i].length; j++){ |
|
temp_sum = parseFloat(temp_sum) + parseFloat(data_matrix[i][j]); |
|
} |
|
avg_rows = avg_rows.concat([temp_sum]/xlab.length); |
|
} |
|
|
|
var avg_col = []; // avg of columns |
|
for(var i = 0; i < data_matrix[0].length; i++){ |
|
var temp_sum = 0; |
|
for(var j = 0; j < data_matrix.length; j++){ |
|
temp_sum = parseFloat(temp_sum) + parseFloat(data_matrix[j][i]); |
|
} |
|
avg_col = avg_col.concat([temp_sum]/ylab.length); |
|
} |
|
|
|
var width = data_matrix[1].length*h_spacing; |
|
var height = data_matrix.length*v_spacing; |
|
|
|
var svg = d3.select("#table").append("svg"); |
|
svg.attr("width", width + margin.left + margin.right) |
|
.attr("height", height + margin.top + margin.bottom) |
|
.append("g") |
|
.attr("transform", "translate(" + margin.left + "," + margin.top + ")") |
|
.selectAll("g") |
|
.data(data_matrix) |
|
.enter() |
|
.append("g") |
|
.attr("class", "row") |
|
.attr("transform", function(d, i){ return "translate(0," + (i*v_spacing) + ")"; }) |
|
.selectAll("circle") |
|
.data(function(d){return d;}) |
|
.enter().append("circle") |
|
.attr("fill", function(d) { |
|
return colorPicker(d); |
|
}) |
|
.attr("r", function(d) { |
|
return Math.sqrt(d*circle_factor); |
|
}) |
|
.attr("cx", function(d,i) { |
|
return (i * h_spacing) + 6; |
|
}) |
|
.attr("cy", 6) |
|
.attr("opacity", circle_opacity) |
|
.on("mouseover", function(){ |
|
d3.select(this).attr("fill", "#F77E1C"); |
|
var temp = d3.select(this).attr("alt"); |
|
temp = Math.round(temp*100)/100 |
|
tooltip.show("<strong>" + temp + "</strong>"); |
|
}) |
|
.on("mouseout", function(){ |
|
d3.select(this).attr("fill", function(d) { |
|
tooltip.hide(); |
|
return colorPicker(d); |
|
}) |
|
}) |
|
.attr("alt", function(d) { |
|
return d; |
|
}) |
|
|
|
// axis |
|
var xScale = d3.scale.ordinal() |
|
.domain(xlab) |
|
.rangePoints([0+5, width-14]); |
|
|
|
var xAxis = d3.svg.axis() |
|
.scale(xScale) |
|
.orient("right") |
|
var yScale = d3.scale.ordinal() |
|
.domain(ylab) |
|
.rangePoints([0+4, height-4]); |
|
var yAxis = d3.svg.axis() |
|
.scale(yScale) |
|
.tickSize(0) |
|
.orient("left") |
|
svg.append("g") |
|
.attr("class", "axis") |
|
.attr("transform", "translate("+ margin.left + "," + (margin.top) + ") rotate(270)") |
|
.call(xAxis); |
|
|
|
svg.append("g") |
|
.attr("class", "axis") |
|
.attr("id","leftwords") |
|
.attr("transform", "translate("+ (margin.left -2) + "," + margin.top + ") rotate(0)") |
|
.call(yAxis); |
|
|
|
var col_hist_size = 0.5* margin.top; |
|
|
|
var col_hist_scale = d3.scale.linear() |
|
.domain([0, d3.max(avg_col)]) |
|
.range([0, col_hist_size]); |
|
|
|
// upper marginal axis line |
|
// row histogram |
|
var row_hist_size = 0.8* margin.right; |
|
|
|
var row_hist_scale = d3.scale.linear() |
|
.domain([0, d3.max(avg_rows)]) |
|
.range([0, row_hist_size]); |
|
|
|
// right hand side marginal bars |
|
svg.append("g") |
|
.attr("transform", "translate(" + (width + margin.right) + "," + margin.top + ")") |
|
.selectAll("rect") |
|
.data(sum_rows) |
|
.enter() |
|
.append("rect") |
|
.attr("x", function(d, i) { |
|
return 0; // spacing of 5 |
|
}) |
|
.attr("y", function(d, i) { |
|
return i * 12; //Bar width of 12 plus 1 for padding |
|
}) |
|
.attr("width", function(d,i) { |
|
return row_hist_scale(avg_rows[i]); |
|
}) |
|
.attr("fill","rgb(54, 144, 192)") |
|
.attr("height", 10) |
|
.attr("class", "rowright"); |
|
|
|
// right marginal text labels |
|
svg.append("g") |
|
.attr("transform", "translate(" + (width + margin.right) + "," + margin.top + ")") |
|
.selectAll("text") |
|
.data(avg_rows) |
|
.enter() |
|
.append("text") |
|
.text(function(d) { |
|
return Math.round(d*100)/100; |
|
}) |
|
.attr("x", function(d, i) { |
|
return row_hist_scale(d) + 5; |
|
}) |
|
.attr("y", function(d, i) { |
|
return i * 12 + 8; |
|
}) |
|
.attr("font-family", "sans-serif") |
|
.attr("font-size", "8px") |
|
.attr("class", "righttext") |
|
.attr("fill", "black"); |
|
|
|
// legend text |
|
var legend = [4, 8, 16, 32]; |
|
|
|
// legend color boxes |
|
svg.append("g") |
|
.attr("transform", "translate(20,20)") |
|
.selectAll("circle") |
|
.data(legend) |
|
.enter() |
|
.append("circle") |
|
.attr("cx", function(d, i) { |
|
return i * (h_spacing + 12); //h_spacing plus padding |
|
}) |
|
.attr("cy", function(d) { |
|
return 30; |
|
}) |
|
.attr("r", function(d){ |
|
return Math.sqrt(d*circle_factor); |
|
}) |
|
.attr("fill",function(d){ |
|
return colorPicker(d); |
|
}) |
|
.attr("height", 10); |
|
|
|
// legend text |
|
svg.append("g") |
|
.attr("transform", "translate(20,45)") |
|
.selectAll("text") |
|
.data(legend) |
|
.enter() |
|
.append("text") |
|
.text(function(d) { |
|
return d; |
|
}) |
|
.attr("x", function(d, i) { |
|
return i * (h_spacing+12); |
|
}) |
|
.attr("y", function(d, i) { |
|
return 25; |
|
}) |
|
.attr("font-family", "sans-serif") |
|
.attr("font-size", "8px") |
|
.attr("fill", "black") |
|
.attr("text-anchor", "middle"); |
|
|
|
svg.append("text") |
|
.attr("transform", "translate(20,25)") |
|
.text("Legend:") |
|
.attr("font-family", "sans-serif") |
|
.attr("font-size", "12px"); |
|
|
|
}); |
|
function animate(value) { |
|
var rows = orders_rows[value]; |
|
var bars = orders_bars[value]; |
|
d3.selectAll(".row").transition() |
|
.duration(2500) |
|
.attr("transform", function(d,i){ |
|
return " translate(0," + rows[i]*12 + " )"; |
|
}); |
|
d3.selectAll(".rowright").transition() |
|
.duration(2500) |
|
.attr("transform", function(d,i){ |
|
return " translate(0," + bars[i] + " )"; |
|
}); |
|
d3.selectAll(".righttext").transition() |
|
.duration(2500) |
|
.attr("transform", function(d,i){ |
|
return " translate(0," + bars[i] + " )"; |
|
}); |
|
d3.selectAll("#leftwords > g").transition() |
|
.duration(2500) |
|
.attr("transform", function(d,i){ |
|
return " translate(0," + ((rows[i]*12) + 5) + " )"; |
|
}); |
|
|
|
}; |
|
|
|
d3.select("#order").on("change", function() { |
|
animate(this.value); |
|
}); |