D3.js饼图显示每个季度的销售额百分比

D3.js pie chart to show the percentage of sales in each quarter

使用d3.js我想制作一个图表,将每个项目显示为饼图,显示不同节目的季度门票销售情况

这里是我要制作的图表样图,图表中的每一段代表一个节目的门票及其在每个季度的销售百分比。现在使用这个 link enter link description here

我制作了一张图表,但不完全是我 needed.Is d3.js 中可用的任何图表来显示我在图片中提到的图表,或者我们需要对其进行自定义得到这样的图表。

Is there any charts available in d3.js to show a graph as I mentioned in the picture or we need to customize it to get a graph like that?

没有现成的解决方案,d3 因为问题注释上的 是操作 DOM 的方法集合,这在创建时具有很大的灵活性自定义可视化(用户不像许多只允许定义修改的现成解决方案那样受限)。因此,是的,您可以制作类似 d3 中的图表,从 d3 的散点图和饼图实现中获取元素和想法来制作您的图表。

此答案显示了一种可用于创建此类图表的方法。理想情况下,它可以为制作满足您需要的可视化效果提供想法。

首先,您需要一种机制来制作可变大小的饼图并放置它们 - 可以说这是最难的部分(之后您只有一个更容易操作的散点图)。这需要对数据结构进行一些思考,我使用了如下结构:

var data = [
  {x:100,y:100,radius:20,slices:[1,5]},
  {x:150,y:180,radius:10,slices:[1,2,3,4]},

您可以根据需要添加其他属性,这所做的只是指定饼图中心的 x 和 y 坐标、饼图的半径以及每个饼图的楔形值。

有了它,您可以使用 d3 中的标准输入循环将组元素 (g) 附加到您的 svg,每个饼图(或数据数组中的项目)一个,将组定位为我们去:

var pies = svg.selectAll("g")
  .data(data)
  .enter()
  .append("g")
  .property("radius",function(d) { return d.radius; })
  .attr("transform",function(d) { return "translate("+d.x+","+d.y+")"; });

因为用于附加楔形的数据数组本身将只包含楔形值,我们可以将半径 属性 保存为组的 属性 并在附加楔形时访问它:

pies.selectAll()
  .data(function(d){ return pie(d.slices); })
  .enter()
  .append("path")
  .attr("d",function(d) { 
      var radius = d3.select(this.parentNode).property("radius"); 
      arc.outerRadius(radius);
      return arc(d) })
  .attr("fill",function(d,i){
           return color[i];      
      });

一个基本示例可能如下所示:

var data = [
  {x:100,y:100,radius:20,slices:[1,5]},
  {x:150,y:180,radius:10,slices:[1,2,3,4]},
  {x:180,y:130,radius:30,slices:[1,2,3,4,5,6,7]},
  {x:50,y:50,radius:15,slices:[5,3]},
  {x:50,y:180,radius:40,slices:[6,3]}
  ]

var width = 500;
var height = 300;

var svg = d3.select("body").append("svg")
  .attr("width", width)
  .attr("height", height);
  
var arc = d3.arc()
  .innerRadius(0)
  .outerRadius(50);
   
var pie = d3.pie()
  .sort(null)
  .value(function(d) { return d; });
   
var color = d3.schemeCategory10;

// Append a group for each pie chart, it will store the radius of each pie as a property
var pies = svg.selectAll("g")
  .data(data)
  .enter()
  .append("g")
  .property("radius",function(d) { return d.radius; })
  .attr("transform",function(d) { return "translate("+d.x+","+d.y+")"; });
  
// draw each pie wedge, using the slices property of the data bound to the parent g  
pies.selectAll()
  .data(function(d){ return pie(d.slices); })
  .enter()
  .append("path")
  .attr("d",function(d) { 
      var radius = d3.select(this.parentNode).property("radius"); 
      arc.outerRadius(radius);
      return arc(d) })
  .attr("fill",function(d,i){
           return color[i];      
      });
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>

如果您想将每个圆圈设置为拥有自己的配色方案,可以使用一些选项。如果每个馅饼只有两种颜色,您可以为父组分配一个填充并使用楔形增量来设置透明度,创建更浅的楔形,例如在您的图像中:

var data = [
  {x:100,y:100,radius:20,slices:[1,5]},
  {x:150,y:180,radius:10,slices:[1,2]},
  {x:180,y:130,radius:30,slices:[1,7]},
  {x:50,y:50,radius:15,slices:[5,3]}
  ]

var width = 500;
var height = 300;

var svg = d3.select("body").append("svg")
  .attr("width", width)
  .attr("height", height);
  
var arc = d3.arc()
  .innerRadius(0)
  .outerRadius(50);
   
var pie = d3.pie()
  .sort(null)
  .value(function(d) { return d; });
   
var color = ["steelblue","orange","pink","crimson"]

// Append a group for each pie chart, it will store the radius of each pie as a property
var pies = svg.selectAll("g")
  .data(data)
  .enter()
  .append("g")
  .property("radius",function(d) { return d.radius; })
  .attr("fill",function(d,i) { return color[i] })
  .attr("transform",function(d) { return "translate("+d.x+","+d.y+")"; });
  
// draw each pie wedge, using the slices property of the data bound to the parent g  
pies.selectAll()
  .data(function(d){ return pie(d.slices); })
  .enter()
  .append("path")
  .attr("d",function(d) { 
      var radius = d3.select(this.parentNode).property("radius"); 
      arc.outerRadius(radius);
      return arc(d) })
  .attr("opacity",function(d,i){
           return 1-i*0.2;      
      });
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>

其他选项可用,例如存储局部变量,将颜色存储为 属性 就像我们对半径所做的那样,或者修改我们的数据结构以包含每个楔形的颜色:

var data = [
  {x:100,y:100,radius:20,
      slices:[{value:1,color:"steelblue"},{value:5,color:"lightblue"} ]},
      
  {x:150,y:180,radius:10,
       slices:[{value:1,color:"crimson"},{value:2,color:"pink"}]},
        
  {x:180,y:130,radius:30,
       slices:[{value:1,color:"lawngreen"},{value:7,color:"darkgreen"}]}
  ]

var width = 500;
var height = 300;

var svg = d3.select("body").append("svg")
  .attr("width", width)
  .attr("height", height);
  
var arc = d3.arc()
  .innerRadius(0)
  .outerRadius(50);
   
var pie = d3.pie()
  .sort(null)
  .value(function(d) { return d.value; });

// Append a group for each pie chart, it will store the radius of each pie as a property
var pies = svg.selectAll("g")
  .data(data)
  .enter()
  .append("g")
  .property("radius",function(d) { return d.radius; })
  .attr("transform",function(d) { return "translate("+d.x+","+d.y+")"; });
  
// draw each pie wedge, using the slices property of the data bound to the parent g  
pies.selectAll()
  .data(function(d){ return pie(d.slices); })
  .enter()
  .append("path")
  .attr("d",function(d) { 
      var radius = d3.select(this.parentNode).property("radius"); 
      arc.outerRadius(radius);
      return arc(d) })
      // remember that d3.pie creates it's own data array, thus using d.data.property:
   .attr("fill",function(d){ return d.data.color; })
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>

现在我们可以调整和实现散点图的特征,例如比例和轴。这对于任何其他散点图本质上都是相同的,我们将缩放 x 和 y 尺度的最大值和最小值(或定义的范围),并添加轴。总而言之,这可能看起来像:

var data = [
  {x:100,y:100,radius:10,slices:[1,5]},
  {x:150,y:180,radius:10,slices:[1,2,3,4]},
  {x:180,y:110,radius:30,slices:[1,2,3,4,5,6,7]},
  {x:50,y:100,radius:15,slices:[5,3]},
  {x:50,y:180,radius:40,slices:[6,3]}
  ]

var width = 500;
var height = 300;
var margin = {left:30,right:10,top:30,bottom:30}

var xScale = d3.scaleLinear()
  .range([0,width-margin.left-margin.right])
  .domain([0,d3.max(data,function(d) { return d.x + 20 }) ]);
  
var yScale = d3.scaleLinear()
  .range([height-margin.top-margin.bottom,0])
  .domain([0,d3.max(data,function(d) { return d.y + 20}) ]);
  

var svg = d3.select("body").append("svg")
 .attr("width", width)
 .attr("height", height);
  
var g = svg.append("g")
  .attr("transform", "translate("+margin.left+","+margin.top+")")
  
var xAxis = d3.axisBottom(xScale);
  
g.append("g")
    .attr("transform", "translate(0,"+(height-margin.bottom-margin.top)+")")
    .call(xAxis);
    
var yAxis = d3.axisLeft(yScale);
  
g.append("g")
    .call(yAxis);

var arc = d3.arc()
   .innerRadius(0)
   .outerRadius(50);
   
var pie = d3.pie()
   .sort(null)
   .value(function(d) { return d; });
   
var color = d3.schemeCategory10;

var pies = g.selectAll(null)
  .data(data)
  .enter()
  .append("g")
  .property("radius",function(d) { return d.radius; })
  .attr("transform",function(d) { return "translate("+xScale(d.x)+","+yScale(d.y)+")"; });
  
  
pies.selectAll()
  .data(function(d){ return pie(d.slices); })
  .enter()
  .append("path")
  .attr("d",function(d) { 
      var radius = d3.select(this.parentNode).property("radius"); 
      arc.outerRadius(radius);
      return arc(d) })
  .attr("fill",function(d,i){
           return color[i];      
      });
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>

添加网格线、图例、鼠标悬停功能和其他功能现在应该相对简单 - 查看 d3 的散点图示例,了解如何实现这些和其他功能,修改圆的散点图大致相同就像修改饼图的散点图一样。

我根据@Andrew Reid提供的示例制作的,这里贴出示例代码供参考

<html>
<head>
    <title>TODO supply a title</title>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>

</head>
<body>

    <script>
        var data = [

            {x: 170, y: 160, radius: 20, slices: [3, 4]},
            {x: 180, y: 40, radius: 30, slices: [ 6, 7]},
            {x: 50, y: 80, radius: 20, slices: [5, 3]},
            {x: 50, y: 180, radius: 40, slices: [6, 3]}
        ]

        var width = 500;
        var height = 300;
        var margin = {left: 30, right: 10, top: 30, bottom: 30}

        var xScale = d3.scaleLinear()
                .range([0, width - margin.left - margin.right])
                .domain([0, d3.max(data, function (d) {
                        return d.x + 20
                    })]);

        var yScale = d3.scaleLinear()
                .range([height - margin.top - margin.bottom, 0])
                .domain([0, d3.max(data, function (d) {
                        return d.y + 20
                    })]);
        xMid=d3.max(xScale.domain())/2;
        yMid=d3.max(yScale.domain())/2;
        console.log(xMid,yMid)


        var svg = d3.select("body").append("svg")
                .attr("width", width)
                .attr("height", height);

        var g = svg.append("g")
                .attr("transform", "translate(" + margin.left + "," + margin.top + ")")

        var xAxis = d3.axisBottom(xScale);

        g.append("g")
                .attr("transform", "translate(0," + (height - margin.bottom - margin.top) + ")")
                .call(xAxis);

        var yAxis = d3.axisLeft(yScale);

        g.append("g")
                .call(yAxis);
        var lineX= g.append("line")
      .attr("x1", 0)
      .attr("x2", 500)
      .attr("y1", yMid+20)
      .attr("y2", yMid+20)
      .attr("stroke-width", 1)
      .attr("stroke", "black")
      .attr("stroke-dasharray", "7,7");
        var liney= g.append("line")
      .attr("x1", xMid+130)
      .attr("x2", xMid+130)
      .attr("y1", -10)
      .attr("y2", 245)
      .attr("stroke-width", 1)
      .attr("stroke", "black")
      .attr("stroke-dasharray", "7,7");

        var arc = d3.arc()
                .innerRadius(0)
                .outerRadius(50);

        var pie = d3.pie()
                .sort(null)
                .value(function (d) {
                    return d;
                });

        var colors = d3.schemeCategory20;
        var color = ["steelblue","orange","green","red"]
        var pies = g.selectAll(null)
                .data(data)
                .enter()
                .append("g")
                .property("radius", function (d) {
                    return d.radius;
                })
                .attr("transform", function (d) {
                    return "translate(" + xScale(d.x) + "," + yScale(d.y) + ")";
                })
                .attr("fill", function (d, i) {
                    return color[i];
                });

        pies.selectAll()
                .data(function (d) {
                    return pie(d.slices);
                })
                .enter()
                .append("path")
                .attr("d", function (d) {
                    var radius = d3.select(this.parentNode).property("radius");
                    arc.outerRadius(radius);
                    return arc(d)
                })
                .attr("opacity",function(d,i){ return 1-i*0.7; });
    </script>
</body>