D3 图表缺少数据
本文关键字:数据 D3 | 更新日期: 2023-09-27 18:36:08
我有一个 d3 折线图,效果很好。它映射了当月或当年收到的电话和电子邮件的数量。但是,唯一的问题是,如果仅在每月的某一天收到电话,则在该值处仅显示一个点。我想发生的是,对于每天没有值,则该值被视为 0。我环顾了一下谷歌,但我只能找到走另一条路的例子。有人能帮忙吗?
我的代码是:
function buildCommunicationLineChart(data, placeholder, callback, type) {
var margin = { top: 20, right: 30, bottom: 40, left: 50 },
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom,
tooltipTextColour = "white";
var color = ["#FF9797", "#86BCFF", "#33FDC0", "#EFA9FE", "#7BCAE1", "#8C8CFF", "#80B584", "#C88E8E", "#DD597D", "#D8F0F8", "#DD597D", "#D6C485", "#990099", "#5B5BFF", "#1FCB4A", "#000000", "#00BFFF", "#BE81F7", "#BDBDBD", "#F79F81"];
if (type == "month") {
var x = d3.scale.linear()
.domain([1, 31])
.range([0, width]);
var tip = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function (d) {
return "<strong>Value:</strong> <span style='color:" + tooltipTextColour + "'>" + d.Value + "</span><br /><strong>Day of Month:</strong><span style='color:white'>" + d.xValue + "</span>";
});
}
else if (type == "year")
{
var x = d3.scale.linear()
.domain([1, 12])
.range([0, width]);
var tip = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function (d) {
return "<strong>Value:</strong> <span style='color:" + tooltipTextColour + "'>" + d.Value + "</span><br /><strong>Month of Year:</strong><span style='color:white'>" + d.xValue + "</span>";
});
}
var y = d3.scale.linear()
.domain([0, 60])
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.tickSize(-height)
.tickPadding(10)
.tickSubdivide(true)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.tickPadding(10)
.tickSize(-width)
.tickSubdivide(true)
.orient("left");
var line = d3.svg.line()
.x(function (d) { var xTest = x(d.xValue); return x(d.xValue); })
.y(function (d) { var yTest = y(d.Value); return y(d.Value); });
var svg = placeholder.append("svg")
.attr("width", width + margin.left + margin.right + 50)
.attr("height", height + margin.top + margin.bottom)
.attr("class", "chart")
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
if (type == "year") {
svg.append("g")
.attr("class", "x axis")
.append("text")
.attr("class", "axis-label")
.attr("transform", "none")
.attr("y", (-margin.left) + 530)
.attr("x", -height + 860)
.text('Month');
}
else if (type == "month") {
svg.append("g")
.attr("class", "x axis")
.append("text")
.attr("class", "axis-label")
.attr("transform", "none")
.attr("y", (-margin.left) + 525)
.attr("x", -height + 860)
.text('Day');
}
var methods = d3.entries(data);
y.domain([
d3.min(methods, function (c) { return d3.min(c.value.DataPoints, function (v) { return v.Value; }); }) -1,
d3.max(methods, function (c) { return d3.max(c.value.DataPoints, function (v) { return v.Value; }); }) +1
]);
svg.call(tip);
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
svg.append("g")
.attr("class", "y axis")
.append("text")
.attr("class", "axis-label")
.attr("transform", "rotate(-90)")
.attr("y", (-margin.left) + 15)
.attr("x", -height / 2)
.text('Communications');
var method = svg.selectAll('.method')
.data(methods)
.enter().append('g')
.attr('class', 'method')
.style('fill',function(d,i){
return color[i];
})
.style('stroke', function (d, i) {
return color[i];
});
var m = method.append('path')
.attr('class', function (d, i) { return 'line line-' + i; })
.attr('d', function (d) { return line(d.value.DataPoints); });
method.selectAll('circle')
.data(function (d) { return d.value.DataPoints; })
.enter().append("circle")
.attr('class', function (d, i) { return 'circle circle-' + i; })
.attr("cx", function (dd) { return x(dd.xValue); })
.attr("cy", function (dd) { return y(dd.Value); })
.attr("r", 3.5)
.on('mouseover', tip.show)
.on('mouseout', tip.hide);
method.append('text')
.datum(function (d) { return { commType: d.value.Type, value: d.value.DataPoints[d.value.DataPoints.length - 1] }; })
.attr("transform", function (d) { return "translate(" + x(d.value.xValue) + "," + y(d.value.Value) + ")"; })
.attr('x', 5)
.attr('class',function(d,i){return 'text text-'+i;})
.attr('dy', '.15em')
.style('stroke','none')
.text(function (d) { return d.commType; });
if (callback) {
callback();
}
}
非常感谢:)
编辑
xVal 是月份编号的日期数:
[{
"Type": "Email",
"DataPoints": [{
"xValue": 1,
"Value": 17
},
{
"xValue": 2,
"Value": 59
}]
},
{
"Type": "Phone",
"DataPoints": [{
"xValue": 1,
"Value": 1
}]
}]
我将假设一些要点:
您正在从数据源接收月份数组的年份数组(如果您逐月接收,则必须为每个月发出 Get 请求,这很容易,但我希望您有一条路线为您提供年份数据,对于此结果做 12 http get 并不酷)数据,例如:
[
{
month: "January",
data: [{
"Type": "Email",
"DataPoints":
[{
"xValue": 1,
"Value": 17
},
{
"xValue": 2,
"Value": 59
}]
},
{
"Type": "Phone",
"DataPoints": [{
"xValue": 1,
"Value": 1
}]
}]
}
},{
month: "February",
data: [...]
},
...
]
我将在 Js 中执行客户端操作:
假设每个月是 31 天,我懒得考虑确切地做这件事,但这并不难,xValue 是月份中的一天。
function completeMonths(data){
_.each(data, function(month){
_.each(month.data, function(typeSource){
for(var i = 1; i < 32; i++){
var dayOnList = _.some(typeSource.DataPoints, function(day){
return day.xValue == i;
});
if(!dayOnList)
typeSource.DataPoints.push({xValue: i, Value: 0});
}
typeSource.DataPoints = _.sortBy(typeSource.DataPoints, function(ite){
return ite.xValue;
});
});
});
return data;
}
可能不是更优化的代码,但我已经尝试过并且它正在工作。实际上,我认为最好将月份的天数映射到现在,并从 1 到 31 数组中拒绝它们。顺便说一句,您的 JSON 格式不正确,您缺少结束"]"。
JSON 结构如下:
[{
"Type": "Email",
"DataPoints": [{
"xValue": 1,
"Value": 17
},
{
"xValue": 2,
"Value": 59
}]
},
{
"Type": "Phone",
"DataPoints": [{
"xValue": 1,
"Value": 1
}]
}]
假设您的DataPoints
数组按当月xValue
和 31 天排序:
var maxDaysInMonth = 31;
// for each dataset
dataset.forEach(function(d){
// loop our month
for (var i = 1; i <= maxDaysInMonth; i++){
// if there's no xValue at that location
if (!d.DataPoints.some(function(v){ return (v.xValue === i) })){
// add a zero in place
d.values.splice((i - 1), 0, {xValue: i, Value: 0});
}
}
});