D3JS makes date duplicates

喜欢而已 提交于 2019-12-31 03:48:08

问题


I have this d3js code:

var tooltip = tooltipd3();
        var svg = d3.select("svg#svg-day"),
            margin = {
                top: 20,
                right: 30,
                bottom: 30,
                left: 25,
                padding: 15
            },
            width = 700 - margin.left - margin.right,
            height = 300 - margin.top - margin.bottom;

        // parse the periodo / time
        var parseTime = d3.timeParse("%Y-%m-%d");

        // set the ranges
        var x = d3.scaleTime().range([0, width - margin.padding]);
        var y = d3.scaleLinear().range([height, 0]);

        // define the area
        var area = d3.area()
            .x(function(d) {
                return x(d.periodo) + (margin.left + margin.padding);
            })
            .y0(height)
            .y1(function(d) {
                return y(d.guadagno);
            });

        // define the line
        var valueline = d3.line()
            .x(function(d) {
                return x(d.periodo) + (margin.left + margin.padding);
            })
            .y(function(d) {
                return y(d.guadagno);
            });

        var div = d3.select("svg#svg-day")
            .append("div") // declare the tooltip div 
            .attr("class", "tooltip") // apply the 'tooltip' class
            .style("opacity", 0);

        // get the data
        d3.csv(base_url() + 'graph/getStatementsDaily/', function(error, data) {
            if (error) throw error;
            $('.graph-loading').hide();
            // format the data
            data.forEach(function(d) {
                d.periodo = parseTime(d.periodo)
                d.guadagno = +d.guadagno;
            });

            // scale the range of the data
            x.domain(d3.extent(data, function(d) {
                return d.periodo;
            }));
            y.domain([0, d3.max(data, function(d) {
                return d.guadagno + ((d.guadagno / 100) * 10); // 10% in più sulla scala numerica
            })]);

            // add the area
            svg.append("path")
                .data([data])
                .attr("class", "area")
                .attr("d", area);

            // add the valueline path.
            svg.append("path")
                .data([data])
                .attr("class", "line")
                .attr("d", valueline);

            // Add the scatterplot
            svg.selectAll("dot")
                .data(data)
                .enter().append("circle")
                .attr("class", "dot")
                .attr("r", 3)
                .attr("cx", function(d) {
                    return x(d.periodo) + (margin.left + margin.padding);
                })
                .attr("cy", function(d) {
                    return y(d.guadagno);
                })
                .on('mouseover', function(d) {
                    var html = '<h5>' + d.guadagno + ' €</h5>';
                    tooltip.mouseover(html); // pass html content
                })
                .on('mousemove', tooltip.mousemove)
                .on('mouseout', tooltip.mouseout);

            // add the X Axis
            svg.append("g")
                .attr("class", "x axis")
                .attr("transform", "translate(" + (margin.left + margin.padding) + "," + (height) + ")")

                //HERE IS THE DATES CODE

             .call(d3.axisBottom(x).tickFormat(d3.timeFormat("%d/%m")))

            // add the Y Axis
            svg.append("g")
                .attr("class", "y axis")
                .attr("transform", "translate (" + (margin.left + margin.padding) + " 0)")
                .call(d3.axisLeft(y));

        });

The dates care coming from a CSV file that has this format:

periodo,guadagno
2017-05-08,0.0
2017-05-09,0.5385
2017-05-10,0.0
2017-05-11,0.0
2017-05-12,0.0
2017-05-13,0.5680
2017-05-14,0.0
2017-05-15,0.0

The result is fine with lots of dates, but with 7 dates I get duplicates as you can see here:

Why is this?? And how do I fix it?


回答1:


This is something that bothers a lot of people new to D3: the ticks in the axis, specially when using a time scale, are automatically generated. In your case, given the date interval in your domain, it coincidentally ended up creating two ticks for each day. But pay attention to this: those ticks represent different times (hours) in the same day (you can see that if you remove the tickFormat in the axis generator).

Let's see your code generating the x axis:

var svg = d3.select("svg");
var data = d3.csvParse(d3.select("#csv").text());
var parseTime = d3.timeParse("%Y-%m-%d");
data.forEach(function(d) {
  d.periodo = parseTime(d.periodo)
});
var x = d3.scaleTime()
  .range([20, 480])
  .domain(d3.extent(data, function(d) {
    return d.periodo;
  }));

var axis = d3.axisBottom(x).tickFormat(d3.timeFormat("%d/%m"))(svg.append("g").attr("transform", "translate(0,50)"));
pre {
  display: none;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="500"></svg>
<pre id="csv">periodo,guadagno
2017-05-08,0.0
2017-05-09,0.5385
2017-05-10,0.0
2017-05-11,0.0
2017-05-12,0.0
2017-05-13,0.5680
2017-05-14,0.0
2017-05-15,0.0</pre>

As you can see, there are two ticks for each day (remember, for different hours).

Let's show that this is a coincidence: This is the same code, but changing the last date for 2017-05-20:

var svg = d3.select("svg");
var data = d3.csvParse(d3.select("#csv").text());
var parseTime = d3.timeParse("%Y-%m-%d");
data.forEach(function(d) {
  d.periodo = parseTime(d.periodo)
});
var x = d3.scaleTime()
  .range([20, 480])
  .domain(d3.extent(data, function(d) {
    return d.periodo;
  }));

var axis = d3.axisBottom(x).tickFormat(d3.timeFormat("%d/%m"))(svg.append("g").attr("transform", "translate(0,50)"));
pre {
  display: none;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="500"></svg>
<pre id="csv">periodo,guadagno
2017-05-08,0.0
2017-05-09,0.5385
2017-05-10,0.0
2017-05-11,0.0
2017-05-12,0.0
2017-05-13,0.5680
2017-05-14,0.0
2017-05-20,0.0</pre>

Back to your code.

The solution is quite simple: using intervals. Let's set the interval for each tick:

d3.axisBottom(x).ticks(d3.timeDay)

Here is the same code with that change only:

var svg = d3.select("svg");
var data = d3.csvParse(d3.select("#csv").text());
var parseTime = d3.timeParse("%Y-%m-%d");
data.forEach(function(d) {
  d.periodo = parseTime(d.periodo)
});
var x = d3.scaleTime()
  .range([20, 480])
  .domain(d3.extent(data, function(d) {
    return d.periodo;
  }));

var axis = d3.axisBottom(x).tickFormat(d3.timeFormat("%d/%m")).ticks(d3.timeDay)(svg.append("g").attr("transform", "translate(0,50)"));
pre {
  display: none;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<svg width="500"></svg>
<pre id="csv">periodo,guadagno
2017-05-08,0.0
2017-05-09,0.5385
2017-05-10,0.0
2017-05-11,0.0
2017-05-12,0.0
2017-05-13,0.5680
2017-05-14,0.0
2017-05-15,0.0</pre>


来源:https://stackoverflow.com/questions/44111608/d3js-makes-date-duplicates

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!