javascript - 使用显示堆叠点的 d3.js 创建点图

标签 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>

数据(水果.csv):

"day","apples"
1,3
2,6
3,1
4,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);

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

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});
pre {
    display: none;
}

body {
    font: 10 px sans - serif;
}

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

.dot {
    stroke: none;
}
<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>


编辑:在评论中进一步澄清后,这就是 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)})

这样,对于每个组,圆圈的数据一直到最大值。例如,如果在给定的一天“苹果”是 3,则数据将是:

[0, 1, 2, 3]

或者,如果“apples”是 6,则数据将是:

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

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

这是一个演示:

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);
pre {
    	display: none;
    }
		
		body {
      font: 10px sans-serif;
    }

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

    .dot {
      stroke: none;
    }
<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>

关于javascript - 使用显示堆叠点的 d3.js 创建点图,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41215085/

相关文章:

javascript - 每次调用一个函数你能返回不同的值吗? - JavaScript

javascript - D3js,在 selectAll 中选择 rect 时未捕获引用错误 ('rect' )

d3.js-堆叠的条形图中的第2组数据值

apache-flex - Flex 图表库

javascript - 在 Google 柱形图中按年份汇总类别

javascript - Chrome window.print() 缺少页面元素

javascript - 是否可以使用 "call"或 "apply"将参数数组一次传递给多个函数?

java - 丰富的面孔 : how to add static text beside value on InputNumberSlider tooltip?

html - 能够选择缩放文本吗?

javascript - 我可以使用什么工具来创建带有倒轴的 HTML5 烛台图表?