Skip to content

Instantly share code, notes, and snippets.

@djokicx
Last active April 22, 2020 16:59
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 djokicx/4fcd66d72a7c0140f8f10014fbfbe31d to your computer and use it in GitHub Desktop.
Save djokicx/4fcd66d72a7c0140f8f10014fbfbe31d to your computer and use it in GitHub Desktop.
Yodlee Datasets
license: mit
We can make this file beautiful and searchable if this error is corrected: No commas found in this CSV file in line 0.
dataset
BASIC_AGG_DATA
ADVANCE_AGG_DATA
ACCT_PROFILE
DOCUMENTS
BASIC_AGG_DATA-BASIC_ACCOUNT_INFO
BASIC_AGG_DATA-ACCOUNT_DETAILS
BASIC_AGG_DATA-TRANSACTIONS
BASIC_AGG_DATA-HOLDINGS
BASIC_AGG_DATA-STATEMENTS
ADVANCE_AGG_DATA-INTEREST_DETAILS
ADVANCE_AGG_DATA-PAYMENT_DETAILS
ADVANCE_AGG_DATA-COVERAGE
ACCT_PROFILE-FULL_ACCT_NUMBER
ACCT_PROFILE-BANK_TRANSFER_CODE
ACCT_PROFILE-PAYMENT_PROFILE
ACCT_PROFILE-HOLDER_NAME
ACCT_PROFILE-HOLDER_DETAILS
DOCUMENTS-TAX
DOCUMENTS-STATEMENTS
DOCUMENTS-EBILLS
<!DOCTYPE html>
<head>
<meta charset="utf-8">
<link href="style.css" rel="stylesheet" type="text/css">
<title>SF Arrests</title>
</head>
<body>
<!--
hard-code the svg and various g layers so that even if the streets are loaded
and drawn last, they will still show up behind the symbols
-->
<svg width="960" height="700" id="vis">
<g id="details" pointer-events="none"></g>
</svg>
<script src="https://d3js.org/d3.v5.min.js"></script>
<script>
const data = "./datasets.csv";
const diameter = 500;
const pad = 14;
const height = 500;
const width = 960;
const r = 13;
const radialLine = d3.linkRadial()
.angle(d => d.theta + Math.PI / 2) // rotate, 0 angle is mapped differently here
.radius(d => d.radial);
const numberFormat = d3.format(".2~s");
let body;
let details;
d3.csv(data).then(function(original) {
nodes = new Set(original.map(row => row.dataset))
let processed = original.map(function(row) {
let parent = row.dataset.substring(0, row.dataset.lastIndexOf("-"));
return {
name : row.dataset,
parent: nodes.has(parent) ? parent : "Datasets",
}
})
processed.push(
{
name: "Datasets",
parent: "",
}
)
let root = d3.stratify()
.id(function(row) { return row.name; })
.parentId(function(row) {return row.parent;})
(processed);
root.count();
root.each(function(node) {
// copy this calculation since value is sometimes overwritten
node.data.leaves = node.value;
})
root.sum(row => row.count)
root.each(function(node) {
// copy this calculation since value is sometimes overwritten
node.data.total = node.value;
})
let data = root;
data.sort(function(a, b) {
return b.height - a.height || b.count - a.count;
});
color = d3.scaleSequential([0, root.height], d3.interpolateBlues)
let layout = d3.cluster().size([2 * Math.PI, (diameter / 2) - pad]);
layout(data);
data.each(function(node) {
node.theta = node.x;
node.radial = node.y;
var point = toCartesian(node.radial, node.theta);
node.x = point.x;
node.y = point.y;
});
let svg = d3.select("#vis")
.style("width", width)
.style("height", height);
let plot = svg.append("g")
.attr("id", "plot")
.attr("transform", `translate(${width/ 3}, ${height/2})`);
const extraInfo = svg.select("g#details")
details = extraInfo.append("foreignObject")
.attr("id", "details")
.attr("width", 960)
.attr("height", 600)
.attr("x", width/ 2 + 150)
.attr("y", height/5);
body = details.append("xhtml:body")
.style("text-align", "left")
.style("background", "none")
.html("<p>N/A</p>");
details.style("visibility", "hidden");
drawNodes(plot.append("g"), data.descendants(), true);
drawLinks(plot.append("g"), data.links(), radialLine);
});
function toCartesian(r, theta) {
return {
x: r * Math.cos(theta),
y: r * Math.sin(theta)
};
}
function drawLinks(g, links, generator) {
let paths = g.selectAll('path')
.data(links)
.enter()
.append('path')
.attr('d', generator)
.attr('class', 'link');
}
function drawNodes(g, nodes, raise) {
let circles = g.selectAll('circle')
.data(nodes, node => node.data.name)
.enter()
.append('circle')
.attr('r', d => d.r ? d.r : r)
.attr('cx', d => d.x)
.attr('cy', d => d.y)
.attr('id', d => d.data.name)
.attr('class', 'node')
.style('fill', d => color(d.depth));
setupEvents(g, circles, raise);
}
function setupEvents(g, selection, raise) {
selection.on('mouseover.highlight', function(d) {
// https://github.com/d3/d3-hierarchy#node_path
// returns path from d3.select(this) node to selection.data()[0] root node
let path = d3.select(this).datum().path(selection.data()[0]);
// select all of the nodes on the shortest path
let update = selection.data(path, node => node.data.name);
// highlight the selected nodes
update.classed('selected', true);
if (raise) {
update.raise();
}
});
selection.on('mouseout.highlight', function(d) {
let path = d3.select(this).datum().path(selection.data()[0]);
let update = selection.data(path, node => node.data.name);
update.classed('selected', false);
});
// show tooltip text on mouseover (hover)
selection.on('mouseover.tooltip', function(d) {
let node = d3.select(this);
let nodeData = node.datum();
let nodeChildren = nodeData.children;
console.log(node);
console.log(nodeChildren);
if(nodeChildren) {
nodeChildren.forEach(element => {
let childSelector = '#' + element.data.name;
showTooltip(g, d3.select(childSelector));
});
}
console.log(node.datum());
let parentName = node.datum().id.substring(0, node.datum().id.lastIndexOf("-"));
if(!parentName.length == 0) {
let parent = d3.select("circle#" + parentName + ".node")
showTooltip(g, parent);
const html = `
<table border="0" cellspacing="0" cellpadding="2">
<tbody>
<tr>
<th>Attribute:</th>
<td id="attribute">${node.datum().id.substring(node.datum().id.lastIndexOf("-") + 1)}</td>
</tr>
<tr>
<th>Dataset:</th>
<td>${parentName}</td>
</tr>
<tr>
<th>Description:</th>
<td>Full account number</td>
</tr>
<tr>
<th>Qualities:</th>
<td>Sensitive information</td>
</tr>
<tr>
<th>Containers:</th>
<td>
<ul>
<li>Bank</li>
<li>Investment</li>
<li>Credit Card</li>
<li>Insurance</li>
<li>Loan</li>
<li>Reward</li>
</ul>
</td>
</tr>
<tr>
<th>Regions:</th>
<td>
<ul>
<li>US</li>
<li>UK</li>
<li>Canada</li>
<li>Insurance</li>
<li>Australia</li>
<li>India</li>
</ul>
</td>
</tr>
</tbody>
</table>
`;
body.html(html);
details.style("visibility", "visible");
}
showTooltip(g, node);
});
// remove tooltip text on mouseout
selection.on('mouseout.tooltip', function(d) {
g.selectAll("#tooltip").remove();
details.style("visibility", "hidden");
});
}
function showTooltip(g, node) {
let gbox = g.node().getBBox(); // get bounding box of group BEFORE adding text
let nbox = node.node().getBBox(); // get bounding box of node
// calculate shift amount
let dx = nbox.width / 2;
let dy = nbox.height / 2;
// retrieve node attributes (calculate middle point)
let x = nbox.x + dx;
let y = nbox.y + dy;
// get data for node
let datum = node.datum();
let name = node.datum().id.substring(node.datum().id.lastIndexOf("-") + 1);
// let name = datum.data.name.substring(datum.data.name)
text = `${name}`;
// create tooltip
let tooltip = g.append('text')
.text(text)
.attr('x', x)
.attr('y', y)
.attr('dy', -dy - 4) // shift upward above circle
.attr('text-anchor', 'middle') // anchor in the middle
.attr('id', 'tooltip');
// it is possible the tooltip will fall off the edge of the
// plot area. we can detect when this happens, and set the
// text anchor appropriately
// get bounding box for the text
let tbox = tooltip.node().getBBox();
// if text will fall off left side, anchor at start
if (tbox.x < gbox.x) {
tooltip.attr('text-anchor', 'start');
tooltip.attr('dx', -dx); // nudge text over from center
}
// if text will fall off right side, anchor at end
else if ((tbox.x + tbox.width) > (gbox.x + gbox.width)) {
tooltip.attr('text-anchor', 'end');
tooltip.attr('dx', dx);
}
// if text will fall off top side, place below circle instead
if (tbox.y < gbox.y) {
tooltip.attr('dy', dy + tbox.height);
}
}
</script>
</body>
svg {
border: 1px dotted whitesmoke;
}
.node {
stroke: silver;
stroke-width: 3px;
}
.link {
fill: none;
stroke: silver;
stroke-width: 1px;
}
.selected {
stroke: #F05E23 !important;
stroke-width: 3px;
}
text#tooltip {
font-family: sans-serif;
font-size: 12px;
text-shadow: 1px 1px 0 #fff, 1px -1px 0 #fff, -1px 1px 0 #fff, -1px -1px 0 #fff;
font-weight: 900;
}
td#attribute {
font-weight: 900;
}
th {
font-weight: 100;
}
#details {
font-family: sans-serif;
font-size: 12px;
text-shadow: 1px 1px 0 #fff, 1px -1px 0 #fff, -1px 1px 0 #fff, -1px -1px 0 #fff;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment