【问题标题】:D3.js line is returning a random numberD3.js 行返回一个随机数
【发布时间】:2018-10-26 12:50:54
【问题描述】:

以我是 D3 新手的事实作为开头,这可能是我遗漏的一个简单错误 - 我知道它必须如此。与我尝试访问数据的方式有关。

试图找出这个家伙一段时间。 (可能是我看太久了)

我一直在玩它直到我的行终于出现了,但现在它没有显示完整的行,只有两个点 - 这是我不确定的数字。

提前致谢!

编辑:目标是使用 lineData[0] 沿 lineData1(月)的 x 轴绘制一条线。最终,lineData2 将用于第二行,但目前只是试图让第一行工作。

编辑:可在此处找到更新的代码(根据 Shashank 的建议): https://codepen.io/anon/pen/RevxYG?editors=0010

我更新后的图表看起来更好,但仍然看到 NaN 值:

这是我的代码:

 let lineData = [
    [4, 2, 0, 3, 3, 4, 52, 3, 0, 3, 3, 1], 
    [0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0], 
    ["Oct", "Nov", "Dec", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep"]
  ]
            console.log("lineData", lineData)

        // set dimensions of the line chart
        let width = 393,
            height = 285;


        // set the scales
        let yScale = d3.scaleLinear()    
            .domain([0, 52])
            .range([height - 40 , 0]);

        let xScale = d3.scaleOrdinal()
            .range([0, width - 40]);

        // create the line
        let line = d3.line()
            .x(function(d, i) { return xScale(i); })
            .y(function(d) { return yScale(d[0]); });

        // create svg container
        let lineChart = d3.select(element)
            .append("svg")
            .attr("viewbox", "0 60 960 600")
            .attr("preserveAspectRatio", "xMinYMin meet")
            .attr("width", width)
            .attr("height", height)
            .attr("class", "line-chart")

        lineChart.append("path")
            .datum(lineData)
            .attr("d", line)
            .attr("fill", "none")
            .attr("stroke", "rebeccapurple")
            .attr("stroke-width", 2)

        lineChart.selectAll("dot")
            .data(lineData)
            .attr("d", line)
            .enter()
            .append("circle")
            .attr("r", 3.5)
            .attr("cx", function(d, i) { return xScale(i)})
            .attr("cy", function(d) { return yScale(d[0])})
            .attr("fill", "rebeccapurple")

        lineChart.append("g")
            .attr("transform", "translate(20, 245)")
            .call(d3.axisBottom(xScale))

        lineChart.append("g")
            .attr("transform", "translate(20, 0)")
            .call(d3.axisLeft(yScale))

数据是通过函数传入的。

数据是:

我的图表看起来像:

我生成的路径/点看起来像:

【问题讨论】:

  • 你能澄清一下到底是什么问题吗?您的数据集中有日期,您需要应用 timeScale 来获取实际的 X、Y 坐标
  • 你能包含你的数据而不是 img 吗?
  • @MikeTung 我认为序数音阶会起作用,不是吗?
  • @mahan 是的,一秒。编辑它
  • 需要澄清一下: 1. 您是否尝试使用lineData[0](12 个数字的数组)绘制 1 条线并使用lineData[1](12 个数字的数组)绘制 12 个圆圈? 2. 或者您是否尝试根据lineData[0]lineData[1] 绘制两条线并相应地圈出? (在上述两种情况下,将 lineData[2] 中的月份视为 xAxis)。那是因为您将lineData 绑定到圈子,同时为圈子调用.attr('d', line),这是令人困惑的。

标签: javascript d3.js linechart


【解决方案1】:

缺失点:

  1. 重要提示:理想情况下,您应该使用d3 time scale,因为您要根据月份绘制一条线。但是顺序量表也可以。我猜你正在这里寻找d3.scaleBand

除了输出范围是连续的和数字的之外,波段尺度类似于序数尺度。

为什么不使用d3.scaleOrdinal?这是一个例子:

let lineData = [
    [4, 2, 0, 3, 3, 4, 52, 3, 0, 3, 3, 1], 
    [0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0], 
    ["Oct", "Nov", "Dec", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep"]
  ]

        let xScale = d3.scaleOrdinal().range(lineData[2])
            .domain(lineData[0]);
            
    console.log('domain is: ' + xScale.domain());
    console.log('range is: ' + xScale.range());
<script src="https://d3js.org/d3.v4.min.js"></script>

您会看到重复值如何映射到对您的情况没有帮助的月份以及 d3.scaleBand()(您也可以使用 d3.scalePointRead more about it in this answer.

  1. 使用上述波段比例,您设置的域将是lineData[2],方式如下:

    xScale = d3.scaleBand().domain(lineData[2])
    
  2. 转换未应用于linecircles。所以我附加了<g> 容器来分别包含线条和圆圈,并转换了这些组元素。例如:

    var dots = lineChart.append('g')
        .attr("transform", "translate(20, 0)");
    

下面的解决方案只是回答了将第一行附加到图表的特定问题。正如您在 cmets 中提到的,您最终会将第二个元素添加为另一行,path 选择会改变

let lineData = [
    [4, 2, 0, 3, 3, 4, 52, 3, 0, 3, 3, 1], 
    [0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0], 
    ["Oct", "Nov", "Dec", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep"]
  ]
        // set dimensions of the line chart
        let width = 393,
            height = 285;


        // set the scales
        let yScale = d3.scaleLinear()    
            .domain([0, 52])
            .range([height - 40 , 0]);

        let xScale = d3.scaleBand().domain(lineData[2])
            .rangeRound([0, width - 40]);

        // create the line
        let line = d3.line()
            .x(function(d, i) { 
            	return xScale(lineData[2][i]) + xScale.bandwidth()/2; 
            })
            .y(function(d) { return yScale(d); });

        // create svg container
        let lineChart = d3.select('body')
            .append("svg")
            .attr("viewbox", "0 60 960 600")
            .attr("preserveAspectRatio", "xMinYMin meet")
            .attr("width", width)
            .attr("height", height)
            .attr("class", "line-chart")

				var lineG = lineChart.append('g')
        	.attr("transform", "translate(20, 0)");
          
        lineG.append("path")
            .data(lineData)
            .attr("d", line)
            .attr("fill", "none")
            .attr("stroke", "rebeccapurple")
            .attr("stroke-width", 2)

			var dots = lineChart.append('g')
        	.attr("transform", "translate(20, 0)");
        dots.selectAll("dot")
            .data(lineData[0])
            .enter()
            .append("circle").classed('dot', true)
            .attr("r", 3.5)
            .attr("cx", function(d, i) { return xScale(lineData[2][i]) + xScale.bandwidth()/2})
            .attr("cy", function(d) { return yScale(d)})
            .attr("fill", "rebeccapurple")

        lineChart.append("g")
            .attr("transform", "translate(20, 245)")
            .call(d3.axisBottom(xScale))

        lineChart.append("g")
            .attr("transform", "translate(20, 0)")
            .call(d3.axisLeft(yScale))
<script src="https://d3js.org/d3.v4.min.js"></script>

建议:将域设置为静态值不是一个好习惯,就像为 yScale [0,52] 所做的那样。尝试使用d3.extentd3.min 方法。

希望这会有所帮助。

编辑:当您尝试使用d3.scalePoint 时,您在为line 函数和圆圈选择xScale 值时犯了一个小错误。您需要在 fn 行中选择 categories[i],如下所示:

.x(function(d, i) { 
     return xScale(categories[i]); 
 })

使用 d3.scalePoint 更新了 sn-p

let data = {
    'New': [4, 2, 0, 3, 3, 4, 52, 3, 0, 3, 3, 1], 
    'Terminated': [0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0]
  };
  let categories = ["Oct", "Nov", "Dec", "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep"];
  
            let lineData = [data.New, data.Terminated, categories];

        
            // set dimensions of the line chart
            let width = 393,
                height = 285;

            // set the scales
            let yScale = d3.scaleLinear()    
                .domain([0, 60]).nice()
                .range([height - 40 , 0]);
        
            let xScale = d3.scalePoint()
                .domain(lineData[2].map(function(d, i) { return d; }))
                .range([0, width - 40]);
        
            // create the line
            let line = d3.line()
                .x(function(d, i) { 
                	return xScale(categories[i]); 
                })
                .y(function(d) { return yScale(d); });
        
            // create svg container
            let lineChart = d3.select('body')
                .append("svg")
                .attr("viewbox", "0 60 960 600")
                .attr("preserveAspectRatio", "xMinYMin meet")
                .attr("width", width)
                .attr("height", height)
                .attr("class", "line-chart")

				var lineG = lineChart.append('g')
        	.attr("transform", "translate(20, 0)");
            lineG.append("path")
                .datum(lineData)
                .attr("d", function(d) { return line(d[0]); })
                .attr("fill", "none")
                .attr("stroke", "rebeccapurple")
                .attr("stroke-width", 2)
          
            lineChart.selectAll("dot")
                .data(lineData[0])
                .enter()
                .append("circle")
                .attr("transform", "translate(20, 0)")
                .attr("r", 3.5)
                .attr("cx", function(d, i) { return xScale(categories[i])})
                .attr("cy", function(d) { return yScale(d)})
                .attr("fill", "rebeccapurple")
        
            lineChart.append("g")
                .attr("transform", "translate(20, 245)")
                .call(d3.axisBottom(xScale))

        
            lineChart.append("g")
                .attr("transform", "translate(20, 0)")
                .call(d3.axisLeft(yScale))
<script src="https://d3js.org/d3.v4.min.js"></script>

希望这也有帮助。

【讨论】:

  • 这很有帮助!有几件事——时间尺度返回了一些非常奇怪的结果。我想坚持使用序数,因为它是一个字符串;然而;我发现 scalePoint 正是我需要的!我尝试进行一些更改,但它破坏了图表。通过我所做的一些更改,现在图表看起来更好,但我仍然看到一些数据的 NaN 值。你可以在这里看到我更新的代码:codepen.io/anon/pen/RevxYG?editors=0010。我会将此链接和更新后的图像添加到我的 OP 中
  • 这实际上是完美的,我走的是你的路线而不是 scalePoint。再次感谢!
  • @Almost_Ashleigh 用d3.scalePoint 的实现更新了帖子(如果你仍然想知道它是如何工作的)。很高兴我能帮上忙! :)
猜你喜欢
  • 1970-01-01
  • 2021-06-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-28
  • 1970-01-01
  • 1970-01-01
  • 2013-11-25
相关资源
最近更新 更多