Javascript 图表上的动态行数
我目前有一个d3多系列折线图,显示收到多少电子邮件和电话 我的数据检索和数据结构如下:Javascript 图表上的动态行数,javascript,c#,linq,d3.js,Javascript,C#,Linq,D3.js,我目前有一个d3多系列折线图,显示收到多少电子邮件和电话 我的数据检索和数据结构如下: var allCommunications = _uow.CommunicationRepository.Get() .Where(c => c.DateOpened.Year == year) .GroupBy(c => new { c.Method, c.DateOp
var allCommunications = _uow.CommunicationRepository.Get()
.Where(c => c.DateOpened.Year == year)
.GroupBy(c => new { c.Method, c.DateOpened.Month })
.Select(g => new
{
Type = g.Key.Method,
xVal = g.Key.Month,
Value = g.Count()
});
然后将其转换为以下结构:
public class LineChartData
{
public int xValue { get; set; }
public int EmailValue { get; set; }
public int PhoneValue { get; set; }
}
图形是使用以下javascript创建的:
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,
emailLineColour = "#779ECB", phoneLineColour = "#FF6961", tooltipTextColour = "white";
var x;
if (type == "month") {
var x = d3.scale.linear()
.domain([1, 31])
.range([0, width]);
} else if (type == "year")
{
var x = d3.scale.linear()
.domain([1, 12])
.range([0, width]);
}
var minPhone = Math.min.apply(Math, data.map(function (o) { return o.PhoneValue }));
var maxPhone = Math.max.apply(Math, data.map(function (o) { return o.PhoneValue }));
var minEmail = Math.min.apply(Math, data.map(function (o) { return o.EmailValue }));
var maxEmail = Math.max.apply(Math, data.map(function (o) { return o.EmailValue }));
var minY = Math.min(minPhone, minEmail);
var maxY = Math.max(maxPhone, maxEmail);
var y = d3.scale.linear()
.domain([minY, maxY + 5])
.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");
if (type == "month") {
var emailTip = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function (d) {
return "<strong>Emails:</strong> <span style='color:"+tooltipTextColour+"'>" + d.EmailValue + "</span><br /><strong>Day of Month:</strong><span style='color:white'>" + d.xValue + "</span>";
});
var phoneTip = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function (d) {
return "<strong>Calls:</strong> <span style='color:" + tooltipTextColour + "'>" + d.PhoneValue + "</span><br /><strong>Day of Month:</strong><span style='color:white'>" + d.xValue + "</span>";
});
}
else if (type == "year") {
var emailTip = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function (d) {
return "<strong>Emails:</strong> <span style='color:" + tooltipTextColour + "'>" + d.EmailValue + "</span><br /><strong>Month of Year:</strong><span style='color:white'>" + d.xValue + "</span>";
});
var phoneTip = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function (d) {
return "<strong>Calls:</strong> <span style='color:" + tooltipTextColour + "'>" + d.PhoneValue + "</span><br /><strong>Month of Year:</strong><span style='color:white'>" + d.xValue + "</span>";
});
}
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);
svg.call(emailTip);
svg.call(phoneTip);
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');
}
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');
svg.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
var emailLine = d3.svg.line()
.interpolate("linear")
.x(function (d) { return x(d.xValue); })
.y(function (d) { return y(d.EmailValue); });
var phoneLine = d3.svg.line()
.interpolate("linear")
.x(function (d) { return x(d.xValue); })
.y(function (d) { return y(d.PhoneValue); });
svg.selectAll('.emailLine')
.data(data)
.enter()
.append("path")
.attr("class", "line")
.attr('stroke', emailLineColour)
.attr("d", emailLine(data));
svg.selectAll("circle.emailLine")
.data(data)
.enter().append("svg:circle")
.attr("class", "emailLine")
.style("fill", emailLineColour)
.attr("cx", emailLine.x())
.attr("cy", emailLine.y())
.attr("r", 5)
.on('mouseover', emailTip.show)
.on('mouseout', emailTip.hide);
svg.selectAll('.phoneLine')
.data(data)
.enter()
.append("path")
.attr("class", "line")
.attr('stroke', phoneLineColour)
.attr("d", phoneLine(data));
svg.selectAll("circle.phoneLine")
.data(data)
.enter().append("svg:circle")
.attr("class", "phoneLine")
.style("fill", phoneLineColour)
.attr("cx", phoneLine.x())
.attr("cy", phoneLine.y())
.attr("r", 5)
.on('mouseover', phoneTip.show)
.on('mouseout', phoneTip.hide);
svg.append("text")
.attr("transform", "translate(" + (x(data[data.length - 1].xValue) + 5) + "," + y(data[data.length - 1].EmailValue) + ")")
.attr("dy", ".35em")
.style("fill", emailLineColour)
.text("Email");
svg.append("text")
.attr("transform", "translate(" + (x(data[data.length - 1].xValue) + 5) + "," + y(data[data.length - 1].PhoneValue) + ")")
.attr("dy", ".35em")
.style("fill", phoneLineColour)
.text("Phone");
if (callback) {
callback();
}
}
还是类似的
所以我想我的问题是,这是否是构建数据的正确方法,是否有任何修改查询以实现此目的的建议,以及如何编辑javascript以解释这一点
对于冗长的问题表示歉意,并提前感谢您的帮助
如果需要更多信息,请询问,我将尽我所能提供
谢谢
编辑:
下面是我在尝试Mark的建议后更新的代码:
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,
emailLineColour = "#779ECB", phoneLineColour = "#FF6961", tooltipTextColour = "white";
var color = d3.scale.category10();
var nest = d3.nest()
.key(function (d) { return d.Type; })
.entries(data);
var x;
if (type == "month") {
var x = d3.scale.linear()
.domain([1, 31])
.range([0, width]);
} else if (type == "year")
{
var x = d3.scale.linear()
.domain([1, 12])
.range([0, width]);
}
var y = d3.scale.linear()
.domain([0, 100])
.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()
.interpolate("linear")
.x(function (d) { return x(d.xValue); })
.y(function (d) { 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');
}
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');
svg.append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
color.domain(d3.keys(nest[0]).filter(function (key) { return key === nest[0].key; }));
var methods = color.domain().map(function (commType) {
return {
commType: commType,
values: nest.map(function (d) {
return { xValue: d.xVal, Value: d.Value };
})
};
});
x.domain(d3.extent(nest, function (d) { return d.xVal; }));
y.domain([
d3.min(methods, function (m) { return d3.min(m.values, function (v) { return v.Value; }); }),
d3.max(methods, function (m) { return d3.max(m.values, function (v) { return v.Value; }); })
]);
var method = svg.selectAll('.method')
.data(methods)
.enter().append('g')
.attr('class', 'method');
method.append('path')
.attr('class', 'line')
.attr('d', function (d) { return line(d.values); })
.attr('stroke', function (d) { return color(d.commType); });
method.append('text')
.datum(function (d) { return { commType: d.commType, value: d.values[d.values.length - 1] }; })
.attr("transform", function (d) { return "translate(" + x(d.value.xVal) + "," + y(d.value.Value) + ")"; })
.attr('x', 3)
.attr('dy', '.35em')
.text(function (d) { return d.commType; });
if (callback) {
callback();
}
}
对于StackOverflow来说,您的问题可能有点过于宽泛,但我会尽力帮助您。对于API应该如何输出数据的问题,我总是这样问:我的数据将如何在前端使用?在本例中,您正在尝试创建一个
d3
多折线图,d3
将需要一个包含数据点数组的对象数组(这是一个很好的例子)。JSON中的类似内容:
[
{
key: 'Email', //<-- identifies the line
values: [ //<-- points for the line
{
xVal: '20160101',
Value: 10
}, {
xVal: '20160102',
Value: 20
}, ...
]
}, {
key: 'Phone',
values: [
{
xVal: 'Jan',
Value: 30
}, {
xVal: '20160102',
Value: 25
}, ...
]
},
...
]
将生成一个JSON对象,如:
[{"Type":"Phone","xVal":"Feb","Value":1},{"Type":"Email","xVal":"Jan","Value":3},{"Type":"Phone","xVal":"Jan","Value":1}]
d3
然后就可以轻松地使用我们的“易于使用”格式:
var nest = d3.nest()
.key(function(d) { return d.Type; })
.entries(data);
产生:
[
{
"key":"Phone",
"values":[
{
"Type":"Phone",
"xVal":"Feb",
"Value":1
},
{
"Type":"Phone",
"xVal":"Jan",
"Value":1
}
]
},
{
"key":"Email",
"values":[
{
"Type":"Email",
"xVal":"Jan",
"Value":3
}
]
}
]
从这个结构中,你的多折线图变得轻而易举
编辑评论 我真的不明白你试图用你的一些代码做什么(特别是你的
方法变量-数据已经是d3
的好格式)。所以我进行了一些重构:
身体{
字体:10px无衬线;
}
.轴线路径,
.轴线{
填充:无;
行程:#000;
形状渲染:边缘清晰;
}
.x轴路径{
显示:无;
}
.线路{
填充:无;
笔画:钢蓝;
笔划宽度:1.5px;
}
//函数buildCommunicationLineChart(数据、占位符、回调、类型){
var保证金={
前20名,
右:30,,
底数:40,
左:50
},
宽度=960-margin.left-margin.right,
高度=500-margin.top-margin.bottom;
变量颜色={
“电话”:“FF6961”,
“电子邮件”:“#779ECB”
}
var color=d3.scale.category10();
风险值数据=[{
“类型”:“电话”,
“xValue”:1,
“价值”:5
}, {
“类型”:“电子邮件”,
“xValue”:1,
“价值”:7
}, {
“类型”:“电子邮件”,
“xValue”:2,
“价值”:1
}, {
“类型”:“电话”,
“xValue”:2,
“价值”:4
}, {
“类型”:“电话”,
“xValue”:4,
“价值”:2
}];
var nest=d3.nest()
.键(功能(d){
返回d.类型;
})
.条目(数据);
var x;
var type=“月”;
如果(类型=“月”){
var x=d3.scale.linear()
.domain([1,31])
.范围([0,宽度]);
}否则如果(类型=“年”){
var x=d3.scale.linear()
.domain([1,12])
.范围([0,宽度]);
}
变量y=d3.scale.linear()
.domain([01100])
.范围([高度,0]);
var xAxis=d3.svg.axis()
.比例(x)
.1尺寸(高度)
.1(10)
.tickSubdivide(真)
.东方(“底部”);
var yAxis=d3.svg.axis()
.比例(y)
.1(10)
.1.1尺寸(-宽度)
.tickSubdivide(真)
.东方(“左”);
var line=d3.svg.line()
.插入(“线性”)
.x(功能(d){
返回x(d.xValue);
})
.y(功能(d){
返回y(d值);
});
var svg=d3。选择('body')。追加(“svg”)
.attr(“宽度”,宽度+边距。左侧+边距。右侧+50)
.attr(“高度”,高度+边距。顶部+边距。底部)
.attr(“类别”、“图表”)
.附加(“g”)
.attr(“转换”、“平移”(+margin.left+)、“+margin.top+”);
y、 领域([
0,
max(嵌套,函数(t){返回d3.max(t.values,函数(v){返回v.Value;});})
]);
x、 领域([
d3.min(嵌套,函数(t){返回d3.min(t.values,函数(v){返回v.xValue;});}),
max(嵌套,函数(t){返回d3.max(t.values,函数(v){返回v.xValue;});})
]);
nest.forEach(函数(d){
对于(var i=x.domain()[0];我您的问题对于StackOverflow来说可能有点过于宽泛,但我会尽力提供帮助。我处理API应该如何输出数据的问题的方式是询问我的数据将如何在前端使用?在这种情况下,您试图创建一个d3
多折线图,d3
将需要一个objec数组ts包含一个数据点数组(这里是一个很好的例子)。类似于JSON:
[
{
key: 'Email', //<-- identifies the line
values: [ //<-- points for the line
{
xVal: '20160101',
Value: 10
}, {
xVal: '20160102',
Value: 20
}, ...
]
}, {
key: 'Phone',
values: [
{
xVal: 'Jan',
Value: 30
}, {
xVal: '20160102',
Value: 25
}, ...
]
},
...
]
将生成一个JSON对象,如:
[{"Type":"Phone","xVal":"Feb","Value":1},{"Type":"Email","xVal":"Jan","Value":3},{"Type":"Phone","xVal":"Jan","Value":1}]
d3
然后就可以轻松地使用我们的“易于使用”格式:
var nest = d3.nest()
.key(function(d) { return d.Type; })
.entries(data);
产生:
[
{
"key":"Phone",
"values":[
{
"Type":"Phone",
"xVal":"Feb",
"Value":1
},
{
"Type":"Phone",
"xVal":"Jan",
"Value":1
}
]
},
{
"key":"Email",
"values":[
{
"Type":"Email",
"xVal":"Jan",
"Value":3
}
]
}
]
从这个结构中,你的多折线图变得轻而易举
编辑评论
我真的不明白你想用你的一些代码做什么(特别是你的方法变量-数据已经是d3
的好格式)。所以我重构了一点:
身体{
字体:10px无衬线;
}
.轴线路径,
.轴线{
填充:无;
行程:#000;
形状渲染:边缘清晰;
}
.x轴路径{
显示:无;
}
.线路{
填充:无;
笔画:钢蓝;
笔划宽度:1.5px;
}
//函数buildCommunicationLineChart(数据、占位符、回调、类型){
var保证金={
前20名,
右:30,,
底数:40,
左:50
},
宽度=960-margin.left-margin.rig