【问题标题】:How to correctly set X axis values to D3 chart如何正确将 X 轴值设置为 D3 图表
【发布时间】:2018-11-05 12:01:43
【问题描述】:

我使用 D3 实现了一个折线图,它应该显示 5 年内每个国家/地区的员工人数。我在下面附上了 JSFiddle。在图表的 X 轴上,我需要将年份作为标签。 (年份来自数据集)但问题是 X 轴有一些附加值作为标签,我无法弄清楚为什么会发生这种情况。我对使用 D3 也很陌生。谁能告诉我这里出了什么问题?

小提琴:https://jsfiddle.net/yasirunilan/e01L3uvt/1/

var data = [
        {
            name: "USA",
            values: [
                {date: "2000", price: "100"},
                {date: "2001", price: "110"},
                {date: "2002", price: "145"},
                {date: "2003", price: "241"},
                {date: "2004", price: "101"}
            ]
        },
        {
            name: "UK",
            values: [
                {date: "2000", price: "200"},
                {date: "2001", price: "120"},
                {date: "2002", price: "33"},
                {date: "2003", price: "21"},
                {date: "2004", price: "51"}
            ]
        },
        {
            name: "UAE",
            values: [
                {date: "2000", price: "50"},
                {date: "2001", price: "10"},
                {date: "2002", price: "5"},
                {date: "2003", price: "71"},
                {date: "2004", price: "20"}
            ]
        },
        {
            name: "Australia",
            values: [
                {date: "2000", price: "20"},
                {date: "2001", price: "40"},
                {date: "2002", price: "25"},
                {date: "2003", price: "75"},
                {date: "2004", price: "19"}
            ]
        },
        {
            name: "India",
            values: [
                {date: "2000", price: "200"},
                {date: "2001", price: "150"},
                {date: "2002", price: "170"},
                {date: "2003", price: "150"},
                {date: "2004", price: "130"}
            ]
        },
        {
            name: "Orange Department F",
            values: [
                {date: "2000", price: "75"},
                {date: "2001", price: "100"},
                {date: "2002", price: "120"},
                {date: "2003", price: "140"},
                {date: "2004", price: "190"}
            ]
        }
    ];

    const margin = 80;
    const width = 1000 - 2 * margin;
    const height = 550 - 2 * margin;

    var duration = 250;

    var lineOpacity = "0.25";
    var lineOpacityHover = "0.85";
    var otherLinesOpacityHover = "0.1";
    var lineStroke = "1.5px";
    var lineStrokeHover = "2.5px";

    var circleOpacity = '0.85';
    var circleOpacityOnLineHover = "0.25"
    var circleRadius = 3;
    var circleRadiusHover = 6;


    /* Format Data */
    var parseDate = d3.time.format("%Y");
    data.forEach(function(d) {
        d.values.forEach(function(d) {
            d.date = parseDate.parse(d.date);
            d.price = +d.price;
        });
    });

    /* Scale */
    var xScale = d3.time.scale()
        .domain(d3.extent(data[0].values, d => d.date))
        .range([0, width-margin]);
    var yScale = d3.scale.linear()
        .domain([0, d3.max(data[0].values, d => d.price)])
        .range([height-margin, 0]);

    // var color = d3.scale.ordinal(d3.schemeCategory10);
    var color = d3.scale.category10();

    /* Add SVG */
    var svg = d3.select("svg")
        .attr("width", (width+margin)+"px")
        .attr("height", (height+margin)+"px")
        .append('g')
        .attr("transform", `translate(${margin}, ${margin})`);


    /* Add line into SVG */
    var line = d3.svg.line()
        .x(d => xScale(d.date))
        .y(d => yScale(d.price));

    let lines = svg.append('g')
        .attr('class', 'lines');

    lines.selectAll('.line-group')
        .data(data).enter()
        .append('g')
        .attr('class', 'line-group')
        .on("mouseover", function(d, i) {
            svg.append("text")
                .attr("class", "title-text")
                .style("fill", color(i))
                .text(d.name)
                .attr("text-anchor", "middle")
                .attr("x", (width-margin)/2)
                .attr("y", 5);
        })
        .on("mouseout", function(d) {
            svg.select(".title-text").remove();
        })
        .append('path')
        .attr('class', 'line')
        .attr('d', d => line(d.values))
        .style('stroke', (d, i) => color(i))
        .style('opacity', lineOpacity)
        .on("mouseover", function(d) {
            d3.selectAll('.line')
                .style('opacity', otherLinesOpacityHover);
            d3.selectAll('.circle')
                .style('opacity', circleOpacityOnLineHover);
            d3.select(this)
                .style('opacity', lineOpacityHover)
                .style("stroke-width", lineStrokeHover)
                .style("cursor", "pointer");
        })
        .on("mouseout", function(d) {
            d3.selectAll(".line")
                .style('opacity', lineOpacity);
            d3.selectAll('.circle')
                .style('opacity', circleOpacity);
            d3.select(this)
                .style("stroke-width", lineStroke)
                .style("cursor", "none");
        });


    /* Add circles in the line */
    lines.selectAll("circle-group")
        .data(data).enter()
        .append("g")
        .style("fill", (d, i) => color(i))
        .selectAll("circle")
        .data(d => d.values).enter()
        .append("g")
        .attr("class", "circle")
        .on("mouseover", function(d) {
            d3.select(this)
                .style("cursor", "pointer")
                .append("text")
                .attr("class", "text")
                .text(`${d.price}`)
                .attr("x", d => xScale(d.date) + 5)
                .attr("y", d => yScale(d.price) - 10);
        })
        .on("mouseout", function(d) {
            d3.select(this)
                .style("cursor", "none")
                .transition()
                .duration(duration)
                .selectAll(".text").remove();
        })
        .append("circle")
        .attr("cx", d => xScale(d.date))
        .attr("cy", d => yScale(d.price))
        .attr("r", circleRadius)
        .style('opacity', circleOpacity)
        .on("mouseover", function(d) {
            d3.select(this)
                .transition()
                .duration(duration)
                .attr("r", circleRadiusHover);
        })
        .on("mouseout", function(d) {
            d3.select(this)
                .transition()
                .duration(duration)
                .attr("r", circleRadius);
        });

    /* Add Axis into SVG */
    var xAxis = d3.svg.axis().scale(xScale)
        .orient("bottom").tickSize(1);

    var yAxis = d3.svg.axis().scale(yScale)
        .orient("left").tickSize(1);
    svg.append("g")
        .attr("class", "x axis")
        .attr("transform", `translate(0, ${height-margin})`)
        .call(xAxis);

    svg.append("g")
        .attr("class", "y axis")
        .call(yAxis)
        .append('text')
        .attr("y", 15)
        .attr("transform", "rotate(-90)")
        .attr("fill", "#000")
        .attr('text-anchor', 'middle')
        .text("No. of Employees");

【问题讨论】:

  • 为什么在学习 d3 时使用 d3v3,我们目前在 d3v5。使用 d3v5,您可以指定只需要年标记处的刻度。为什么不结合鼠标事件处理程序?
  • 我正在实施图表的系统已经使用 d3 V3。因为最好使用相同的库版本,所以我使用 D3v3 实现了这个。我们不能在 v3 中做到这一点。您究竟要求组合鼠标事件处理程序是为了什么?
  • 然后实现一个自定义刻度格式化程序,如github.com/d3/d3-time-format

标签: javascript d3.js charts axis


【解决方案1】:

这里我把xAxis的tickFormat改成了

var xAxis = d3.svg.axis().scale(xScale)
            .orient("bottom").ticks(5).tickFormat(function(d) {
              if (d % 1 == 0) {
                return d3.format('.f')(d)
              } else {
                return ""
              }
            }).tickSize(1);

下面是源代码的完整工作演示,它工作正常。希望对你有帮助。

var data = [
            {
                name: "USA",
                values: [
                    {date: "2000", price: "100"},
                    {date: "2001", price: "110"},
                    {date: "2002", price: "145"},
                    {date: "2003", price: "241"},
                    {date: "2004", price: "101"}
                ]
            },
            {
                name: "UK",
                values: [
                    {date: "2000", price: "200"},
                    {date: "2001", price: "120"},
                    {date: "2002", price: "33"},
                    {date: "2003", price: "21"},
                    {date: "2004", price: "51"}
                ]
            },
            {
                name: "UAE",
                values: [
                    {date: "2000", price: "50"},
                    {date: "2001", price: "10"},
                    {date: "2002", price: "5"},
                    {date: "2003", price: "71"},
                    {date: "2004", price: "20"}
                ]
            },
            {
                name: "Australia",
                values: [
                    {date: "2000", price: "20"},
                    {date: "2001", price: "40"},
                    {date: "2002", price: "25"},
                    {date: "2003", price: "75"},
                    {date: "2004", price: "19"}
                ]
            },
            {
                name: "India",
                values: [
                    {date: "2000", price: "200"},
                    {date: "2001", price: "150"},
                    {date: "2002", price: "170"},
                    {date: "2003", price: "150"},
                    {date: "2004", price: "130"}
                ]
            },
            {
                name: "Orange Department F",
                values: [
                    {date: "2000", price: "75"},
                    {date: "2001", price: "100"},
                    {date: "2002", price: "120"},
                    {date: "2003", price: "140"},
                    {date: "2004", price: "190"}
                ]
            }
        ];

        const margin = 80;
        const width = 1000 - 2 * margin;
        const height = 550 - 2 * margin;

        var duration = 250;

        var lineOpacity = "0.25";
        var lineOpacityHover = "0.85";
        var otherLinesOpacityHover = "0.1";
        var lineStroke = "1.5px";
        var lineStrokeHover = "2.5px";

        var circleOpacity = '0.85';
        var circleOpacityOnLineHover = "0.25"
        var circleRadius = 3;
        var circleRadiusHover = 6;


        /* Format Data */
        var parseDate = d3.time.format("%Y");
        data.forEach(function(d) {
            d.values.forEach(function(d) {
               // d.date = parseDate.parse(d.date);
                d.price = +d.price;
            });
        });

			//console.log(data);

				//console.log(d3.extent(data[0].values, d => d.date));
        /* Scale */
        var xScale = d3.scale.linear()
            .domain(d3.extent(data[0].values, d => d.date))
            .range([0, width-margin]);
            
        var yScale = d3.scale.linear()
            .domain([0, d3.max(data[0].values, d => d.price)])
            .range([height-margin, 0]);

        // var color = d3.scale.ordinal(d3.schemeCategory10);
        var color = d3.scale.category10();

        /* Add SVG */
        var svg = d3.select("svg")
            .attr("width", (width+margin)+"px")
            .attr("height", (height+margin)+"px")
            .append('g')
            .attr("transform", `translate(${margin}, ${margin})`);


        /* Add line into SVG */
        var line = d3.svg.line()
            .x(d => xScale(d.date))
            .y(d => yScale(d.price));

        let lines = svg.append('g')
            .attr('class', 'lines');

        lines.selectAll('.line-group')
            .data(data).enter()
            .append('g')
            .attr('class', 'line-group')
            .on("mouseover", function(d, i) {
                svg.append("text")
                    .attr("class", "title-text")
                    .style("fill", color(i))
                    .text(d.name)
                    .attr("text-anchor", "middle")
                    .attr("x", (width-margin)/2)
                    .attr("y", 5);
            })
            .on("mouseout", function(d) {
                svg.select(".title-text").remove();
            })
            .append('path')
            .attr('class', 'line')
            .attr('d', d => line(d.values))
            .style('stroke', (d, i) => color(i))
            .style('opacity', lineOpacity)
            .on("mouseover", function(d) {
                d3.selectAll('.line')
                    .style('opacity', otherLinesOpacityHover);
                d3.selectAll('.circle')
                    .style('opacity', circleOpacityOnLineHover);
                d3.select(this)
                    .style('opacity', lineOpacityHover)
                    .style("stroke-width", lineStrokeHover)
                    .style("cursor", "pointer");
            })
            .on("mouseout", function(d) {
                d3.selectAll(".line")
                    .style('opacity', lineOpacity);
                d3.selectAll('.circle')
                    .style('opacity', circleOpacity);
                d3.select(this)
                    .style("stroke-width", lineStroke)
                    .style("cursor", "none");
            });


        /* Add circles in the line */
        lines.selectAll("circle-group")
            .data(data).enter()
            .append("g")
            .style("fill", (d, i) => color(i))
            .selectAll("circle")
            .data(d => d.values).enter()
            .append("g")
            .attr("class", "circle")
            .on("mouseover", function(d) {
                d3.select(this)
                    .style("cursor", "pointer")
                    .append("text")
                    .attr("class", "text")
                    .text(`${d.price}`)
                    .attr("x", d => xScale(d.date) + 5)
                    .attr("y", d => yScale(d.price) - 10);
            })
            .on("mouseout", function(d) {
                d3.select(this)
                    .style("cursor", "none")
                    .transition()
                    .duration(duration)
                    .selectAll(".text").remove();
            })
            .append("circle")
            .attr("cx", d => xScale(d.date))
            .attr("cy", d => yScale(d.price))
            .attr("r", circleRadius)
            .style('opacity', circleOpacity)
            .on("mouseover", function(d) {
                d3.select(this)
                    .transition()
                    .duration(duration)
                    .attr("r", circleRadiusHover);
            })
            .on("mouseout", function(d) {
                d3.select(this)
                    .transition()
                    .duration(duration)
                    .attr("r", circleRadius);
            });

        /* Add Axis into SVG */
        var xAxis = d3.svg.axis().scale(xScale)
            .orient("bottom").ticks(5).tickFormat(function(d) {
              if (d % 1 == 0) {
                return d3.format('.f')(d)
              } else {
                return ""
              }
            }).tickSize(1);

        var yAxis = d3.svg.axis().scale(yScale)
            .orient("left").tickSize(1);
        svg.append("g")
            .attr("class", "x axis")
            .attr("transform", `translate(0, ${height-margin})`)
            .call(xAxis);

        svg.append("g")
            .attr("class", "y axis")
            .call(yAxis)
            .append('text')
            .attr("y", 15)
            .attr("transform", "rotate(-90)")
            .attr("fill", "#000")
            .attr('text-anchor', 'middle')
            .text("No. of Employees");
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.3.10/d3.min.js"></script>
<html>
  <head>
  </head>
  <body>
    <svg/>
  </body>
</html>

【讨论】:

  • 不是一个通用的解决方案是年数的变化。或者如果图表的大小发生变化
  • @rioV8 对不起!我没有得到你。你能解释一下吗?
  • d%1 == 0 不适用于所有年数,它始终是正确的,因为您设置了刻度数并且您有一个线性 x 刻度,问题是:x 是一个时间刻度而不是线性规模,我们需要处理通用时间值,但只显示年份刻度。这是仅针对此输入案例的解决方法。
【解决方案2】:

对于这个,我通过更改刻度格式找到了解决方案。现在它按预期工作。以下代码显示了具有正确行为的示例代码。

小提琴:https://jsfiddle.net/yasirunilan/tz57wc29/13/

var data = [
        {
            name: "USA",
            values: [
                {date: "2000", price: "100"},
                {date: "2001", price: "110"},
                {date: "2002", price: "145"},
                {date: "2003", price: "241"},
                {date: "2004", price: "101"}
            ]
        },
        {
            name: "UK",
            values: [
                {date: "2000", price: "200"},
                {date: "2001", price: "120"},
                {date: "2002", price: "33"},
                {date: "2003", price: "21"},
                {date: "2004", price: "51"}
            ]
        },
        {
            name: "India",
            values: [
                {date: "2000", price: "50"},
                {date: "2001", price: "10"},
                {date: "2002", price: "5"},
                {date: "2003", price: "71"},
                {date: "2004", price: "20"}
            ]
        },
        {
            name: "Australia",
            values: [
                {date: "2000", price: "20"},
                {date: "2001", price: "40"},
                {date: "2002", price: "25"},
                {date: "2003", price: "75"},
                {date: "2004", price: "19"}
            ]
        },
        {
            name: "UAE",
            values: [
                {date: "2000", price: "200"},
                {date: "2001", price: "150"},
                {date: "2002", price: "170"},
                {date: "2003", price: "150"},
                {date: "2004", price: "130"}
            ]
        },
        {
            name: "China",
            values: [
                {date: "2000", price: "75"},
                {date: "2001", price: "100"},
                {date: "2002", price: "120"},
                {date: "2003", price: "140"},
                {date: "2004", price: "190"}
            ]
        }
    ];

    const margin = 80;
    const width = 1000 - 2 * margin;
    const height = 550 - 2 * margin;

    var duration = 250;

    var lineOpacity = "0.25";
    var lineOpacityHover = "0.85";
    var otherLinesOpacityHover = "0.1";
    var lineStroke = "1.5px";
    var lineStrokeHover = "2.5px";

    var circleOpacity = '0.85';
    var circleOpacityOnLineHover = "0.25"
    var circleRadius = 3;
    var circleRadiusHover = 6;


    /* Format Data */
    var parseDate = d3.time.format("%Y");
    data.forEach(function(d) {
        d.values.forEach(function(d) {
            d.date = parseDate.parse(d.date);
            d.price = +d.price;
        });
    });

    /* Scale */
    var xScale = d3.time.scale()
        .domain(d3.extent(data[0].values, d => d.date))
        .range([0, width-margin]);
    var yScale = d3.scale.linear()
        .domain([0, d3.max(data[0].values, d => d.price)])
        .range([height-margin, 0]);

    // var color = d3.scale.ordinal(d3.schemeCategory10);
    var color = d3.scale.category10();

    /* Add SVG */
    var svg = d3.select("svg")
        .attr("width", (width+margin)+"px")
        .attr("height", (height+margin)+"px")
        .append('g')
        .attr("transform", `translate(${margin}, ${margin})`);


    /* Add line into SVG */
    var line = d3.svg.line()
        .x(d => xScale(d.date))
        .y(d => yScale(d.price));

    let lines = svg.append('g')
        .attr('class', 'lines');

    lines.selectAll('.line-group')
        .data(data).enter()
        .append('g')
        .attr('class', 'line-group')
        .on("mouseover", function(d, i) {
            svg.append("text")
                .attr("class", "title-text")
                .style("fill", color(i))
                .text(d.name)
                .attr("text-anchor", "middle")
                .attr("x", (width-margin)/2)
                .attr("y", 5);
        })
        .on("mouseout", function(d) {
            svg.select(".title-text").remove();
        })
        .append('path')
        .attr('class', 'line')
        .attr('d', d => line(d.values))
        .style('stroke', (d, i) => color(i))
        .style('opacity', lineOpacity)
        .on("mouseover", function(d) {
            d3.selectAll('.line')
                .style('opacity', otherLinesOpacityHover);
            d3.selectAll('.circle')
                .style('opacity', circleOpacityOnLineHover);
            d3.select(this)
                .style('opacity', lineOpacityHover)
                .style("stroke-width", lineStrokeHover)
                .style("cursor", "pointer");
        })
        .on("mouseout", function(d) {
            d3.selectAll(".line")
                .style('opacity', lineOpacity);
            d3.selectAll('.circle')
                .style('opacity', circleOpacity);
            d3.select(this)
                .style("stroke-width", lineStroke)
                .style("cursor", "none");
        });


    /* Add circles in the line */
    lines.selectAll("circle-group")
        .data(data).enter()
        .append("g")
        .style("fill", (d, i) => color(i))
        .selectAll("circle")
        .data(d => d.values).enter()
        .append("g")
        .attr("class", "circle")
        .on("mouseover", function(d) {
            d3.select(this)
                .style("cursor", "pointer")
                .append("text")
                .attr("class", "text")
                .text(`${d.price}`)
                .attr("x", d => xScale(d.date) + 5)
                .attr("y", d => yScale(d.price) - 10);
        })
        .on("mouseout", function(d) {
            d3.select(this)
                .style("cursor", "none")
                .transition()
                .duration(duration)
                .selectAll(".text").remove();
        })
        .append("circle")
        .attr("cx", d => xScale(d.date))
        .attr("cy", d => yScale(d.price))
        .attr("r", circleRadius)
        .style('opacity', circleOpacity)
        .on("mouseover", function(d) {
            d3.select(this)
                .transition()
                .duration(duration)
                .attr("r", circleRadiusHover);
        })
        .on("mouseout", function(d) {
            d3.select(this)
                .transition()
                .duration(duration)
                .attr("r", circleRadius);
        });



    var xAxis = d3.svg.axis().scale(xScale)
        .orient("bottom").tickFormat(d3.time.format("%Y")).tickSize(1);

    var yAxis = d3.svg.axis().scale(yScale)
        .orient("left").tickSize(1);

    svg.append("g")
        .attr("class", "x axis")
        .attr("transform", `translate(0, ${height-margin})`)
        .call(xAxis.ticks(d3.time.year));

    svg.append("g")
        .attr("class", "y axis")
        .call(yAxis)
        .append('text')
        .attr("y", 15)
        .attr("transform", "rotate(-90)")
        .attr("fill", "#000")
        .attr('text-anchor', 'middle')
        .text("No. of Employees");

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-07-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-24
    • 1970-01-01
    相关资源
    最近更新 更多