Измените d3. js область рассеяния и параллельные координаты и данные и данные после выбора <option>html - PullRequest
0 голосов
/ 31 марта 2020

У меня есть файл 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

...