创建一个点图表,其中d3.js显示堆叠点

时间:2016-12-19 02:41:31

标签: javascript d3.js charts

我想在d3.js中创建一个圆点图,它看起来有点像条形图,但是有堆叠的点而不是条形。

这个问题与我想要的类似:D3.js (Wilkinson type) Dot Plot Example

我已经用这段代码来尝试完成我想要的东西,但是我被卡住了。

这是根据我的CSV文件中的苹果数据显示我想要的图像: dot chart mockup

我到目前为止的代码如下。当我在console.log d3.range(d.apples)时,我看到我每天都有一个数组,这就是我想要的。但是,我无法弄清楚如何为数组中的每个点绘制一个圆,其中x轴值是日#,而y轴值将是数组中的位置+ 1.

任何帮助都非常欢迎!谢谢。

代码:

<!DOCTYPE html>
    <meta charset="utf-8">
    <style>

    body {
      font: 10px sans-serif;
    }

    .axis path,
    .axis line {
      fill: none;
      stroke: #999;
      shape-rendering: crispEdges;
    }

    .dot {
      stroke: none;
    }

    </style>
    <body>
    <script src="//d3js.org/d3.v3.min.js"></script>
    <script>

    var margin = {top: 30, right: 20, bottom: 30, left: 50},
        width = 360 - margin.left - margin.right,
        height = 300 - margin.top - margin.bottom;

    var x = d3.scale.linear()
        .range([0, width]);

    var y = d3.scale.linear()
        .range([height, 0]);

    var color = d3.scale.category10();

    var xAxis = d3.svg.axis()
        .scale(x)
        .orient("bottom");

    var yAxis = d3.svg.axis()
        .scale(y)
        .orient("left");

    var svg = d3.select("body").append("svg")
        .attr("width", width + margin.left + margin.right)
        .attr("height", height + margin.top + margin.bottom)
            .append("g")
              .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    var chart = svg.append("g")
        .attr("id", "chart");


    d3.csv("fruit.csv", function(error, data) {
      if (error) throw error;
        // console.log("data: ", data);

      data.forEach(function(d) {
        d.day = +d.day;
        d.apples = +d.apples;
      });

      x.domain([0,4]);
      y.domain([0,6]);

      svg.append("g")
          .attr("class", "x axis")
          .attr("transform", "translate(0," + height + ")")
          .call(xAxis)
        .append("text")
          .attr("class", "label")
          .attr("x", width)
          .attr("y", -6)
          .style("text-anchor", "end")
          .text("Day");

      svg.append("g")
          .attr("class", "y axis")
          .call(yAxis)
        .append("text")
          .attr("class", "label")
          .attr("transform", "rotate(-90)")
          .attr("y", 6)
          .attr("dy", ".71em")
          .style("text-anchor", "end")
          .text("amount")

      chart.selectAll("g")
          .data(data)
                .enter()
                .append("g")
                .selectAll("circle")
                .data(data)
            .enter().append("circle")
          .attr("class", "dot")
          .attr("r", 3.5)
          .attr("cx", function(d, i) { return x(d.day); })

                //.attr("cy", function(d,i){ return y(d.apples); })

                .attr("cy", function(d,i,j){ 

                    var range = d3.range(d.apples);
                    console.log("apple range: ", range);

                    return y(range[i])
                })          
          .style("fill", "blue")
          .style("opacity", .5);

    });

    </script>

数据(fruit.csv):

"day","apples"
1,3
2,6
3,1
4,2

1 个答案:

答案 0 :(得分:2)

有几种方法可以做你想要的。我认为最传统的方法是嵌套数据并附加几个组,每个组合一个。但是,为了简单起见,我想提出一个不同的方法:让我们改变你的数据,使它变平,因此,在每个对象中,我们只有一个水果:

originalData.forEach(function(d) {
    data.push({
        day: d.day,
        fruit: "apples",
        amount: +d.apples
    });
    data.push({
        day: d.day,
        fruit: "pears",
        amount: +d.pears
    });
    data.push({
        day: d.day,
        fruit: "oranges",
        amount: +d.oranges
    });
});

其中originalData是数据数组,就像现在一样。

这样做,您的数据就会变成这样:

[{
    "day": "1",
    "fruit": "apples",
    "amount": 3
}, {
    "day": "1",
    "fruit": "pears",
    "amount": 6
}, {
    "day": "1",
    "fruit": "oranges",
    "amount": 3
}, {
    "day": "2",
    "fruit": "apples",
    "amount": 6
},//etc...
]

然后,我们设定了日期的序数比例:

var x = d3.scale.ordinal()
    .rangePoints([0, width], 0.5);
    .domain(data.map(d => d.day));

最后,我们画圆圈:

var dots = svg.selectAll("circle")
    .data(data)
    .enter().append("circle")
    .attr("class", "dot")
    .attr("r", 3.5)
    .attr("cx", function(d) {
        return x(d.day);
    })
    .attr("cy", function(d) {
        return y(d.amount)
    })
    .style("fill", function(d) {
        return color(d.fruit)
    })
    .style("opacity", .5);

这是一个演示(我放了一些标题,所以你可以将鼠标悬停在点上以检查水果和金额):

&#13;
&#13;
var rawData = d3.csv.parse(d3.select("#csv").text());

    var data = [];

    rawData.forEach(function(d) {
        data.push({
            day: d.day,
            fruit: "apples",
            amount: +d.apples
        });
        data.push({
            day: d.day,
            fruit: "pears",
            amount: +d.pears
        });
        data.push({
            day: d.day,
            fruit: "oranges",
            amount: +d.oranges
        });
    });

    var margin = {
            top: 30,
            right: 20,
            bottom: 30,
            left: 50
        },
        width = 360 - margin.left - margin.right,
        height = 300 - margin.top - margin.bottom;

    var x = d3.scale.ordinal()
        .rangePoints([0, width], 0.5);

    var y = d3.scale.linear()
        .range([height, 0]);

    var color = d3.scale.category10()
        .domain(["apples", "pears", "oranges"]);

    var xAxis = d3.svg.axis()
        .scale(x)
        .orient("bottom");

    var yAxis = d3.svg.axis()
        .scale(y)
        .orient("left");

    var svg = d3.select("body").append("svg")
        .attr("width", width + margin.left + margin.right)
        .attr("height", height + margin.top + margin.bottom)
        .append("g")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    var chart = svg.append("g")
        .attr("id", "chart");

    x.domain(data.map(d => d.day));
    y.domain([0, 10]);

    svg.append("g")
        .attr("class", "x axis")
        .attr("transform", "translate(0," + height + ")")
        .call(xAxis)
        .append("text")
        .attr("class", "label")
        .attr("x", width)
        .attr("y", -6)
        .style("text-anchor", "end")
        .text("Day");

    svg.append("g")
        .attr("class", "y axis")
        .call(yAxis)
        .append("text")
        .attr("class", "label")
        .attr("transform", "rotate(-90)")
        .attr("y", 6)
        .attr("dy", ".71em")
        .style("text-anchor", "end")
        .text("amount");

    var dots = svg.selectAll("circle")
        .data(data)
        .enter().append("circle")
        .attr("class", "dot")
        .attr("r", 3.5)
        .attr("cx", function(d) {
            return x(d.day);
        })
        .attr("cy", function(d) {
            return y(d.amount)
        })
        .style("fill", function(d) {
            return color(d.fruit)
        })
        .style("opacity", .5)
        .append("title")
        .text(function(d){ return d.fruit + ": " + d.amount});
&#13;
pre {
    display: none;
}

body {
    font: 10 px sans - serif;
}

.axis path,
.axis line {
    fill: none;
    stroke: #999;
    shape-rendering: crispEdges;
}

.dot {
    stroke: none;
}
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<pre id="csv">"day","apples","pears","oranges"
1,3,6,3
2,6,8,2
3,1,0,7
4,2,3,8</pre>
&#13;
&#13;
&#13;

编辑:在评论中进一步澄清之后,这就是OP想要的:

按照您的方式添加群组,但根据当天进行翻译:

var groups = svg.selectAll(".groups")
    .data(data)
    .enter()
    .append("g")
    .attr("transform", function(d) {
        return "translate(" + x(d.day) + ".0)";
    });

然后,在点中,使用d3.range设置数据:

var dots = groups.selectAll("circle")
    .data(function(d){ return d3.range(+d.apples + 1)})

这样,对于每个组,圆圈的数据一直到最大值。例如,如果在某一天&#34;苹果&#34;是3,数据将是:

[0, 1, 2, 3]

或者,如果&#34; apples&#34;是6,数据将是:

[0, 1, 2, 3, 4, 5, 6]

如果您不想要前导零,只需执行d3.range(1, +d.apples + 1)

这是一个演示:

&#13;
&#13;
var data = d3.csv.parse(d3.select("#csv").text());

var margin = {
        top: 30,
        right: 20,
        bottom: 30,
        left: 50
    },
    width = 360 - margin.left - margin.right,
    height = 300 - margin.top - margin.bottom;

var x = d3.scale.ordinal()
    .rangePoints([0, width], 0.5);

var y = d3.scale.linear()
    .range([height, 0]);

var xAxis = d3.svg.axis()
    .scale(x)
    .orient("bottom");

var yAxis = d3.svg.axis()
    .scale(y)
    .orient("left");

var svg = d3.select("body").append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
    .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

var chart = svg.append("g")
    .attr("id", "chart");

x.domain(data.map(d => d.day));
y.domain([0, 7]);

svg.append("g")
    .attr("class", "x axis")
    .attr("transform", "translate(0," + height + ")")
    .call(xAxis)
    .append("text")
    .attr("class", "label")
    .attr("x", width)
    .attr("y", -6)
    .style("text-anchor", "end")
    .text("Day");

svg.append("g")
    .attr("class", "y axis")
    .call(yAxis)
    .append("text")
    .attr("class", "label")
    .attr("transform", "rotate(-90)")
    .attr("y", 6)
    .attr("dy", ".71em")
    .style("text-anchor", "end")
    .text("amount");

var groups = svg.selectAll(".groups")
    .data(data)
    .enter()
    .append("g")
    .attr("transform", function(d) {
        return "translate(" + x(d.day) + ".0)";
    });

var dots = groups.selectAll("circle")
    .data(function(d) {
        return d3.range(1, +d.apples + 1)
    })
    .enter().append("circle")
    .attr("class", "dot")
    .attr("r", 3.5)
    .attr("cy", function(d) {
        return y(d)
    })
    .style("fill", "blue")
    .style("opacity", .5);
&#13;
pre {
    	display: none;
    }
		
		body {
      font: 10px sans-serif;
    }

    .axis path,
    .axis line {
      fill: none;
      stroke: #999;
      shape-rendering: crispEdges;
    }

    .dot {
      stroke: none;
    }
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<pre id="csv">"day","apples"
1,3
2,6
3,1
4,2</pre>
&#13;
&#13;
&#13;