Skip to content

Instantly share code, notes, and snippets.

@varontron
Forked from senthilthyagarajan/.block
Created February 7, 2020 17:22
Show Gist options
  • Save varontron/93dea4147ac48720dc4eafa8050bc9af to your computer and use it in GitHub Desktop.
Save varontron/93dea4147ac48720dc4eafa8050bc9af to your computer and use it in GitHub Desktop.
Segments By Audience
width: 820
height: 730

This viz represents the groups within each audience segment by the sample size

#chart {
width: 800px;
/* height: 620px; */
margin: 0 auto;
padding-top: 30px;
}
text {
pointer-events: none;
}
.grandparent text {
font-weight: bold;
}
rect {
stroke: #fff;
stroke-width: 1px;
}
rect.parent,
.grandparent rect {
stroke-width: 2px;
}
.children rect.parent,
.grandparent rect {
cursor: pointer;
}
.children rect.child {
opacity: 0;
}
.children:hover rect.child {
opacity: 1;
stroke-width: 1px;
}
.children:hover rect.parent {
opacity: 0;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<link rel="stylesheet" href="index.css">
</head>
<body>
<div id="chart"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
<script src="index.js"></script>
</body>
</html>
var log = console.log;
var margin = {top: 30, right: 0, bottom: 0, left: 0};
var width = 800;
var height = 670 - margin.top - margin.bottom;
var transitioning;
// x axis scale
var x = d3.scale.linear()
.domain([0, width])
.range([0, width]);
// y axis scale
var y = d3.scale.linear()
.domain([0, height])
.range([0, height]);
// treemap layout
var treemap = d3.layout.treemap()
.children(function(d, depth) { return depth ? null : d._children; })
.sort(function(a, b) { return a.value - b.value; })
.ratio(height / width * 0.5 * (1 + Math.sqrt(5)))
.round(false);
// define svg
var svg = d3.select("#chart").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.bottom + margin.top)
.style("margin-left", -margin.left + "px")
.style("margin.right", -margin.right + "px")
.append("g")
.attr("transform", `translate(${margin.left},${margin.top})`)
.style("shape-rendering", "crispEdges");
// top gray rectangle
var grandparent = svg.append("g")
.attr("class", "grandparent");
// Add grandparent rect
grandparent.append("rect")
.attr("y", -margin.top)
.attr("width", width)
.attr("height", margin.top)
.style("fill", "#d9d9d9");
// Add grandparent text
grandparent.append("text")
.attr("class", "title")
.attr("x", 6)
.attr("y", 6 - margin.top)
.attr("dy", ".75em");
// custom root initializer
function initialize(root) {
root.x = root.y = 0;
root.dx = width;
root.dy = height;
root.depth = 0;
};
// Aggregate the values for internal nodes. This is normally done by the
// treemap layout, but not here because of our custom implementation.
// We also take a snapshot of the original children (_children) to avoid
// the children being overwritten when when layout is computed.
// Alteration made for function to be iterative
function accumulateVal(d, attr) {
return accumulate(d);
function accumulate(d) {
return (d._children = d.children)
// recursion step, note that p and v are defined by reduce
? d[attr] = d.children.reduce(function(p, v) {return p + accumulate(v); }, 0)
: d[attr];
};
};
// Compute the treemap layout recursively such that each group of siblings
// uses the same size (1×1) rather than the dimensions of the parent cell.
// This optimizes the layout for the current zoom state. Note that a wrapper
// object is created for the parent node for each group of siblings so that
// the parent’s dimensions are not discarded as we recurse. Since each group
// of sibling was laid out in 1×1, we must rescale to fit using absolute
// coordinates. This lets us use a viewport to zoom.
function layout(d) {
if (d._children) {
// treemap nodes comes from the treemap set of functions as part of d3
treemap.nodes({_children: d._children});
d._children.forEach(function(c) {
c.x = d.x + c.x * d.dx;
c.y = d.y + c.y * d.dy;
c.dx *= d.dx;
c.dy *= d.dy;
c.parent = d;
// recursion
layout(c);
});
}
};
d3.csv("Public_Grants_2010-2012(July-28-2018).csv", treeMapZoomable);
function treeMapZoomable(error, grants) {
if (error) throw error;
// Define color scale
var allValues = grants.map(function(d) { return [d["GRANT_PROGRAM"], d["YEAR"]]; })
.reduce(function(acc, curVal) { return acc.concat(curVal); }, []);
var scaleOrdNames = [...new Set(allValues)];
var colorScale = d3.scale.ordinal().domain(scaleOrdNames)
.range(['#66c2a5','#fc8d62','#8da0cb','#e78ac3','#a6d854','#ffd92f','#e5c494']);
// Define aggregrated grant programs
var aggGrantPrograms = d3.nest().key(function(d) { return d.YEAR; })
.key(function(d) { return d["GRANT_PROGRAM"]; })
.entries(grants)
.map(function(d) {
var dValues = d.values.map(function(g){
var gValues = g.values;
var sum = gValues.map(function(p) { return p["GRANT_AWARD"]; })
.reduce(function(acc, curVal) { return acc + (+curVal); }, 0);
return {
name: g.key,
value: gValues.length,
sum: sum
};
});
return {
name: d.key,
children: dValues
};
});
// Root for hierarchy
var rootObject = {name: "Audience Segment", children: aggGrantPrograms};
initialize(rootObject);
["value", "sum"].forEach(function(d) { accumulateVal(rootObject, d); });
layout(rootObject);
display(rootObject);
function display(d) {
// Grandparent when clicked transitions the children out to parent
grandparent
.datum(d.parent)
.on("click", transition)
.select("text.title")
.text(name(d));
grandparent
.datum(d.parent)
.select("rect");
var g1 = svg.insert("g", ".grandparent")
.datum(d)
.attr("class", "depth");
var g = g1.selectAll("g")
.data(d._children)
.enter().append("g");
// When parent is clicked transitions to children
g.filter(function(d) { return d._children; })
.classed("children", true)
.on("click", transition);
g.selectAll(".child")
.data(function(d) { return d._children || [d]; })
.enter().append("rect")
.attr("class", "child")
.call(rect);
g.append("rect")
.attr("class", "parent")
.call(rect)
.append("title")
.text(function(d) { return `Audience Segment: ${d.name}`; });
// Appending year, grants, and sum texts for each g
var textClassArr = [
{class: "year", accsor: function(d) { return d.name; }},
{class: "grants", accsor: function(d) { return `${d.value} segments`; }},
{class: "sum", accsor: function(d) { return `${d.sum.toLocaleString()} sample size`; }}
];
textClassArr.forEach(function(p, i) {
g.append("text")
.attr("class", p.class)
.attr("dy", ".5em")
.text(p.accsor)
.call(rectText(i));
});
function transition(d) {
if (transitioning || !d) return;
transitioning = true;
var g2 = display(d);
var t1 = g1.transition().duration(750);
var t2 = g2.transition().duration(750);
// Update the domain only after entering new elements.
x.domain([d.x, d.x + d.dx]);
y.domain([d.y, d.y + d.dy]);
// Enable anti-aliasing during the transition.
svg.style("shape-rendering", null);
// Draw child nodes on top of parent nodes.
svg.selectAll(".depth").sort(function(a, b) { return a.depth - b.depth; });
// Fade-in entering text.
g2.selectAll("text").style("fill-opacity", 0);
// Transition to the new view.
textClassArr.forEach(function(d, i) {
var textClass = `text.${d.class}`;
var textPlacmt = rectText(i);
t1.selectAll(textClass).call(textPlacmt).style("fill-opacity", 0);
t2.selectAll(textClass).call(textPlacmt).style("fill-opacity", 1);
});
t1.selectAll("rect").call(rect);
t2.selectAll("rect").call(rect);
// Remove the old node when the transition is finished.
t1.remove().each("end", function() {
svg.style("shape-rendering", "crispEdges");
transitioning = false;
});
};
return g;
};
function rectText(i) {
return grantText;
function grantText(text) {
return text.attr("x", function(d) { return x(d.x) + 6; })
.attr("y", function(d) { return y(d.y) + (25 + (i * 18)); })
.attr("fill", "black");
};
};
function rect(rect) {
rect.attr("x", function(d) { return x(d.x); })
.attr("y", function(d) { return y(d.y); })
.attr("width", function(d) { return x(d.x + d.dx) - x(d.x); })
.attr("height", function(d) { return y(d.y + d.dy) - y(d.y); })
.attr("fill", function(d) { return colorScale(d.name);});
};
function name(d) {
return d.parent
? `${name(d.parent)}.${d.name} - Total Segments: ${d.value} -
Sample Size: ${d.sum.toLocaleString()}`
: d.name;
};
};
YEAR GRANT_PROGRAM GRANT_AWARD
A1 ACTIVE MOBILE APP USAGE 1107
A1 CREDIT SCORES 1140
A1 DAYTIME TV SHOWS 48
A1 DIGITAL MUSIC SUBSRIPTION 1550
A1 EARLY EVENING TV SHOWS 138
A1 EARLY EVENING WEEKEND TV SHOWS 212
A1 EARLY MORNING TV SHOWS 68
A1 ECONOMIC OUTLOOK 1140
A1 FOOD LIFESTYLE 1140
A1 GENERAL SEGMENT 1140
A1 HEALTH AND WELLBEING 1140
A1 LATE NIGHT TV SHOWS 79
A1 LATE NIGHT WEEKEND 48
A1 LEISURE ACTIVITIES 11633
A1 MOBILE ADOPTION STYLE 1140
A1 MOVIE GENRE 2887
A1 ONE A WEEK TV SHOWS 3262
A1 ONLINE ACTIVITY 13313
A1 PHYSICAL FITNESS LOCATION 2028
A1 RETAIL SHOPPING 1140
A1 SOCIAL MEDIA USAGE 809
A1 TECHNOLOGY ADOPTION 1140
A1 TRAVEL 1925
A1 VIDEO STREAMING SUBSCRIPTIONS 1748
A1 VIEWS ON DATA PRIVACY 1140
A1 WATCHED ON AMAZON 175
A1 WATCHED ON HULU 36
A1 WATCHED ON NETFLIX 212
A1 WEBSITES VISITED 14800
A1 WEEKEND NEWS 137
A2 ACTIVE MOBILE APP USAGE 1272
A2 CREDIT SCORES 1296
A2 DAYTIME TV SHOWS 85
A2 DIGITAL MUSIC SUBSRIPTION 1802
A2 EARLY EVENING TV SHOWS 129
A2 EARLY EVENING WEEKEND TV SHOWS 186
A2 EARLY MORNING TV SHOWS 70
A2 ECONOMIC OUTLOOK 1296
A2 FOOD LIFESTYLE 1296
A2 GENERAL SEGMENT 1295
A2 HEALTH AND WELLBEING 1296
A2 LATE NIGHT TV SHOWS 74
A2 LATE NIGHT WEEKEND 42
A2 LEISURE ACTIVITIES 13143
A2 MOBILE ADOPTION STYLE 1296
A2 MOVIE GENRE 3319
A2 ONE A WEEK TV SHOWS 3629
A2 ONLINE ACTIVITY 15344
A2 PHYSICAL FITNESS LOCATION 1786
A2 RETAIL SHOPPING 1296
A2 SOCIAL MEDIA USAGE 957
A2 TECHNOLOGY ADOPTION 1296
A2 TRAVEL 2152
A2 VIDEO STREAMING SUBSCRIPTIONS 2057
A2 VIEWS ON DATA PRIVACY 1296
A2 WATCHED ON AMAZON 153
A2 WATCHED ON HULU 42
A2 WATCHED ON NETFLIX 290
A2 WEBSITES VISITED 17801
A2 WEEKEND NEWS 121
A3 ACTIVE MOBILE APP USAGE 769
A3 CREDIT SCORES 790
A3 DAYTIME TV SHOWS 56
A3 DIGITAL MUSIC SUBSRIPTION 1011
A3 EARLY EVENING TV SHOWS 71
A3 EARLY EVENING WEEKEND TV SHOWS 108
A3 EARLY MORNING TV SHOWS 48
A3 ECONOMIC OUTLOOK 790
A3 FOOD LIFESTYLE 790
A3 GENERAL SEGMENT 790
A3 HEALTH AND WELLBEING 790
A3 LATE NIGHT TV SHOWS 47
A3 LATE NIGHT WEEKEND 21
A3 LEISURE ACTIVITIES 7159
A3 MOBILE ADOPTION STYLE 790
A3 MOVIE GENRE 1773
A3 ONE A WEEK TV SHOWS 2574
A3 ONLINE ACTIVITY 8829
A3 PHYSICAL FITNESS LOCATION 443
A3 RETAIL SHOPPING 790
A3 SOCIAL MEDIA USAGE 604
A3 TECHNOLOGY ADOPTION 790
A3 TRAVEL 1227
A3 VIDEO STREAMING SUBSCRIPTIONS 1223
A3 VIEWS ON DATA PRIVACY 790
A3 WATCHED ON AMAZON 83
A3 WATCHED ON HULU 18
A3 WATCHED ON NETFLIX 134
A3 WEBSITES VISITED 9521
A3 WEEKEND NEWS 78
A4 ACTIVE MOBILE APP USAGE 448
A4 CREDIT SCORES 468
A4 DAYTIME TV SHOWS 38
A4 DIGITAL MUSIC SUBSRIPTION 505
A4 EARLY EVENING TV SHOWS 46
A4 EARLY EVENING WEEKEND TV SHOWS 46
A4 EARLY MORNING TV SHOWS 32
A4 ECONOMIC OUTLOOK 468
A4 FOOD LIFESTYLE 468
A4 GENERAL SEGMENT 466
A4 HEALTH AND WELLBEING 468
A4 LATE NIGHT TV SHOWS 28
A4 LATE NIGHT WEEKEND 14
A4 LEISURE ACTIVITIES 3675
A4 MOBILE ADOPTION STYLE 468
A4 MOVIE GENRE 1058
A4 ONE A WEEK TV SHOWS 1532
A4 ONLINE ACTIVITY 4482
A4 PHYSICAL FITNESS LOCATION 152
A4 RETAIL SHOPPING 468
A4 SOCIAL MEDIA USAGE 313
A4 TECHNOLOGY ADOPTION 468
A4 TRAVEL 684
A4 VIDEO STREAMING SUBSCRIPTIONS 638
A4 VIEWS ON DATA PRIVACY 468
A4 WATCHED ON AMAZON 33
A4 WATCHED ON HULU 3
A4 WATCHED ON NETFLIX 81
A4 WEBSITES VISITED 5000
A4 WEEKEND NEWS 25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment