Skip to content

Instantly share code, notes, and snippets.

@nbremer
Last active November 13, 2018 13:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nbremer/7d0a91497fc64f30d1ab to your computer and use it in GitHub Desktop.
Save nbremer/7d0a91497fc64f30d1ab to your computer and use it in GitHub Desktop.
Chord diagram 2014 - End result
height: 700

The 2014 version of the Chord Diagram created for the Global Mobile Consumer Survey held by Deloitte each year. It shows the flows of people between the phone brand of their previous phone and the current phone

For an explanation on how to understand this chart, please take a look at my "Storytelling" version of the same Chord Diagram but with 2013 data

<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
<title>Chord Diagram - Phone Brands</title>
<script src="http://d3js.org/d3.v3.min.js"></script>
<!-- jQuery -->
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<!-- Bootstrap 3 -->
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css">
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js"></script>
<style>
body {
/*background: url(http://mbostock.github.io/d3/talk/20111018/texture-noise.png);*/
overflow: hidden;
margin: 0;
font-size: 14px;
font-family: "Helvetica Neue", Helvetica;
}
#footer {
position: absolute;
top: 0;
z-index: 1;
display: block;
font-size: 36px;
font-weight: 300;
text-shadow: 0 1px 0 #fff;
top: 640px;
right: 140px;
text-align: right;
}
line {
stroke: #000;
stroke-width: 1.px;
}
.hint {
position: absolute;
right: 0;
width: 350px;
font-size: 12px;
color: #999;
}
text {
font-size: 10px;
}
.titles{
font-size: 14px;
}
path.chord {
fill-opacity: .80;
}
</style>
</head>
<body>
<div id="cont" class="container-fluid">
<div class="row text-center">
<div class="col-sm-12 column"><div id="chart"></div></div>
</div>
</div>
<script type="text/javascript">
var NameProvider = ["Apple","HTC","Huawei","LG","Nokia","Samsung","Sony","Other"];
var matrix = [
[9.6899,0.8859,0.0554,0.443,2.5471,2.4363,0.5537,2.5471], //Apple
[0.1107,1.8272,0,0.4983,1.1074,1.052,0.2215,0.4983], //HTC
[0.0554,0.2769,0.2215,0.2215,0.3876,0.8306,0.0554,0.3322], //Huawei
[0.0554,0.1107,0.0554,1.2182,1.1628,0.6645,0.4983,1.052], //LG
[0.2215,0.443,0,0.2769,10.4097,1.2182,0.4983,2.8239], //Nokia
[1.1628,2.6024,0,1.3843,8.7486,16.8328,1.7165,5.5925], //Samsung
[0.0554,0.4983,0,0.3322,0.443,0.8859,1.7719,0.443], //Sony
[0.2215,0.7198,0,0.3322,1.6611,1.495,0.1107,5.4264] //Other
];
var colors = ["#C4C4C4","#69B40F","#EC1D25","#C8125C","#008FC8","#10218B","#134B24","#737373"];
var chord = d3.layout.chord()
.padding(.04)
.sortSubgroups(d3.descending) //sort the chords inside an arc from high to low
.sortChords(d3.descending) //which chord should be shown on top when chords cross. Now the biggest chord is at the bottom
.matrix(matrix);
var width = 900,
height = 650,
innerRadius = Math.min(width, height) * .41,
outerRadius = innerRadius * 1.05;
var fill = d3.scale.ordinal()
.domain(d3.range(NameProvider.length))
.range(colors);
var arc = d3.svg.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius);
var svg = d3.select("#chart").append("svg:svg")
.attr("width", width)
.attr("height", height+50)
.append("svg:g")
.attr("transform", "translate(" + width / 2 + "," + (height / 2+30) + ")");
////////////////////////////////////////////////////////////
////////////////// Draw outer Arcs /////////////////////////
////////////////////////////////////////////////////////////
var g = svg.selectAll("g.group")
.data(chord.groups)
.enter().append("svg:g")
.attr("class", "group")
.on("mouseover", fade(.02))
.on("mouseout", fade(.80));
g.append("svg:path")
.style("stroke", function(d) { return fill(d.index); })
.style("fill", function(d) { return fill(d.index); })
.attr("d", arc);
////////////////////////////////////////////////////////////
////////////////// Append Ticks ////////////////////////////
////////////////////////////////////////////////////////////
var ticks = svg.append("svg:g").selectAll("g.ticks")
.data(chord.groups)
.enter().append("svg:g").selectAll("g.ticks")
.attr("class", "ticks")
.data(groupTicks)
.enter().append("svg:g")
.attr("transform", function(d) {
return "rotate(" + (d.angle * 180 / Math.PI - 90) + ")"
+ "translate(" + outerRadius+40 + ",0)";
});
ticks.append("svg:line")
.attr("x1", 1)
.attr("y1", 0)
.attr("x2", 5)
.attr("y2", 0)
.style("stroke", "#000");
ticks.append("svg: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 Names ////////////////////////////
////////////////////////////////////////////////////////////
g.append("svg:text")
.each(function(d) { d.angle = (d.startAngle + d.endAngle) / 2; })
.attr("dy", ".35em")
.attr("class", "titles")
.attr("text-anchor", function(d) { return d.angle > Math.PI ? "end" : null; })
.attr("transform", function(d) {
return "rotate(" + (d.angle * 180 / Math.PI - 90) + ")"
+ "translate(" + (innerRadius + 55) + ")"
+ (d.angle > Math.PI ? "rotate(180)" : "");
})
.text(function(d,i) { return NameProvider[i]; });
////////////////////////////////////////////////////////////
////////////////// Draw inner chords ///////////////////////
////////////////////////////////////////////////////////////
svg.selectAll("path.chord")
.data(chord.chords)
.enter().append("svg:path")
.attr("class", "chord")
.style("stroke", function(d) { return d3.rgb(fill(d.source.index)).darker(); })
.style("fill", function(d) { return fill(d.source.index); })
.attr("d", d3.svg.chord().radius(innerRadius));
////////////////////////////////////////////////////////////
////////////////// Extra Functions /////////////////////////
////////////////////////////////////////////////////////////
// Returns an event handler for fading a given chord group.
function fade(opacity) {
return function(d, i) {
svg.selectAll("path.chord")
.filter(function(d) { return d.source.index != i && d.target.index != i; })
.transition()
.style("stroke-opacity", opacity)
.style("fill-opacity", opacity);
};
}//fade
// 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 + "%"
};
});
}//groupTicks
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment