Created
March 31, 2018 23:23
-
-
Save Zhenmao/e82798d65c0d083745e58184bcc6d291 to your computer and use it in GitHub Desktop.
Parallel coordinates
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<meta charset="utf-8"> | |
<link rel="stylesheet" type="text/css" href="parallel-coordinates.css"> | |
<body> | |
<div id="chart"></div> | |
<script src="//d3js.org/d3.v4.min.js"></script> | |
<script src="parallel-coordinates.js"></script> | |
<script> | |
var divID = "#chart"; | |
var datafile = "user1.csv"; // Change to "user2.csv" or "user3.csv" to switch dataset | |
parcoords(divID, datafile); | |
</script> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
body { | |
background: #222222; | |
} | |
svg { | |
font: 12px sans-serif; | |
} | |
svg text { | |
fill: #B6B6B6; | |
} | |
.background path { | |
fill: none; | |
stroke: #ddd; | |
shape-rendering: crispEdges; | |
} | |
.foreground path { | |
fill: none; | |
stroke-width: 1.5px; | |
/* stroke: steelblue; */ | |
} | |
.brush .extent { | |
fill-opacity: .3; | |
stroke: #fff; | |
shape-rendering: crispEdges; | |
} | |
.axis line, | |
.axis path { | |
fill: none; | |
stroke: #B6B6B6; | |
shape-rendering: crispEdges; | |
} | |
.axis-title { | |
font-size: 12px; | |
} | |
.axis text { | |
text-shadow: 0 1px 0 #222222, 1px 0 0 #222222, 0 -1px 0 #222222, -1px 0 0 #222222; | |
} | |
.slider-track, | |
.slider-handle { | |
fill: #B6B6B6; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var parcoords = function(id, datafile) { | |
var margin = { top: 30, right: 20, bottom: 50, left: 60 }, | |
width = 800 - margin.left - margin.right, | |
height = 500 - margin.top - margin.bottom; | |
var x = d3.scalePoint().rangeRound([0, width]).padding(0), | |
y = {}; | |
var line = d3.line(), | |
axis = d3.axisLeft(); | |
var dataByHour; | |
var dimensions; | |
var currentHour = 0; | |
var hours = d3.range(24); | |
var svg = d3.select(id).append("svg") | |
.attr("width", width + margin.left + margin.right) | |
.attr("height", height + margin.top + margin.bottom) | |
.append("g") | |
.attr("transform", "translate(" + margin.left + "," + margin.top + ")"); | |
var background = svg.append("g") | |
.attr("class", "background"); | |
var foreground = svg.append("g") | |
.attr("class", "foreground"); | |
d3.csv(datafile, function(error, data) { | |
dataByHour = d3.nest() | |
.key(function(d) { | |
return d.Hour; | |
}) | |
.map(data); | |
// Extract the list of dimensions and create a scale for each. | |
dimensions = d3.keys(data[0]).filter(function (d) { | |
return d != "Hour" && d != "Color" && (y[d] = d3.scaleLinear() | |
.domain(d3.extent(data, function (p) { return +p[d]; })) | |
.range([height, 0])); | |
}); | |
x.domain(dimensions); | |
// Add a group element for each dimension. | |
var g = svg.selectAll(".dimension") | |
.data(dimensions) | |
.enter().append("g") | |
.attr("class", "dimension") | |
.attr("transform", function (d) { return "translate(" + x(d) + ")"; }); | |
// Add an axis and title. | |
g.append("g") | |
.attr("class", "axis") | |
.each(function (d) { d3.select(this).call(axis.scale(y[d])); }) | |
.append("text") | |
.attr("class", "axis-title") | |
.style("text-anchor", "middle") | |
.attr("y", -9) | |
.text(function (d) { return d; }); | |
// Add and store a brush for each axis. | |
g.append("g") | |
.attr("class", "brush") | |
.each(function (d) { | |
d3.select(this).call( | |
y[d].brush = d3.brushY() | |
.extent([[-8, 0], [8, height]]) | |
.on("brush", brush) | |
) | |
}) | |
.selectAll("rect") | |
.attr("x", -8) | |
.attr("width", 16); | |
addSlider(); | |
drawLines(); | |
}); | |
function addSlider() { | |
var sliderWidth = width, // Total width of the slider | |
sliderHeight = 4, // The height of the slider color rect | |
sliderLeft = 0, // The left positiion of the slider | |
sliderTop = 25, // The top position of the slider | |
handleRadius = 8; // The radius of slider circle handle | |
// X scale for slider | |
var x = d3.scaleLinear() | |
.domain([0, 23]) | |
.range([0, sliderWidth]) | |
.clamp(true); | |
// Slider container | |
var slider = svg.append("g") | |
.attr("class", "slider axis") | |
.attr("transform", "translate(" + sliderLeft + "," + (height + sliderTop) + ")"); | |
// Slider title | |
slider.append("text") | |
.attr("class", "slider-caption") | |
.attr("x", 0) | |
.attr("y", -9) | |
.text("Hour"); | |
// Slider ticks | |
var xAxis = d3.axisBottom(x) | |
.tickSize(13) | |
.tickFormat(d3.format("d")) | |
.tickValues(hours); | |
slider.call(xAxis) | |
.select(".domain") | |
.remove(); // Remove the domain line | |
// Slider circle handle | |
var handle = slider.append("circle") | |
.attr("class", "slider-handle") | |
.attr("cx", x(currentHour)) | |
.attr("cy", sliderHeight / 2) | |
.attr("r", handleRadius); | |
// Slider track | |
slider.append("rect") | |
.attr("class", "slider-track") | |
.attr("height", sliderHeight) | |
.attr("width", sliderWidth) | |
.call(d3.drag() | |
.on("start drag", dragging) | |
.on("end", drawLines)); | |
function dragging() { | |
// Find the year | |
currentHour = Math.round(x.invert(d3.event.x)); | |
// Transition the handle | |
handle.transition() | |
.duration(50) | |
.ease(d3.easeLinear) | |
.attr("cx", x(currentHour)); | |
} | |
} | |
function drawLines() { | |
var data = dataByHour.get(currentHour); | |
// Add grey background lines for context. | |
bgPath = background | |
.selectAll("path") | |
.data(data); | |
bgPath = bgPath.enter() | |
.append("path") | |
.merge(bgPath) | |
.transition() | |
.attr("d", path); | |
// Add blue foreground lines for focus. | |
fgPath = foreground | |
.selectAll("path") | |
.data(data); | |
fgPath = fgPath.enter() | |
.append("path") | |
.merge(fgPath) | |
.transition() | |
.attr("d", path) | |
.attr("stroke", function(d) { | |
return d.Color; | |
}); | |
} | |
// Returns the path for a given data point. | |
function path(d) { | |
return line(dimensions.map(function (p) { return [x(p), y[p](d[p])]; })); | |
} | |
// Handles a brush event, toggling the display of foreground lines. | |
function brush() { | |
var actives = [], | |
extents = []; | |
svg.selectAll(".brush").each(function(p, i) { | |
var extent = d3.brushSelection(this); | |
if (extent) { | |
actives.push(dimensions[i]); | |
extents.push(extent.map(y[dimensions[i]].invert)); | |
} | |
}); | |
fgPath.style("display", function (d) { | |
return actives.every(function (p, i) { | |
return extents[i][1] <= d[p] && d[p] <= extents[i][0]; | |
}) ? null : "none"; | |
}); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Hour | Satisfaction | Violation Pattern | Audit Log Modification | Access Pattern | Data Exfiltration | Privilege Change | Color | |
---|---|---|---|---|---|---|---|---|
0 | 0 | 0 | 0 | 0 | 0 | 0 | green | |
1 | 0 | 0 | 0 | 0 | 0 | 0 | green | |
2 | 0 | 47 | 51 | 32 | 54 | 1 | orange | |
3 | 0 | 93 | 57 | 86 | 98 | 44 | red | |
4 | 0 | 89 | 53 | 87 | 73 | 57 | red | |
5 | 0 | 93 | 79 | 82 | 49 | 22 | red | |
6 | 0 | 22 | 34 | 18 | 32 | 3 | green | |
7 | 2 | 7 | 1 | 20 | 0 | 0 | green | |
8 | 5 | 5 | 0 | 13 | 6 | 0 | green | |
9 | 15 | 24 | 0 | 16 | 12 | 1 | green | |
10 | 35 | 22 | 2 | 34 | 24 | 0 | green | |
11 | 67 | 56 | 3 | 33 | 23 | 15 | orange | |
12 | 78 | 35 | 0 | 54 | 15 | 8 | orange | |
13 | 0 | 0 | 0 | 0 | 0 | 0 | green | |
14 | 22 | 43 | 24 | 12 | 45 | 12 | green | |
15 | 31 | 4 | 22 | 4 | 4 | 19 | green | |
16 | 16 | 3 | 12 | 3 | 19 | 25 | green | |
17 | 66 | 35 | 15 | 76 | 22 | 5 | orange | |
18 | 3 | 65 | 0 | 89 | 56 | 2 | orange | |
19 | 9 | 22 | 0 | 0 | 0 | 0 | green | |
20 | 2 | 15 | 13 | 0 | 0 | 0 | green | |
21 | 0 | 0 | 2 | 0 | 0 | 0 | green | |
22 | 0 | 0 | 0 | 0 | 0 | 0 | green | |
23 | 0 | 0 | 0 | 0 | 0 | 0 | green |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment