Javascript 图表上的动态行数

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

我目前有一个d3多系列折线图,显示收到多少电子邮件和电话

我的数据检索和数据结构如下:

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