Hyperbola
Hyperbola

Reputation: 528

Histogram with D3

I'm trying to create a simple histogram with D3 through a function. The y values for the chart are passed to the function as an array and then the function creates the svg and the bars. I'm getting the axes properly but bars are getting cut off.

It seems my x-values for the rects are too large to fit inside the svg. I cannot think of any idea to fix it and would appreciate any kind of help.

Upvotes: 1

Views: 932

Answers (1)

Gerardo Furtado
Gerardo Furtado

Reputation: 102194

The problem in your chart is that you're using a linear scale for the x axis, with a domain going from 0 to 1, and then using this for the x position:

.attr("x", function(d, i) { return x(i) - 0.5; })

Linear scales are used for histograms but not for bar charts, and it's not clear to me that you're in fact drawing a histogram. That being said, without making major changes in your code (it's not my place, I'm only answering a specific question), you can change the x attribute to:

.attr("x", function(d, i) {
    return x(i/dataset.length);
})

Here is the demo:

<script src="https://d3js.org/d3.v4.min.js"></script>
<style>
  .title {
    padding-top: 10px;
    font-family: "Oswald", sans-serif;
  }
  
  .subtitle {
    font-family: "Open Sans", sans-serif;
    margin-top: 10px;
  }
  
  .bar:hover {
    fill: blue;
  }

</style>

<body>
  <div class="container">
    <h1 class="display-4 text-center title">HW #4</h1>
    <hr>
    <div class="row justify-content-md-center">
      <div class="col-12 col-md-auto">
        <button type="button" class="btn btn-primary">Bar chart</button>
        <br>
        <br>
        <br>
        <div id="normal"></div>
      </div>
    </div>
  </div>
  <script>
    function plotHistogram(element, dataset) {

      var margin = {
          top: 20,
          right: 20,
          bottom: 100,
          left: 40
        },
        width = 900 - margin.left - margin.right,
        height = 600 - margin.top - margin.bottom;

      // set the ranges
      var x = d3.scaleLinear()
        .domain([0, 1])
        .range([0, width]);

      var y = d3.scaleLinear()
        .domain([0, d3.max(dataset, function(d) {
          return d;
        })])
        .range([height, 0]);

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

      // append the rectangles for the bar chart
      svg.selectAll(".bar")
        .data(dataset)
        .enter().append("rect")
        .attr("class", "bar")
        .attr("x", function(d, i) {
          return x(i/dataset.length);
        })
        .attr("width", x(1/dataset.length)-2)
        .attr("y", function(d) {
          return y(d);
        })
        .attr("height", function(d) {
          return height - y(d);
        })
        .attr("fill", "teal").attr("opacity", 0.5);

      // add the x Axis
      svg.append("g")
        .attr("transform", "translate(0," + height + ")")
        .call(d3.axisBottom(x))
        .selectAll("text")
        .style("text-anchor", "end")
        .attr("dx", "-.8em")
        .attr("dy", "-.55em")
        .attr("transform", "rotate(-90)");

      // add the y Axis
      svg.append("g")
        .call(d3.axisLeft(y));


    }

    // Change the array here
    plotHistogram("#normal", [10, 20, 50, 30, 15]);

  </script>
</body>

However, I must say that this is a strange code for any D3 programmer. I suggest that you, firstly, decide if you want to draw a bar chart or a histogram (they are fundamentally different). Then, after this decision, use the appropriate methods and scales.

Upvotes: 2

Related Questions