I have a dataset with a column called CategoryLevel1
which contains the names of groups. I applied the nest function to Categorylevel1
and generated a series of svgs based on the keys. I then created rectangles representing items in the entire dataset and repeated the rectangles in each svg. I applied a filter to each svg, so that only the dataset items with the key of that svg can be seen.
My real dataset is bigger than the toy dataset represented here. The result of the above code is a long webpage of svgs - very confusing. To make things more clear, I would like svgs to be grouped according to a column called CategoryLevel2
. Here is the effect I am after:
Here's what I have so far:
var doc = `Manual Name CategoryLevel1 CategoryLevel2
DOG "General Furry, Program and Subject Files" Average Quantity and Planning Edibles
TR Senate Committee on animal Standards Bowl and Plate Design Edibles
TR Published Canine Bowl and Plate Design Edibles
TR Canine case files Bowl and Plate Design Edibles
DOG Canine Files Avoiding Neck Strain Edibles
DOG Canine Files Drooling Edibles
DOG Canine Files Drooling Edibles
DG ADVERTISING At home At home
DG PROMOTIONS At home At home
DG3 Publications At home At home
TR Public and Information Services At home At home
TR Petting Services Getting special treats At home
TR Petting Services Getting special treats At home
TR Petting Services Getting special treats At home
TR Petting Services Getting special treats At home
TR Petting Services Getting special treats At home
TR Petting Services Getting special treats At home
DG DEVELOPMENT Optimal time of day - walking Walks and outings
DG INCOME AND REVENUE Optimal time of day - walking Walks and outings
TR Fundraising Optimal time of day - walking Walks and outings
TR Fundraising Optimal time of day - walking Walks and outings
DG DEVELOPMENT Optimal time of day - walking Walks and outings
DG INCOME AND REVENUE Optimal time of day - walking Walks and outings
TR Wishbone Protective Measures Walks and outings
TR Wishbone Protective Measures Walks and outings
DG Wishbone Observant of Limps Etc Walks and outings
DOG Wishbone Observant of Limps Etc Walks and outings
TR Wishbone Observant of Limps Etc Walks and outings`;
const data = d3.tsvParse(doc, function(d) {
return {
Manual: d.Manual,
Name: d.Name,
CategoryLevel1: d.CategoryLevel1,
CategoryLevel2: d.CategoryLevel2
};
});
var nest = d3.nest()
.key(function(d) {
return d.CategoryLevel1;
})
.entries(data);
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0)
var height = 100,
width = 300;
var color = d3.scaleOrdinal(["#edf8fb", "#b3cde3", "#8c96c6", "#88419d"]);
/* var svg = d3.select("body").append("svg").attr("height", "100%").attr("width", "100%");
var g = d3.select("svg").attr("height", "100%").attr("width", "100%"); */
var svgs = d3.select("body")
.selectAll("svg")
.data(nest)
.enter()
.append('svg')
.attr("width", width)
.attr("height", height + 20);
svgs.append("text")
.attr('class', 'label')
.data(nest)
.attr('x', width / 2)
.attr('y', height)
.text(function(d) {
return d.key;
})
.attr('text-anchor', 'middle')
svgs.selectAll("rect")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.filter(function(d, i) {
const x = d3.select(this.parentNode).datum();
return x.key == d.CategoryLevel1 ? 1 : 0;
})
.attr("height", function(d) {
return 50;
})
.attr("width", "5")
.attr("x", function(d, i) {
return i * 10;
})
.attr("y", 0)
.attr("fill", function(d) {
return color(d.Manual)
})
.on("mouseover", function(d, i) {
div.transition()
.duration(200)
.style("opacity", .9);
div.html(`${d.Name}`)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 50) + "px");
})
.on("mouseout", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
});
.page {
width: 90%;
margin: auto;
}
.menu {
height: 100px;
background-color: #B2D6FF;
/* Medium blue */
}
.sidebar {
height: 50px;
width: 15%;
background-color: #F09A9D;
float: inline-start;
display: block;
margin: 0.1%;
/* Red */
}
.title {
width: 100%;
background-color: none;
display: inline-block;
float: inline-start;
/* Yellow */
}
div.tooltip {
position: absolute;
text-align: center;
width: auto;
height: auto;
padding: 3px;
font: 12px sans-serif;
border: 0px;
border-radius: 3px;
pointer-events: none;
background: lightgrey;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Mapping Dog Care Manuals</title>
<script src="https://d3js.org/d3.v4.min.js"></script>
</head>
</html>
What I have tried:
I tried creating svgs representing CategoryLevel2, then appending an "innerSVG" and running a code that would generate CategoryLevel1 svgs. The problem is in the filter line - it doesn't access the correct parent of CategoryLevel1:
.filter(function(d, i) {
const x = d3.select(this.parentNode).datum();
return x.key == d.CategoryLevel1 ? 1 : 0;
})
I also tried using the "transform, translate" function to separate the rectangles based on Categorylevel1, but rejected this because it would be tricky to move the text associated with CategoryLevel1.
I'm now working on trying to use one of the d3.hierarchy layouts. The problem is that once I apply d3.stratify to the dataset, the results cannot be used to generate a series of svgs. That is to say, nothing shows up in the DOM when the following code is applied: (FYI I also replaced treeData with root.descendants() etc -
var treeData = d3.stratify()
.id(function(d) { return d.CategoryLevel1; })
.parentId(function(d) { return d.CategoryLevel2; })
(data);
var svgs = d3.select("chart")
.selectAll("svg")
.data(treeData)
.enter()
.append('svg')
.attr("width", width)
.attr("height", height + 20);
There is an important information missing here: you didn't told us how do you want to separate the CategoryLevel2
groups: In the same row? In the same column? Some other pattern?
Therefore, in my solution I'll use a container <div>
with display: flex
to separate the groups in 3 columns, one for each value of CategoryLevel2
(which are Edibles
, At Home
Walks and outings
).
This can be done just using CategoryLevel2
as the first key in the nest function:
var nest = d3.nest()
.key(function(d) {
return d.CategoryLevel2;
})
.key(function(d) {
return d.CategoryLevel1;
})
.entries(data);
This will create the following array:
[{key: "Edibles", values: Array},
{key: "At home", values: Array},
{key: "Walks and outings", values: Array}];
In each values
value, you'll have your original nest, like:
{
"key": "Edibles",
"values": [{
"key": "Average Quantity and Planning",
"values": [
//etc...
]
}, {
"key": "Bowl and Plate Design",
"values": [
//etc..
]
}, {
"key": "Avoiding Neck Strain",
"values": [
//etc...
]
}, {
"key": "Drooling",
"values": [
//etc
]
}]
}
And then you append the divs using the nested data:
var divs = d3.select(".container")
.selectAll(null)
.data(nest)
.enter()
.append("div")
.attr("class", "innerdiv");
Of course, don't forget to use the inner arrays for the real SVGs:
var svgs = divs.selectAll(null)
.data(function(d) {
return d.values;
})
.enter()
//etc...
Here is the code with those changes:
var doc = `Manual Name CategoryLevel1 CategoryLevel2
DOG "General Furry, Program and Subject Files" Average Quantity and Planning Edibles
TR Senate Committee on animal Standards Bowl and Plate Design Edibles
TR Published Canine Bowl and Plate Design Edibles
TR Canine case files Bowl and Plate Design Edibles
DOG Canine Files Avoiding Neck Strain Edibles
DOG Canine Files Drooling Edibles
DOG Canine Files Drooling Edibles
DG ADVERTISING At home At home
DG PROMOTIONS At home At home
DG3 Publications At home At home
TR Public and Information Services At home At home
TR Petting Services Getting special treats At home
TR Petting Services Getting special treats At home
TR Petting Services Getting special treats At home
TR Petting Services Getting special treats At home
TR Petting Services Getting special treats At home
TR Petting Services Getting special treats At home
DG DEVELOPMENT Optimal time of day - walking Walks and outings
DG INCOME AND REVENUE Optimal time of day - walking Walks and outings
TR Fundraising Optimal time of day - walking Walks and outings
TR Fundraising Optimal time of day - walking Walks and outings
DG DEVELOPMENT Optimal time of day - walking Walks and outings
DG INCOME AND REVENUE Optimal time of day - walking Walks and outings
TR Wishbone Protective Measures Walks and outings
TR Wishbone Protective Measures Walks and outings
DG Wishbone Observant of Limps Etc Walks and outings
DOG Wishbone Observant of Limps Etc Walks and outings
TR Wishbone Observant of Limps Etc Walks and outings`;
const data = d3.tsvParse(doc, function(d) {
return {
Manual: d.Manual,
Name: d.Name,
CategoryLevel1: d.CategoryLevel1,
CategoryLevel2: d.CategoryLevel2
};
});
var nest = d3.nest()
.key(function(d) {
return d.CategoryLevel2;
})
.key(function(d) {
return d.CategoryLevel1;
})
.entries(data);
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0)
var height = 80,
width = 150;
var color = d3.scaleOrdinal(["#edf8fb", "#b3cde3", "#8c96c6", "#88419d"]);
/* var svg = d3.select("body").append("svg").attr("height", "100%").attr("width", "100%");
var g = d3.select("svg").attr("height", "100%").attr("width", "100%"); */
var divs = d3.select(".container")
.selectAll(null)
.data(nest)
.enter()
.append("div")
.attr("class", "innerdiv");
divs.append("p")
.html(function(d){
return d.key;
});
var svgs = divs.selectAll(null)
.data(function(d) {
return d.values;
})
.enter()
.append('svg')
.attr("width", width)
.attr("height", height + 20);
svgs.append("text")
.attr('class', 'label')
.attr('x', width / 2)
.attr('y', height)
.style("font-size", "10px")
.text(function(d) {
return d.key;
})
.attr('text-anchor', 'middle')
svgs.selectAll("rect")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.filter(function(d, i) {
const x = d3.select(this.parentNode).datum();
return x.key == d.CategoryLevel1 ? 1 : 0;
})
.attr("height", function(d) {
return 50;
})
.attr("width", "5")
.attr("x", function(d, i) {
return i * 10;
})
.attr("y", 0)
.attr("fill", function(d) {
return color(d.Manual)
})
.on("mouseover", function(d, i) {
div.transition()
.duration(200)
.style("opacity", .9);
div.html(`${d.Name}`)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 50) + "px");
})
.on("mouseout", function(d) {
div.transition()
.duration(500)
.style("opacity", 0);
});
div.tooltip {
position: absolute;
text-align: center;
width: auto;
height: auto;
padding: 3px;
font: 12px sans-serif;
border: 0px;
border-radius: 3px;
pointer-events: none;
background: lightgrey;
}
.container {
display: flex;
}
.innerdiv {
text-align: center;
font-size: 21px;
font-family: "Arial";
flex: 1 1 0;
}
.innerdiv + .innerdiv {
padding-left: 16px;
border-left: 2px solid lightgray;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Mapping Dog Care Manuals</title>
<script src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<div class="container"></div>
</body>
</html>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With