У меня есть файл HTML с двумя кнопками выбора, диаграммой рассеяния и графиком параллельных координат. Диаграмма рассеяния позволяет нанести sh на точки, которые затем выделены на соответствующих параллельных линиях координат. То, что я хотел бы сделать, это изменить данные (и, таким образом, домены оси) относительно выбранной опции в кнопках. Я не знаю, как это сделать, так как мне нужно отфильтровать набор данных (файл CSV), чтобы показать такие данные. Вот мой код:
<!DOCTYPE html>
<meta charset="utf-8">
<head>
<style type="text/css">
svg {
font: 10px sans-serif;
}
.background path {
fill: none;
stroke: #ddd;
shape-rendering: crispEdges;
}
.foreground path {
fill: none;
stroke: steelblue;
}
.axis line,
.axis path {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.axis text {
fill: black;
text-shadow: 0 1px 0 #fff, 1px 0 0 #fff, 0 -1px 0 #fff, -1px 0 0 #fff;
cursor: move;
}
</style>
</head>
<body>
<div class="selectcontainer">
<div id="select1">
<select id="Category 1" class="chosen">
<option value="" disabled selected>Choose category 1...</option>
<option>Education</option>
<option>Lifestyle</option>
<option>Health & Fitness</option>
<option>News</option>
<option>Music</option>
<option>Social Networking</option>
<option>Photo & Video</option>
<option>Games</option>
<option>Food & Drink</option>
<option>Utilities</option>
<option>Weather</option>
<option>Reference</option>
<option>Catalogs</option>
<option>Shopping</option>
<option>Medical</option>
<option>Finance</option>
<option>Travel</option>
<option>Book</option>
<option>Business</option>
<option>Entertainment</option>
<option>Navigation</option>
<option>Sports</option>
<option>Productivity</option>
</select>
</div>
<div id="select2">
<select id="Category 2" class="chosen">
<option value="" disabled selected>Choose category 2...</option>
<option>Education</option>
<option>Lifestyle</option>
<option>Health & Fitness</option>
<option>News</option>
<option>Music</option>
<option>Social Networking</option>
<option>Photo & Video</option>
<option>Games</option>
<option>Food & Drink</option>
<option>Utilities</option>
<option>Weather</option>
<option>Reference</option>
<option>Catalogs</option>
<option>Shopping</option>
<option>Medical</option>
<option>Finance</option>
<option>Travel</option>
<option>Book</option>
<option>Business</option>
<option>Entertainment</option>
<option>Navigation</option>
<option>Sports</option>
<option>Productivity</option>
</select>
<div>
</div>
<div id="scatterArea"></div>
<div id="parallelArea"></div>
<script src="https://d3js.org/d3.v4.min.js" charset="utf-8"></script>
<script>
var csvColumns, focus;
var dataSelection=[];
var margin = {top: 20, right: 20, bottom: 110, left: 50},
margin2 = {top: 430, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom,
height2 = 500 - margin2.top - margin2.bottom;
var x = d3.scaleLinear().range([0, width]),
x2 = d3.scaleLinear().range([0, width]),
y = d3.scaleLinear().range([height, 0]),
y2 = d3.scaleLinear().range([height2, 0]);
var xAxis = d3.axisBottom(x),
xAxis2 = d3.axisBottom(x2),
yAxis = d3.axisLeft(y);
var brush = d3.brushX()
.extent([[0, 0], [width, height2]])
.on("brush", brushed);
var brushTot=d3.brush()
.extent([[0,0],[width, height]])
.on("end", selected);
var color= d3.scaleOrdinal(d3.schemeCategory10);
d3.csv("static/pca-data.csv", function(error, data) {
csvColumns= d3.keys(data[0])
if (error) throw error;
leftCategory = "Finance"
rightCategory = "Social Networking"
data = data.filter(function(d){ return (d.prime_genre == leftCategory || d.prime_genre == rightCategory) });
data.forEach(function(d){
if (d.prime_genre == leftCategory) d.prime_genre = +1;
else d.prime_genre = +2;
});
drawParallel(data);
drawScatter(data);
})
/// START DRAWING SCATTER ///
function drawScatter(data){
var svg = d3.select("#scatterArea").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
svg.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
focus = svg.append("g")
.attr("class", "focus")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var context = svg.append("g")
.attr("class", "context")
.attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");
// set domains wrt X and Y values of the csv
x.domain(d3.extent(data, function(d) { return +d[csvColumns[0]]; }));
y.domain(d3.extent(data, function(d) { return +d[csvColumns[1]]; }));
x2.domain(x.domain());
y2.domain(y.domain());
// append scatter plot to main chart area
var dots = focus.append("g");
dots.attr("clip-path", "url(#clip)");
dots.selectAll("dot")
.data(data)
.enter().append("circle")
.attr('class', 'dot')
.attr("r",5)
.attr("fill","grey")
.attr("opacity",".3")
.attr("cx", function(d) { return x(+d[csvColumns[0]]); })
.attr("cy", function(d) { return y(+d[csvColumns[1]]); })
.style("fill", function(d) {return color(d[csvColumns[2]]); });
// add x axis
focus.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// add y axis
focus.append("g")
.attr("class", "axis axis--y")
.call(yAxis);
// add x axis label
focus.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - margin.left)
.attr("x",0 - (height / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text(csvColumns[1]);
// add y axis label
svg.append("text")
.attr("transform",
"translate(" + ((width + margin.right + margin.left)/2) + " ," +
(height + margin.top + margin.bottom) + ")")
.style("text-anchor", "middle")
.text(csvColumns[0]);
// add brush effect
focus.append("g")
.attr("class", "brushT")
.call(brushTot);
}
//////////END SCATTER //////////////////////
//////////START DRAWING PARALLEL////////////
function drawParallel(data){
var margin = {top: 30, right: 10, bottom: 10, left: 36},
width = 960 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
var x = d3.scaleBand().rangeRound([0, width+100]).padding(.1),
y = {},
dragging = {};
// broken lines spreading across the parallelArea
var line = d3.line(),
background,
foreground,
extents;
// parallelArea svg creation
var svg = d3.select("#parallelArea").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 + ")");
// add domains to parallelArea only for attributes (no X-Y coordinates)
x.domain(dimensions = d3.keys(data[0]).filter(function(d) {
if ((d == "X") || (d == "Y")) {
return false;
}
return y[d] = d3.scaleLinear()
.domain(d3.extent(data, function(p) {
return +p[d]; }))
.range([height, 0]);
}));
extents = dimensions.map(function(p) { return [0,0]; });
background = svg.append("g")
.attr("class", "background")
.selectAll("path")
.data(data)
.enter().append("path")
.attr("class","backpath")
.attr("d", path);
foreground = svg.append("g")
.attr("class", "foreground")
.selectAll("path")
.data(data)
.enter().append("path")
.attr("class","forepath")
.attr("d", path);
var g = svg.selectAll(".dimension")
.data(dimensions)
.enter().append("g")
.attr("class", "dimension")
.attr("transform", function(d) { return "translate(" + x(d) + ")"; })
.call(d3.drag()
.subject(function(d) { return {x: x(d)}; })
.on("start", function(d) {
dragging[d] = x(d);
background.attr("visibility", "hidden");
})
.on("drag", function(d) {
dragging[d] = Math.min(width, Math.max(0, d3.event.x));
foreground.attr("d", path);
dimensions.sort(function(a, b) { return position(a) - position(b); });
x.domain(dimensions);
g.attr("transform", function(d) { return "translate(" + position(d) + ")"; })
})
.on("end", function(d) {
delete dragging[d];
transition(d3.select(this)).attr("transform", "translate(" + x(d) + ")");
transition(foreground).attr("d", path);
background
.attr("d", path)
.transition()
.delay(500)
.duration(0)
.attr("visibility", null);
}));
// Add an axis and title.
g.append("g")
.attr("class", "axis")
.each(function(d) { d3.select(this).call(d3.axisLeft(y[d]));})
.append("text")
.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 start", brushstart).on("brush", brush_parallel_chart));
})
.selectAll("rect")
.attr("x", -8)
.attr("width", 16);
function position(d) {
var v = dragging[d];
return v == null ? x(d) : v;
}
function transition(g) {
return g.transition().duration(500);
}
// Returns the path for a given data point.
function path(d) {
return line(dimensions.map(function(p) { return [position(p), y[p](d[p])]; }));
}
function brushstart() {
d3.event.sourceEvent.stopPropagation();
}
// Handles a brush event, toggling the display of foreground lines.
function brush_parallel_chart() {
for(var i=0;i<dimensions.length;++i){
if(d3.event.target==y[dimensions[i]].brush) {
extents[i]=d3.event.selection.map(y[dimensions[i]].invert,y[dimensions[i]]);
}
}
foreground.style("display", function(d) {
return dimensions.every(function(p, i) {
if(extents[i][0]==0 && extents[i][0]==0) {
return true;
}
return extents[i][1] <= d[p] && d[p] <= extents[i][0];
}) ? null : "none";
});
}
}
/////////END PARALLEL////////
//create brush function redraw scatterplot with selection
function brushed() {
var selection = d3.event.selection;
console.log(selection)
x.domain(selection.map(x2.invert, x2));
focus.selectAll(".dot")
.attr("cx", function(d) { return x(d[csvColumns[0]]); })
.attr("cy", function(d) { return y(d[csvColumns[1]]); });
focus.select(".axis--x").call(xAxis);
}
function selected(){
dataSelection=[]
var selection= d3.event.selection;
if (selection != null){
focus.selectAll(".dot")
.style("opacity",function(d){
if ((x(d[csvColumns[0]]) > selection[0][0]) && (x(d[csvColumns[0]]) < selection[1][0]) && (y(d[csvColumns[1]]) > selection[0][1]) && (y(d[csvColumns[1]]) < selection[1][1])) {
dataSelection.push(d.id)
return "1.0"
}
else
{
return "0.3"
}
})
}
else
{
focus.selectAll(".dot")
.style("fill",function(d) {return color(d[csvColumns[2]]); })
.style("opacity",".3")
console.log("reset");
}
d3.select("#parallelArea").selectAll(".forepath")
.style("stroke","steelblue")
var c=d3.select("#parallelArea").selectAll(".forepath")
.style("stroke",function(d){
if ((x(d[csvColumns[0]]) > selection[0][0]) && (x(d[csvColumns[0]]) < selection[1][0]) && (y(d[csvColumns[1]]) > selection[0][1]) && (y(d[csvColumns[1]]) < selection[1][1])) {
dataSelection.push(d.id)
return "red"
}
else
{
return "steelblue"
}
})
}
</script>
</body>
Как видите, категории теперь передаются в виде строк, но я хотел бы пропустить их через выбранный параметр и обновить все при выборе других категорий. Я пытался прочитать этот код https://bl.ocks.org/NGuernse/9e4b5232394d853bd76d94bde102fa9c, но, похоже, он сильно отличается от моего ... вот мой набор данных CSV: https://easyupload.io/5xxs3a