Skip to content

Instantly share code, notes, and snippets.

@dukevis
Last active August 29, 2015 14:00
Show Gist options
  • Save dukevis/11004937 to your computer and use it in GitHub Desktop.
Save dukevis/11004937 to your computer and use it in GitHub Desktop.
chord diagram + multiline chart
Year UNC Duke NCS WF MD UVA Clem
1954 0 1 3 2 1 0 0
1955 0 3 6 3 1 1 0
1956 1 4 9 5 1 1 0
1957 3 4 9 6 2 1 0
1958 5 5 10 6 5 1 0
1959 7 6 13 6 5 2 0
1960 8 9 14 8 5 2 0
1961 8 11 14 10 6 2 0
1962 8 12 14 13 6 2 2
1963 9 15 15 15 6 2 2
1964 10 18 15 17 6 2 3
1965 10 20 18 18 7 2 3
1966 11 23 20 18 7 2 3
1967 14 25 20 19 7 2 3
1968 17 26 22 19 7 2 3
1969 20 28 22 20 7 2 3
1970 20 28 25 21 7 3 3
1971 22 28 26 21 7 4 3
1972 24 29 26 21 9 5 3
1973 24 29 28 22 11 6 3
1974 25 29 30 22 13 7 3
1975 28 29 32 22 13 7 4
1976 29 29 32 22 14 10 5
1977 31 29 33 22 14 12 6
1978 31 32 33 24 15 12 6
1979 33 34 34 24 16 12 6
1980 34 37 34 24 18 12 7
1981 37 37 34 25 20 13 7
1982 40 37 35 26 20 15 7
1983 41 37 38 26 20 17 7
1984 42 39 38 27 23 17 7
1985 44 40 39 27 23 17 7
1986 44 43 39 27 24 18 7
1987 46 43 42 28 24 19 7
1988 48 46 43 28 25 19 7
1989 51 48 43 28 26 20 7
1990 51 49 43 28 26 22 8
1991 54 50 44 28 26 23 8
1992 56 53 44 28 27 23 8
1993 58 53 44 28 28 24 9
1994 61 54 44 29 28 26 10
1995 63 55 44 32 29 27 10
1996 63 55 45 35 30 27 11
1997 66 55 48 36 31 27 11
1998 69 57 49 36 32 27 12
1999 71 60 50 36 33 27 12
2000 71 63 51 37 35 27 12
2001 73 66 51 37 36 27 13
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<style>
body{
font: 10px sans-serif;
}
.axis path,
.axis line{
fill:none;
stroke:#000;
shape-rendering: crispEdges;
}
.line {
fill: none;
stroke: black;
stroke-width: 3.5px;
}
.legend{
padding: 5px;
font: 10px sans-serif;
border: .5em solid black;
}
.team line{
stroke: black;
}
.chord path {
fill-opacity: .67;
stroke: #000;
stroke-width: .5px;
}
.ticks {
font-family: sans-serif;
}
</style>
<title>ACC Tournament, Cumulative Wins</title>
<body>
<script type="text/javascript" src="http://d3js.org/d3.v3.js"></script>
</head>
<script type="text/javascript">
var matrix = [
[0,8,10,14,11,10,12],
[9,0,11,12,9,11,5],
[4,8,0,6,10,10,5],
[7,4,3,0,3,6,7],
[3,6,5,3,0,4,9],
[3,3,2,6,4,0,5],
[1,3,2,3,1,1,0],
];
var max_scores = [];
var teams = ["North Carolina","Duke","NC State","Wake Forest","Maryland",
"Virginia","Clemson"];
var colors = ["#99BADD","#00009C","#CE1126","#CFB53B","#FF0000","#0D3268","#FF6300"];
var abbrDict={"Clem":"Clemson","Duke":"Duke","MD":"Maryland","UNC":"North Carolina", "NCS":"NC State", "UVA":"Virginia","WF":"Wake Forest"};
var dataset=0;
var w=932;
var h=490;
var padding = 30;
var color = d3.scale.ordinal() //custom color scale for teams
.range(colors)
.domain(teams);
d3.csv("basketball_data.csv",function(data){
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "Year"; }));
data.forEach(function(d) {
d.Year = +d.Year;
d.Clem = +d.Clem;
d.Duke = +d.Duke;
d.MD = +d.MD;
d.UNC = +d.UNC;
d.NCS = +d.NCS;
d.UVA = +d.UVA;
d.WF = +d.WF;
});
var teams = color.domain().map(function(name) {
return {
name: name,
sort_value: 0,
values: data.map(function(d) {
return {date: d.Year, wins: d[name]};
})
};
});
//teams in order of most cumulative wins
teams.sort(function(a,b){
var last_b = b.values.length;
var last_a = a.values.length;
max_scores.push(abbrDict[b.name]);
max_scores.push(b.values[last_b-1].wins);
max_scores.push(abbrDict[a.name]);
max_scores.push(a.values[last_a-1].wins);
return parseFloat(b.values[last_b-1].wins)-parseFloat(a.values[last_a-1].wins);
});
console.log(max_scores);
console.log(teams);
visualize(teams);
});
var visualize = function(team_dict){
var svg = d3.select("body").append("svg")
.attr("width",w+padding*2)
.attr("height",h + padding*2)
.append("g")
.attr("transform", "translate(" + padding + "," + padding + ")");
var chord = d3.layout.chord()
.padding(.05)
.sortSubgroups(d3.descending)
.matrix(matrix);
var innerRadius = Math.min(w, h) * .41;
var outerRadius = innerRadius * 1.1;
var chord_fill = d3.scale.ordinal()
.domain(d3.range(10))
.range(colors);
//line chart code
var xScale = d3.scale.linear().range([padding/2,(w-padding/2)/3]);
var yScale = d3.scale.linear().range([h,padding]);
var xAxis = d3.svg.axis()
.scale(xScale)
.orient("bottom")
.tickFormat(d3.format("04d"));
var yAxis = d3.svg.axis()
.scale(yScale)
.orient("left");
var line = d3.svg.line()
.x(function(d){
if(xScale(d.date)<0){ //quick fix to ensure Wake Forest wasn't extending into negatives
return padding;
}
else{
return padding+xScale(d.date);
}
})
.y(function(d){
return yScale(d.wins);
})
xScale.domain([1954,2001]);
yScale.domain([
d3.min(team_dict, function(c) { return d3.min(c.values, function(v) { return v.wins; }); }),
d3.max(team_dict, function(c) { return d3.max(c.values, function(v) { return v.wins; }); })
]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate("+padding+"," + h + ")")
.call(xAxis)
.append("text")
.attr("y",30)
.attr("x",w/4)
.style("font-size",16+"px")
.text("Year");
svg.append("g")
.attr("class", "y axis")
.attr("transform","translate("+padding/2+",0)")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", -40)
.attr("dy", ".71em")
//.style("text-anchor", "end")
.attr("x",-h/1.4)
.style("font-size",16+"px")
.text("Cumulative Number of Wins");
var team = svg.selectAll("g.team")
.data(team_dict)
.enter()
.append("g")
.attr("class", "team");
team.append("path")
.attr("class", "team line")
.attr("d", function(d) { return line(d.values); })
.style("stroke", function(d) { return color(d.name); });
//border for legend
//not apart of the g group/legend class because Clemson's orange square disappears when I do that
svg.append("rect")
.attr("x",padding)
.attr("y",-15)
.attr("width",200)
.attr("height",100)
.attr("style","fill-opacity:0;stroke:black;stroke-width:1.5px");
var legend = svg.append("g")
.attr("class","legend")
.attr("width",200)
.attr("height",100);
//create elements inside of the legend
var gs = legend.selectAll("g.keybox")
.data(team_dict).enter().append("g")
.attr("class","keybox").attr("width",80).attr("height",15).attr("x",function(d,i){
if(i<4){return 65;}
else{return 150;}
})
.attr("y",function(d,i){
if(i<4){return i*20;}
else{return (i-4) * 20;}
})
.attr("id",function(d){
return ""+color(d.name);
});
gs.append("text")
.attr("class","keybox").attr("x",function(d,i){
if(i<4){return 80;}
else{return 163;}
})
//alternate boxes between two columns
.attr("y",function(d,i){
if(i<4){return i*20 +9;}
else{return (i-4)*20 +9;}
})
.attr("stroke-width",0)
.attr("stroke",function(d){
return color(d.name);
})
.text(function(d){
return abbrDict[d.name];
})
.on("mouseover", fade(.1))
.on("mouseout", fade(1));
gs.append("rect")
.attr("class","keybox")
.attr("x",function(d,i){
if(i<4){return 65;}
else{return 150;}
})
.attr("y",function(d,i){
if(i<4){return i*20;}
else{return (i-4) * 20;}
})
.attr("width",10)
.attr("height",10)
.style("fill",function(d){
return color(d.name);
})
.on("mouseover", fade(.1))
.on("mouseout", fade(1));
svg.selectAll("g.keybox").on("mouseover",function(){
var keepName = this.textContent;
var id = this.id;
var selectedLine_box;
var team_color;
var team_wins;
svg.selectAll("path.team.line").style("stroke", function(d){
if(abbrDict[d.name]!=keepName){
return "#C0C0C0";
}
else{
selectedLine_box = d3.select(this);
team_color=color(d.name);
var last = d.values.length;
team_wins = d.values[last-1].wins;
return color(d.name);
}
});
var pathNode = selectedLine_box.node();
var node = selectedLine_box.node();
//console.log(node);
var parent = node.parentNode;
var grandparent = node.parentNode.parentNode;
//console.log(parent);
//console.log(grandparent);
//console.log(parent.childNodes);
grandparent.removeChild(parent);
grandparent.appendChild(parent);
/*
var offset = (.75*w -(w/3))/3;
var center = offset+(w/3);
var center_label = svg.append("text")
.attr("id","center_label")
.attr("x",center)
.attr("y",h/2)
.style("text-anchor","middle")
.style("font-size",30+"px")
.style("font-family","sans-serif")
.text(keepName);
var score = svg.append("text")
.attr("id","score")
.attr("x",center)
.attr("y",h/2+40)
.style("text-anchor","middle")
.style("font-size",35+"px")
.text(""+team_wins);*/
});
svg.selectAll("g.keybox").on("mouseout",function(){
d3.select("#tooltip").remove();
d3.select("#center_label").remove();
d3.select("#score").remove();
svg.selectAll("path.team.line").style("stroke",function(d){
return color(d.name);
});
svg.selectAll("path.rim").style("opacity", 1);
});
svg.selectAll("g.team")
.on("mouseover",function(d){
var lineName;
var label_color;
var team_wins;
svg.selectAll("path.team.line").style("stroke","#C0C0C0"); //set all to gray
var selectedGroup = d3.select(this);
//console.log(this);
var selectedLine = selectedGroup.select("path.team.line");
selectedLine.style("stroke",function(d){ //let active keep color
lineName = abbrDict[d.name]; //full name to be at end of line
label_color=color(d.name);
var last = d.values.length;
team_wins = d.values[last-1].wins;
return color(d.name);
});
this.parentNode.appendChild(this); //brings team to front, must select the path's g parent to reorder it
var offset = (.75*w -(w/3))/3;
//console.log(offset);
var center = offset+(w/3);
var center_label = svg.append("text")
.attr("id","center_label")
.attr("x",center-padding)
.attr("y",h/2)
.style("text-anchor","middle")
.style("font-size",30+"px")
.style("font-family","sans-serif")
.text(lineName);
var score = svg.append("text")
.attr("id","score")
.attr("x",center-padding)
.attr("y",h/2+40)
.style("text-anchor","middle")
.style("font-size",35+"px")
.text(""+team_wins);
})
.on("mouseout",function(){
d3.select("#tooltip").remove();
d3.select("#center_label").remove();
d3.select("#score").remove();
d3.selectAll("path.team").transition().style("stroke",function(d){
return color(d.name);
});
});
//chord code
svg.append("g")
.attr("class","groups")
.attr("transform","translate("+.75*w+","+h/2+")")
.selectAll("path")
.data(chord.groups)
.enter().append("path")
.attr("class","rim")
.style("fill", function(d) { return chord_fill(d.index); })
.style("stroke", function(d) { return chord_fill(d.index); })
.attr("d", d3.svg.arc().innerRadius(innerRadius).outerRadius(outerRadius))
.on("mouseover", fade(.1))
.on("mouseout", fade(1));
var ticks = svg.append("g")
.attr("transform","translate("+.75*w+","+h/2+")")
.attr("class","ticks")
.selectAll("g")
.data(chord.groups)
.enter().append("g").selectAll("g")
.data(groupTicks)
.enter().append("g")
.attr("transform", function(d) {
return "rotate(" + (d.angle * 180 / Math.PI - 90) + ")"
+ "translate(" + outerRadius + ",0)";
});
// Append tick lines
ticks.append("line")
.attr("x1", 1)
.attr("y1", 0)
.attr("x2", 5)
.attr("y2", 0)
.style("stroke", "#000");
// Append tick labels
ticks.append("text")
.attr("x", 8)
.attr("dy", ".35em")
.attr("transform", function(d) { return d.angle > Math.PI ? "rotate(180)translate(-16)" : null; })
.style("text-anchor", function(d) { return d.angle > Math.PI ? "end" : null; })
.text(function(d) { return d.label; });
// Append chords
svg.append("g")
.attr("class", "chord")
.attr("transform","translate("+.75*w+","+h/2+")")
.selectAll("path")
.data(chord.chords)
.enter().append("path")
.attr("class","inner")
.attr("d", d3.svg.chord().radius(innerRadius))
.style("fill", function(d) { return d.source.value == d.target.value ? chord_fill(chord_fill.range().length-1) : chord_fill(d.source.index); })
.style("opacity", 1);
// Returns an array of tick angles and labels, given a group.
function groupTicks(d) {
var k = (d.endAngle - d.startAngle) / d.value;
return d3.range(0, d.value, 1).map(function(v, i) {
return {
angle: v * k + d.startAngle,
label: i % 5 ? null : v / 1
};
});
}
function fade(opacity){
return function(g,i){
var target_team;
var goal_index;
svg.selectAll(".chord path")
.filter(function(d){
console.log(chord_fill(i));
target_team_index = colors.indexOf(chord_fill(i));
goal_index = i;
target_team = teams[i];
return d.source.index != i && d.target.index !=i;
})
.transition()
.style("opacity",opacity);
var offset = (.75*w -(w/3))/3;
var center = offset+(w/3);
if(opacity==.1){
var center_label = svg.append("text")
.attr("id","center_label")
.attr("x",center)
.attr("y",h/2)
.style("text-anchor","middle")
.style("font-size",30+"px")
.style("font-family","sans-serif")
.text(target_team);
var target_ind = max_scores.indexOf(target_team);
console.log(target_ind);
var target_wins = max_scores[target_ind+1];
var center_score = svg.append("text")
.attr("id","score")
.attr("x",center)
.attr("y",h/2+40)
.style("text-anchor","middle")
.style("font-size",35+"px")
.text(""+target_wins);
}
if(opacity==1){
d3.select("#center_label").remove();
d3.select("#score").remove();
}
//update scores
d3.selectAll(".logo_score")
.filter(function(d, j) { return j != i; })
.text(function(d, j) {
var target = j < i ? j : j + 1;
return matrix[i][target] + "-" + matrix[target][i]; })
.style("opacity", function() { return opacity == 1 ? 0 : 1; })
.style("border-color", function(d, j) {
var target = j < i ? j : j + 1;
if (matrix[i][target] > matrix[target][i]) return "#090";
else if (matrix[i][target] < matrix[target][i]) return "#930";
else return d3.select("body").style("color"); });
}
}
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment