javascript - Making an interactive scatter plot with d3? - Stack Overflow

I have a scatter plot based on this example .What i want to do is add interactivity by allowing the use

I have a scatter plot based on this example .

What i want to do is add interactivity by allowing the user to choose the x and y dimensions, as well as the size of the circles based on one of the columns in the data file. When they choose something, the graph should automatically scale the axis.

How would i go about doing this? Here is my code so far:

Thanks.

<!DOCTYPE html>
<meta charset="utf-8">
<style>

/*set the axis line color, dot stroke, font size, and font position*/
body {
  font: 15px sans-serif;
}

.name{
  position: relative;
  top: 90px;
  text-align: left;
  font-weight: bold;
}

.title {
  position: relative;
  text-align: left;
  font-size: 25px;
}

.axis path,
.axis line {
  fill: none;
  stroke: #000;
  shape-rendering: crispEdges;
}

.dot {
  stroke: #000;
}

#filter {
  position: absolute;
}

#mark {
  padding-left: 150px;
  position: inherit;
}

#xAXs {
  position: relative;
  left: 290px;
  bottom: 30px;
}

#yAXs {
position: relative;
bottom: 30px;
left: 315px;

}

#label {
position: absolute;
top: 599px;
bottom: 125px;
left: 300px;
right: 0px;
}

#label2 {
position: absolute;
top: 599px;
bottom: 125px;
left: 430px;
right: 0px;
}

</style>

<body>


<script src="d3.min.js"></script>

<script>

var margin = {top: 20, right: 20, bottom: 30, left: 40},
    width = 960 - margin.left - margin.right,
    height = 500 - margin.top - margin.bottom;


var x = d3.scale.linear()
    .range([0, width]);

var y = d3.scale.linear()
    .range([height, 0]);


var color = d3.scale.category10();

var xAxis = d3.svg.axis()
    .scale(x)
    .orient("bottom");

var yAxis = d3.svg.axis()
    .scale(y)
    .orient("left");


var svg = d3.select("body").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 + ")");


d3.csv("iris.csv", function(error, data) {
  data.forEach(function(d) {
    d.petalLength = +d.petalLength;
    d.petalWidth = +d.petalWidth;
    d.sepalLength = +d.sepalLength;
    d.sepalWidth = +d.sepalWidth;
  });

  x.domain(d3.extent(data, function(d) { return d.petalWidth; })).nice();
  y.domain(d3.extent(data, function(d) { return d.petalLength; })).nice();

  svg.append("g")
      .attr("class", "x axis")
      .attr("transform", "translate(0," + height + ")")
      .call(xAxis)
    .append("text")
      .attr("class", "label")
      .attr("x", width)
      .attr("y", -6)
      .style("text-anchor", "end")
      .text("Petal Width (cm)");

  svg.append("g")
      .attr("class", "y axis")
      .call(yAxis)
    .append("text")
      .attr("class", "label")
      .attr("transform", "rotate(-90)")
      .attr("y", 6)
      .attr("dy", ".71em")
      .style("text-anchor", "end")
      .text("Petal Length (cm)")

 var circles = svg.selectAll(".dot")
      .data(data)
    .enter().append("circle")
      .attr("class", "dot")
      .attr("r", 3.5)
      .attr("cx", function(d) { return x(d.petalWidth); })
      .attr("cy", function(d) { return y(d.petalLength); })
      .style("fill", function(d) { return color(d.species); });


  var legend = svg.selectAll(".legend")
      .data(color.domain())
      .enter().append("g")
      .attr("class", "legend")
      .attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });


  legend.append("rect")
      .attr("x", width - 18)
      .attr("width", 18)
      .attr("height", 18)
      .style("fill", color);


  legend.append("text")
      .attr("x", width - 24)
      .attr("y", 9)
      .attr("dy", ".35em")
      .style("text-anchor", "end")
      .text(function(d) { return d; });



  d3.selectAll("[name=v]").on("change", function() {
      var selected = this.value;
      display = this.checked ? "inline" : "none";


  svg.selectAll(".dot")
      .filter(function(d) {return selected == d.species;})
      .attr("display", display);
      });



  d3.selectAll("[name=sepal]").on("change", function(d) {
     radius = this.value;


  svg.selectAll(".dot")
     console.log(radius);
     circles.attr("r", radius);
     });



  d3.select("[name=xAX]").on("change", function(){
    xAxy = this.value;
    console.log(xAxy)
  })

  d3.select("[name=yAX]").on("change", function(){
    yAxy = this.value;
    console.log(yAxy)
  })

});

</script>
<br><br>


  <div id="filter">
    <b>Species Filter:</b>
        <br>
    <input name='v' value="Iris-setosa" type="checkbox" checked>Iris-setosa
    </input>
        <br>
    <input name="v" value="Iris-versicolor" type="checkbox" checked >Iris-versicolor
    </input>
        <br>
    <input name="v" value="Iris-virginica" type="checkbox" checked >Iris-virginica
    </input>
  </div>


  <form id="mark">
    <b>Size of Mark:</b>
    <div><input type="radio" name="sepal" value='sepalWidth'>Sepal Width</div>
    <div><input type="radio" name="sepal" value="sepalLength">Sepal Length</div>
  </form>

<div id="label"><b>x-Axis:</b></div>
  <select name="xAX" id="xAXs">
        <option value ="petalWidth">petalWidth</option>
        <option value ="petalLength">petalLength</option>
        <option value ="sepalLength">sepalLength</option>
        <option value ="sepalWidth">sepalWidth</option>
  </select>

<div id="label2"><b>y-Axis:</b></div>
  <select name="yAX" id="yAXs">
        <option value ="petalLength">petalLength</option>
        <option value ="petalWidth">petalWidth</option>
        <option value ="sepalLength">sepalLength</option>
        <option value ="sepalWidth">sepalWidth</option>
  </select>  

  <br>
</body>

I have a scatter plot based on this example http://bl.ocks/mbostock/3887118.

What i want to do is add interactivity by allowing the user to choose the x and y dimensions, as well as the size of the circles based on one of the columns in the data file. When they choose something, the graph should automatically scale the axis.

How would i go about doing this? Here is my code so far: http://plnkr.co/edit/ZCdEBa79Y85Koz7MepXw?p=preview

Thanks.

<!DOCTYPE html>
<meta charset="utf-8">
<style>

/*set the axis line color, dot stroke, font size, and font position*/
body {
  font: 15px sans-serif;
}

.name{
  position: relative;
  top: 90px;
  text-align: left;
  font-weight: bold;
}

.title {
  position: relative;
  text-align: left;
  font-size: 25px;
}

.axis path,
.axis line {
  fill: none;
  stroke: #000;
  shape-rendering: crispEdges;
}

.dot {
  stroke: #000;
}

#filter {
  position: absolute;
}

#mark {
  padding-left: 150px;
  position: inherit;
}

#xAXs {
  position: relative;
  left: 290px;
  bottom: 30px;
}

#yAXs {
position: relative;
bottom: 30px;
left: 315px;

}

#label {
position: absolute;
top: 599px;
bottom: 125px;
left: 300px;
right: 0px;
}

#label2 {
position: absolute;
top: 599px;
bottom: 125px;
left: 430px;
right: 0px;
}

</style>

<body>


<script src="d3.min.js"></script>

<script>

var margin = {top: 20, right: 20, bottom: 30, left: 40},
    width = 960 - margin.left - margin.right,
    height = 500 - margin.top - margin.bottom;


var x = d3.scale.linear()
    .range([0, width]);

var y = d3.scale.linear()
    .range([height, 0]);


var color = d3.scale.category10();

var xAxis = d3.svg.axis()
    .scale(x)
    .orient("bottom");

var yAxis = d3.svg.axis()
    .scale(y)
    .orient("left");


var svg = d3.select("body").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 + ")");


d3.csv("iris.csv", function(error, data) {
  data.forEach(function(d) {
    d.petalLength = +d.petalLength;
    d.petalWidth = +d.petalWidth;
    d.sepalLength = +d.sepalLength;
    d.sepalWidth = +d.sepalWidth;
  });

  x.domain(d3.extent(data, function(d) { return d.petalWidth; })).nice();
  y.domain(d3.extent(data, function(d) { return d.petalLength; })).nice();

  svg.append("g")
      .attr("class", "x axis")
      .attr("transform", "translate(0," + height + ")")
      .call(xAxis)
    .append("text")
      .attr("class", "label")
      .attr("x", width)
      .attr("y", -6)
      .style("text-anchor", "end")
      .text("Petal Width (cm)");

  svg.append("g")
      .attr("class", "y axis")
      .call(yAxis)
    .append("text")
      .attr("class", "label")
      .attr("transform", "rotate(-90)")
      .attr("y", 6)
      .attr("dy", ".71em")
      .style("text-anchor", "end")
      .text("Petal Length (cm)")

 var circles = svg.selectAll(".dot")
      .data(data)
    .enter().append("circle")
      .attr("class", "dot")
      .attr("r", 3.5)
      .attr("cx", function(d) { return x(d.petalWidth); })
      .attr("cy", function(d) { return y(d.petalLength); })
      .style("fill", function(d) { return color(d.species); });


  var legend = svg.selectAll(".legend")
      .data(color.domain())
      .enter().append("g")
      .attr("class", "legend")
      .attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });


  legend.append("rect")
      .attr("x", width - 18)
      .attr("width", 18)
      .attr("height", 18)
      .style("fill", color);


  legend.append("text")
      .attr("x", width - 24)
      .attr("y", 9)
      .attr("dy", ".35em")
      .style("text-anchor", "end")
      .text(function(d) { return d; });



  d3.selectAll("[name=v]").on("change", function() {
      var selected = this.value;
      display = this.checked ? "inline" : "none";


  svg.selectAll(".dot")
      .filter(function(d) {return selected == d.species;})
      .attr("display", display);
      });



  d3.selectAll("[name=sepal]").on("change", function(d) {
     radius = this.value;


  svg.selectAll(".dot")
     console.log(radius);
     circles.attr("r", radius);
     });



  d3.select("[name=xAX]").on("change", function(){
    xAxy = this.value;
    console.log(xAxy)
  })

  d3.select("[name=yAX]").on("change", function(){
    yAxy = this.value;
    console.log(yAxy)
  })

});

</script>
<br><br>


  <div id="filter">
    <b>Species Filter:</b>
        <br>
    <input name='v' value="Iris-setosa" type="checkbox" checked>Iris-setosa
    </input>
        <br>
    <input name="v" value="Iris-versicolor" type="checkbox" checked >Iris-versicolor
    </input>
        <br>
    <input name="v" value="Iris-virginica" type="checkbox" checked >Iris-virginica
    </input>
  </div>


  <form id="mark">
    <b>Size of Mark:</b>
    <div><input type="radio" name="sepal" value='sepalWidth'>Sepal Width</div>
    <div><input type="radio" name="sepal" value="sepalLength">Sepal Length</div>
  </form>

<div id="label"><b>x-Axis:</b></div>
  <select name="xAX" id="xAXs">
        <option value ="petalWidth">petalWidth</option>
        <option value ="petalLength">petalLength</option>
        <option value ="sepalLength">sepalLength</option>
        <option value ="sepalWidth">sepalWidth</option>
  </select>

<div id="label2"><b>y-Axis:</b></div>
  <select name="yAX" id="yAXs">
        <option value ="petalLength">petalLength</option>
        <option value ="petalWidth">petalWidth</option>
        <option value ="sepalLength">sepalLength</option>
        <option value ="sepalWidth">sepalWidth</option>
  </select>  

  <br>
</body>
Share Improve this question asked Feb 9, 2015 at 4:47 LurkerLurker 431 silver badge3 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 2

Try this plunkr: http://plnkr.co/edit/MkZcXJPS7hrcWh3M0MZ1?p=preview

You were almost there, I just had to update a couple of your functions. The following describes the highlights:

d3.selectAll("[name=sepal]").on("change", function(d) {
   radius = this.value;

   svg.selectAll(".dot")
   console.log(radius);
   circles.attr("r", function(d) { return d[radius]; });
});

You are setting radius to the column in the csv that you want to read the radius from, so now you just have to update the radius of your circles in the svg, which is basically done the same way as when you originally set them up.

d3.select("[name=xAX]").on("change", function(){
  xAxy = this.value;
  console.log(xAxy)
  x.domain(d3.extent(data, function(d) { return d[xAxy]; })).nice();

  svg.select(".x.axis").transition().call(xAxis);

  svg.selectAll(".dot").transition().attr("cx", function(d) { 
      return x(d[xAxy]);
  });
  svg.selectAll(".x.axis").selectAll("text.label").text(axisNames[xAxy] + " (cm)");
});

d3.select("[name=yAX]").on("change", function(){
  yAxy = this.value;
  console.log(yAxy)
  y.domain(d3.extent(data, function(d) { return d[yAxy]; })).nice();
  svg.select(".y.axis").transition().call(yAxis);
  svg.selectAll(".dot").transition().attr("cy", function(d) { 
      return y(d[yAxy]);
  });
  svg.selectAll(".y.axis").selectAll("text.label").text(axisNames[yAxy] + " (cm)");
});

The changes for the x and y axes are basically the same as each other, with just the axis being referenced getting changed. Here I'm doing:

  1. Updating the domain with the new range (based on the value of xAxy or yAxy)
  2. Update the actual axis in the svg by setting up a transition and calling the xAxis or yAxis ponent again.
  3. Update the cx or cy position of each .dot
  4. Update the text on the axis by looking it up in a new array (axisNames) which gives a pretty print of the variable being graphed.

Your question is asking a lot. In general dynamic d3 visualizations follow the enter, exit, and update pattern. There's some great tutorial's out there to get you started.

To fix your specific code, you divide into three parts. First init the non-update-able portions of the plot:

var margin = {top: 20, right: 20, bottom: 30, left: 40},
    width = 500 - margin.left - margin.right,
    height = 500 - margin.top - margin.bottom;

var x = d3.scale.linear()
    .range([0, width]);

var y = d3.scale.linear()
    .range([height, 0]);

var color = d3.scale.category10();

var xAxis = d3.svg.axis()
    .scale(x)
    .orient("bottom");

var yAxis = d3.svg.axis()
    .scale(y)
    .orient("left");

var svg = d3.select("body").insert("svg",":first-child")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
    .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

// keep reference to axises
var xg = svg.append("g")
      .attr("class", "x axis")
      .attr("transform", "translate(0," + height + ")");
xg
  .append("text")
  .attr("class", "label")
  .attr("x", width)
  .attr("y", -6)
  .style("text-anchor", "end");

var yg = svg.append("g")
      .attr("class", "y axis");
yg
  .append("text")
  .attr("class", "label")
  .attr("transform", "rotate(-90)")
  .attr("y", 6)
  .attr("dy", ".71em")
  .style("text-anchor", "end");

// legend is always static
var legend = svg.selectAll(".legend")
    .data(color.domain())
    .enter().append("g")
    .attr("class", "legend")
    .attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });

legend.append("rect")
    .attr("x", width - 18)
    .attr("width", 18)
    .attr("height", 18)
    .style("fill", color);

legend.append("text")
    .attr("x", width - 24)
    .attr("y", 9)
    .attr("dy", ".35em")
    .style("text-anchor", "end")
    .text(function(d) { return d; });

Then wrap the update-able portion in a function:

function update(){

  // get user selections
  var xVar = d3.select('#xAXs').node().value,
    yVar = d3.select('#yAXs').node().value;

  var checks = {};
  d3.selectAll('input[type=checkbox]').each(function(){
    checks[this.value] = this.checked;
  });

  var radAttr = d3.select('input[type=radio]:checked').node().value;

  // filter data based on user selections
  var data = baseData.filter(function(d,i){
    d.x = d[xVar]; // create/modify a x,y so that d3 will know it's an update
    d.y = d[yVar];
    return checks[d.species];
  });

  // set domains
  x.domain(d3.extent(data, function(d) { return d.x; })).nice();
  y.domain(d3.extent(data, function(d) { return d.y; })).nice();

  xg.call(xAxis);
  yg.call(yAxis);

  xg.select("text").text(xVar);
  yg.select("text").text(yVar);

  // on enter
  var circles = svg.selectAll(".dot")
    .data(data);

  circles.enter()
    .append("circle")
    .attr("class", "dot");

  circles.exit().remove();

  // on update
  circles.attr("cx", function(d) { return x(d.x); })
    .attr("cy", function(d) { return y(d.y); })
    .style("fill", function(d) { return color(d.species); })
    .attr("r", function(d){ return d[radAttr]; });
}

Finally trigger update on user actions:

d3.selectAll('select').on('change',function(){
  update();
});

d3.selectAll('input').on('click', function(){
  update();
})

Here's an example putting this together.

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745492926a4630066.html

相关推荐

  • javascript - Making an interactive scatter plot with d3? - Stack Overflow

    I have a scatter plot based on this example .What i want to do is add interactivity by allowing the use

    2小时前
    20

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信