【问题标题】:D3.js pie chart to show the percentage of sales in each quarterD3.js 饼图显示每个季度的销售额百分比
【发布时间】:2017-11-10 16:48:51
【问题描述】:

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

这是我要制作的图表示例图片,图表中的每个部分代表一个节目的门票及其每个季度的销售额百分比。现在使用这个链接enter link description here

我已经制作了一个图表,但它并不完全是我需要的。d3.js 中是否有任何图表可以显示我在图片中提到的图表,或者我们需要对其进行自定义以获得这样的图表。

【问题讨论】:

  • D3 中绝对有 no 图表可用。 D3 只是适合操作 DOM 的方法的集合。我投票结束这个问题作为题外话。
  • ok @GerardoFurtado 使用这个 d3.js 是否可以创建一个看起来像我上传的图像中的 DOM 对象

标签: d3.js pie-chart


【解决方案1】:

d3.js 中是否有可用的图表来显示我提到的图表 在图片中还是我们需要对其进行自定义以获得这样的图表?

不,没有现成的解决方案,d3 作为问题注释上的comment 是用于操作 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 坐标、饼图的半径以及每个饼图的楔形值。

这样,您可以将一个组元素 (g) 附加到您的 svg,使用 d3 中的标准输入循环为每个饼图(或数据数组中的项目)添加一个元素,并在我们进行时定位组:

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 查看散点图示例以了解如何实现这些和其他功能,修改圆的散点图与修改饼图的散点图。

【讨论】:

  • 这是一个非常好的答案。但是,我必须说实话:作为一个 D3 程序员(这是我唯一的收入来源),我觉得这种问题不公平和不尊重,我希望没有人回答它,我希望它被关闭。我喜欢在 S.O. 帮助其他程序员,但这只是在做别人(整个!)的工作。我没有对您的答案投反对票,但我不会投赞成票。我希望你能理解我的意思。
  • @GerardoFurtado,我明白你的意思,考虑到问题的广泛性,我本来也不打算回答。最终,我认为我提出的解决方案并不难实现,也不是特别新颖(例如,有地理上的例子)。尽管对这个结论的不确定性可能是为什么我在围墙坐了一个小时并在框中输入答案后提交了答案。
【解决方案2】:

来自@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>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-03-02
    • 2016-07-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-16
    • 1970-01-01
    相关资源
    最近更新 更多