简体   繁体   English

Javascript d3 饼图不会从带有字典列表的 JSON 文件中提取数据

[英]Javascript d3 pie chart doesn't pull data from JSON file with list of dictionaries

I have a .json file with data, and I'd like to make a d3 donut (pie) chart from it.我有一个包含数据的 .json 文件,我想从中制作一个 d3 甜甜圈(饼图)图。 I'm not especially fluent in javascript, and every example I can find either pulls from inline json data or the json file is structured differently than mine (mine is a list of dictionaries; theirs are often single dictionaries).我对 javascript 不是特别流利,我能找到的每个例子要么是从内联 json 数据中提取的,要么是 json 文件的结构与我的不同(我的是一个字典列表;他们的通常是单个字典)。 I've been troubleshooting for a few days, and somehow can't land on anything that actually works.我已经进行了几天的故障排除,但不知何故无法解决任何实际有效的问题。 Any thoughts/tips?任何想法/提示?

The example at https://www.d3-graph-gallery.com/graph/donut_label.html uses inline json data to render a donut chart with labels. https://www.d3-graph-gallery.com/graph/donut_label.html 上的示例使用内联 json 数据来呈现带有标签的圆环图。 I've attempted to modify it that code by:我试图通过以下方式修改它的代码:

  • pulling json data from /data/all-facet-digitized.json从 /data/all-facet-digitized.json 中提取 json 数据
  • pull labels each dictionary's "facet" key ("true" and "false"), and values from each dictionary's "count" key (373977 and 55433). pull 标记每个字典的“facet”键(“true”和“false”),以及来自每个字典的“count”键(373977 和 55433)的值。
  • change the color scale domain to match the facet keys ("true" and "false")更改色标域以匹配刻面键(“true”和“false”)

/data/all-facet-digitized.json looks like: /data/all-facet-digitized.json 看起来像:

[
  {
    "count": "55433",
    "facet": "true"
  },  
  {
    "count": "373977",
    "facet": "false"
  }
]

Code in the of my html file looks like:我的 html 文件中的代码如下所示:


    <div id="chart"></div> <!-- div containing the donut chart -->


    <script src="//d3js.org/d3.v4.min.js"></script>


    <script>
    // set the dimensions and margins of the graph
    var width = 450
        height = 450
        margin = 40

    // The radius of the pieplot is half the width or half the height (smallest one) minus margin.
    var radius = Math.min(width, height) / 2 - margin

    // append the svg object to the div called 'chart'
    var svg = d3.select("#chart")
      .append("svg")
        .attr("width", width)
        .attr("height", height)
      .append("g")
        .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");


    // Parse the Data
    d3.json("/data/all-facet-digitized.json", function(data) {

      // set the color scale
      var color = d3.scaleOrdinal()
        .domain(["true","false"])
        .range(d3.schemeDark2);

      // Compute the position of each group on the pie:
      var pie = d3.pie()
        .sort(null) // Do not sort group by size
        .value(function(d) {return d.count; })
      var data_ready = pie(d3.entries(data))

      // The arc generator
      var arc = d3.arc()
        .innerRadius(radius * 0.5)         // This is the size of the donut hole
        .outerRadius(radius * 0.8)

      // Another arc that won't be drawn. Just for labels positioning
      var outerArc = d3.arc()
        .innerRadius(radius * 0.9)
        .outerRadius(radius * 0.9)

      // Build the pie chart: Basically, each part of the pie is a path that we build using the arc function.
      svg
        .selectAll('allSlices')
        .data(data_ready)
        .enter()
        .append('path')
        .attr('d', arc)
        .attr('fill', function(d){ return(color(d.facet)) })
        .attr("stroke", "white")
        .style("stroke-width", "2px")
        .style("opacity", 0.7)

      // Add the polylines between chart and labels:
      svg
        .selectAll('allPolylines')
        .data(data_ready)
        .enter()
        .append('polyline')
          .attr("stroke", "black")
          .style("fill", "none")
          .attr("stroke-width", 1)
          .attr('points', function(d) {
            var posA = arc.centroid(d) // line insertion in the slice
            var posB = outerArc.centroid(d) // line break: we use the other arc generator that has been built only for that
            var posC = outerArc.centroid(d); // Label position = almost the same as posB
            var midangle = d.startAngle + (d.endAngle - d.startAngle) / 2 // we need the angle to see if the X position will be at the extreme right or extreme left
            posC[0] = radius * 0.95 * (midangle < Math.PI ? 1 : -1); // multiply by 1 or -1 to put it on the right or on the left
            return [posA, posB, posC]
          })

      // Add the polylines between chart and labels:
      svg
        .selectAll('allLabels')
        .data(data_ready)
        .enter()
        .append('text')
          .text( function(d) { console.log(d.facet) ; return d.facet} )
          .attr('transform', function(d) {
              var pos = outerArc.centroid(d);
              var midangle = d.startAngle + (d.endAngle - d.startAngle) / 2
              pos[0] = radius * 0.99 * (midangle < Math.PI ? 1 : -1);
              return 'translate(' + pos + ')';
          })
          .style('text-anchor', function(d) {
              var midangle = d.startAngle + (d.endAngle - d.startAngle) / 2
              return (midangle < Math.PI ? 'start' : 'end')
          })
      })

    </script>

My result renders as an empty space:我的结果呈现为一个空白空间:


    <div id="chart">
      <svg width="450" height="450">
        <g transform="translate(225,225)"></g>
      </svg>
    </div>

The schemeDark2 doens't exist in d3 v4. schemeDark2在 d3 v4 中不存在。 I've replaced it with schemeCategory10 :我已经用schemeCategory10替换了它:

  var color = d3.scaleOrdinal()
    .domain(["true","false"])
    .range(d3.schemeCategory10);

Since you have an array of objects, you don't need d3.entries .由于您有一组对象,因此不需要d3.entries That takes an object and converts it to an array where each key is an item of the array.这需要一个对象并将其转换为一个数组,其中每个键都是数组的一个项目。 But since you already have an array here, you can put it directly in pie() :但是因为这里已经有一个数组,你可以直接把它放在pie()

  // Compute the position of each group on the pie:
  var pie = d3.pie()
    .sort(null) // Do not sort group by size
    .value(function(d) {return d.count; })
  var data_ready = pie(data)

Now that you've got the data, you can access it on any of the functions: try putting console.log(data_ready) to see what's available.现在您已经获得了数据,您可以在任何函数上访问它:尝试放入console.log(data_ready)以查看可用的内容。 You'll see that the data is bound for each object as the .data property.您将看到数据作为.data属性绑定到每个对象。 pie() takes an array and puts it in a format that's convenient to make pie charts with. pie()接受一个数组并将其放入便于制作饼图的格式中。

Say we want to access the facet property: we would access that as item.data.facet .假设我们要访问 facet 属性:我们将其作为item.data.facet访问。 So in your functions, to access, you can do:因此,在您的功能中,要访问,您可以执行以下操作:

  svg
    .selectAll('allSlices')
    .data(data_ready)
    .enter()
    .append('path')
    .attr('d', arc)
    .attr('fill', function(d){ return(color(d.data.facet)) })

 <head></head> <div id="chart"></div> <!-- div containing the donut chart --> <script src="//d3js.org/d3.v4.min.js"></script> <script> // set the dimensions and margins of the graph var width = 450 height = 450 margin = 40 // The radius of the pieplot is half the width or half the height (smallest one) minus margin. var radius = Math.min(width, height) / 2 - margin // append the svg object to the div called 'chart' var svg = d3.select("#chart") .append("svg") .attr("width", width) .attr("height", height) .append("g") .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")"); // Parse the Data var data = [ { "count": "55433", "facet": "true" }, { "count": "373977", "facet": "false" } ] // set the color scale var color = d3.scaleOrdinal() .domain(["true","false"]) .range(d3.schemeCategory10); // Compute the position of each group on the pie: var pie = d3.pie() .sort(null) // Do not sort group by size .value(function(d) {return d.count; }) var data_ready = pie(data) console.log('data_r', data_ready) // The arc generator var arc = d3.arc() .innerRadius(radius * 0.5) // This is the size of the donut hole .outerRadius(radius * 0.8) // Another arc that won't be drawn. Just for labels positioning var outerArc = d3.arc() .innerRadius(radius * 0.9) .outerRadius(radius * 0.9) // Build the pie chart: Basically, each part of the pie is a path that we build using the arc function. svg .selectAll('allSlices') .data(data_ready) .enter() .append('path') .attr('d', arc) .attr('fill', function(d){ return(color(d.data.facet)) }) .attr("stroke", "white") .style("stroke-width", "2px") .style("opacity", 0.7) // Add the polylines between chart and labels: svg .selectAll('allPolylines') .data(data_ready) .enter() .append('polyline') .attr("stroke", "black") .style("fill", "none") .attr("stroke-width", 1) .attr('points', function(d) { var posA = arc.centroid(d) // line insertion in the slice var posB = outerArc.centroid(d) // line break: we use the other arc generator that has been built only for that var posC = outerArc.centroid(d); // Label position = almost the same as posB var midangle = d.startAngle + (d.endAngle - d.startAngle) / 2 // we need the angle to see if the X position will be at the extreme right or extreme left posC[0] = radius * 0.95 * (midangle < Math.PI ? 1 : -1); // multiply by 1 or -1 to put it on the right or on the left return [posA, posB, posC] }) // Add the polylines between chart and labels: svg .selectAll('allLabels') .data(data_ready) .enter() .append('text') .text( function(d) { return d.data.facet} ) .attr('transform', function(d) { var pos = outerArc.centroid(d); var midangle = d.startAngle + (d.endAngle - d.startAngle) / 2 pos[0] = radius * 0.99 * (midangle < Math.PI ? 1 : -1); return 'translate(' + pos + ')'; }) .style('text-anchor', function(d) { var midangle = d.startAngle + (d.endAngle - d.startAngle) / 2 return (midangle < Math.PI ? 'start' : 'end') }) </script>

Ok, the issues here is that you've completely missed how data_ready is structured after converting the JSON response.好的,这里的问题是您完全错过了转换 JSON 响应后data_ready的结构。 You might want to add console.log(data_ready) just after you set data_ready and inspect it in the console for better understanding of the following fixes.您可能希望在设置data_ready后添加console.log(data_ready)并在控制台中检查它以更好地理解以下修复。

First a color fix:首先是颜色修复:

       .attr('fill', function(d){ return(color(d.data.value.facet)) })

Then a data fix:然后是数据修复:

       .value(function(d) {return d.value.count; })

And lastly a label fix:最后是标签修复:

       .text( function(d) { console.log(d.data.key) ; return d.data.value.facet } )

Your script should look like this:您的脚本应如下所示:

// set the dimensions and margins of the graph
var width = 450
    height = 450
    margin = 40

// The radius of the pieplot is half the width or half the height (smallest one). I subtract a bit of margin.
var radius = Math.min(width, height) / 2 - margin

// append the svg object to the div called 'my_dataviz'
var svg = d3.select("#chart")
  .append("svg")
    .attr("width", width)
    .attr("height", height)
  .append("g")
    .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");

d3.json("/data/all-facet-digitized.json", function(data) {

// set the color scale
var color = d3.scaleOrdinal()
  .domain(["true","false"])
  .range(d3.schemeDark2);

// Compute the position of each group on the pie:
var pie = d3.pie()
  .sort(null) // Do not sort group by size
  .value(function(d) {return d.value.count; })
var data_ready = pie(d3.entries(data))

// The arc generator
var arc = d3.arc()
  .innerRadius(radius * 0.5)         // This is the size of the donut hole
  .outerRadius(radius * 0.8)

// Another arc that won't be drawn. Just for labels positioning
var outerArc = d3.arc()
  .innerRadius(radius * 0.9)
  .outerRadius(radius * 0.9)

// Build the pie chart: Basically, each part of the pie is a path that we build using the arc function.
svg
  .selectAll('allSlices')
  .data(data_ready)
  .enter()
  .append('path')
  .attr('d', arc)
  .attr('fill', function(d){ return(color(d.data.value.facet)) })
  .attr("stroke", "white")
  .style("stroke-width", "2px")
  .style("opacity", 0.7)

// Add the polylines between chart and labels:
svg
  .selectAll('allPolylines')
  .data(data_ready)
  .enter()
  .append('polyline')
    .attr("stroke", "black")
    .style("fill", "none")
    .attr("stroke-width", 1)
    .attr('points', function(d) {
      var posA = arc.centroid(d) // line insertion in the slice
      var posB = outerArc.centroid(d) // line break: we use the other arc generator that has been built only for that
      var posC = outerArc.centroid(d); // Label position = almost the same as posB
      var midangle = d.startAngle + (d.endAngle - d.startAngle) / 2 // we need the angle to see if the X position will be at the extreme right or extreme left
      posC[0] = radius * 0.95 * (midangle < Math.PI ? 1 : -1); // multiply by 1 or -1 to put it on the right or on the left
      return [posA, posB, posC]
    })

// Add the polylines between chart and labels:
svg
  .selectAll('allLabels')
  .data(data_ready)
  .enter()
  .append('text')
    .text( function(d) { console.log(d.data.key) ; return d.data.value.facet } )
    .attr('transform', function(d) {
        var pos = outerArc.centroid(d);
        var midangle = d.startAngle + (d.endAngle - d.startAngle) / 2
        pos[0] = radius * 0.99 * (midangle < Math.PI ? 1 : -1);
        return 'translate(' + pos + ')';
    })
    .style('text-anchor', function(d) {
        var midangle = d.startAngle + (d.endAngle - d.startAngle) / 2
        return (midangle < Math.PI ? 'start' : 'end')
    })
})

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM