绘制具有 2 个或 3 个彩色区域的密度函数?

Plot density function with 2 or 3 colored areas?

我刚开始学习 javascriptd3.js,参加了几门 lynda.com 课程。我的 objective 是创建一个函数,该函数采用数字数组和截止值并生成如下图:

我能够编写 javascript 生成此代码的代码:

唉,我很难找到一种方法来告诉 d3.js 应该读取 -opts.threshold 左侧的区域,-opts.thresholdopts.threshold蓝色,其余绿色。

这是我的 javascript 代码:

HTMLWidgets.widget({

  name: 'IMposterior',

  type: 'output',

  factory: function(el, width, height) {

    // TODO: define shared variables for this instance

    return {

      renderValue: function(opts) {

        console.log("MME: ", opts.MME);
        console.log("threshold: ", opts.threshold);
        console.log("prob: ", opts.prob);
        console.log("colors: ", opts.colors);


        var margin = {left:50,right:50,top:40,bottom:0};

        var xMax = opts.x.reduce(function(a, b) {
          return Math.max(a, b);

        });
        var yMax = opts.y.reduce(function(a, b) {
          return Math.max(a, b);

        });
        var xMin = opts.x.reduce(function(a, b) {
          return Math.min(a, b);

        });
        var yMin = opts.y.reduce(function(a, b) {
          return Math.min(a, b);

        });


        var y = d3.scaleLinear()
                    .domain([0,yMax])
                    .range([height,0]);

        var x = d3.scaleLinear()
                    .domain([xMin,xMax])
                    .range([0,width]);


        var yAxis = d3.axisLeft(y);
        var xAxis = d3.axisBottom(x);


        var area = d3.area()
                         .x(function(d,i){ return x(opts.x[i]) ;})
                         .y0(height)
                         .y1(function(d){ return y(d); });

        var svg = d3.select(el).append('svg').attr("height","100%").attr("width","100%");
        var chartGroup = svg.append("g").attr("transform","translate("+margin.left+","+margin.top+")");


        chartGroup.append("path")
             .attr("d", area(opts.y));


        chartGroup.append("g")
            .attr("class","axis x")
            .attr("transform","translate(0,"+height+")")
            .call(xAxis);


      },

      resize: function(width, height) {

        // TODO: code to re-render the widget with a new size

      }

    };
  }
});

如果这有帮助,我将所有代码保存在 public github repo

在此answer中提出了两种解决方案,使用渐变或使用多个区域。我将提出一个替代解决方案:使用该区域作为三个矩形的剪辑路径,它们共同覆盖整个绘图区域。

通过创建一个包含每个矩形左右边缘的数据数组来制作矩形。附加矩形时,矩形高度和y属性可以分别设置为svg高度和零,因此不需要包含在数组中。

第一个矩形的左边缘为 xScale.range()[0],最后一个矩形的右边缘为 xScale.range()[1]。中间坐标可以用xScale(1)xScale(-1)

这样的数组可能看起来像(使用您建议的配置和 x 刻度名称):

var rects = [ 
              [x.range()[0],x(-1)],
              [x(-1),x(1)],
              [x(1),x.range()[1]]
            ]

然后放置它们:

.enter()
  .append("rect")
  .attr("x", function(d) { return d[0]; })
  .attr("width", function(d) { return d[1] - d[0]; })
  .attr("y", 0)
  .attr("height",height)

不要忘记为矩形设置 clip-path 属性: .attr("clip-path","url(#areaID)"),并将填充设置为三种不同的颜色。

现在您所要做的就是将您的区域的填充和描边设置为 none,并将您的区域附加到具有指定 ID 的剪辑路径:

svg.append("clipPath)
.attr("id","area")
.append("path")
.attr( // area attributes
...

Here 概念的实际应用(虽然使用的是 v3,但不应影响矩形或文本路径。

感谢@andrew-reid 的建议,我得以实施使用多个区域的解决方案。

HTMLWidgets.widget({

  name: 'IMposterior',

  type: 'output',

  factory: function(el, width, height) {

    // TODO: define shared variables for this instance

    return {

      renderValue: function(opts) {

        console.log("MME: ", opts.MME);
        console.log("threshold: ", opts.threshold);
        console.log("prob: ", opts.prob);
        console.log("colors: ", opts.colors);
        console.log("data: ", opts.data);


        var margin = {left:50,right:50,top:40,bottom:0};

        xMax = d3.max(opts.data, function(d) { return d.x ; });
        yMax = d3.max(opts.data, function(d) { return d.y ; });
        xMin = d3.min(opts.data, function(d) { return d.x ; });
        yMin = d3.min(opts.data, function(d) { return d.y ; });

        var y = d3.scaleLinear()
                    .domain([0,yMax])
                    .range([height,0]);

        var x = d3.scaleLinear()
                    .domain([xMin,xMax])
                    .range([0,width]);


        var yAxis = d3.axisLeft(y);
        var xAxis = d3.axisBottom(x);


        var area = d3.area()
                         .x(function(d){ return x(d.x) ;})
                         .y0(height)
                         .y1(function(d){ return y(d.y); });

        var svg = d3.select(el).append('svg').attr("height","100%").attr("width","100%");
        var chartGroup = svg.append("g").attr("transform","translate("+margin.left+","+margin.top+")");


        chartGroup.append("path")
             .attr("d", area(opts.data.filter(function(d){  return d.x< -opts.MME ;})))
             .style("fill", opts.colors[0]);

        chartGroup.append("path")
             .attr("d", area(opts.data.filter(function(d){  return d.x > opts.MME ;})))
             .style("fill", opts.colors[2]);

        if(opts.MME !==0){
          chartGroup.append("path")
             .attr("d", area(opts.data.filter(function(d){  return (d.x < opts.MME & d.x > -opts.MME) ;})))
             .style("fill", opts.colors[1]);
        }


        chartGroup.append("g")
            .attr("class","axis x")
            .attr("transform","translate(0,"+height+")")
            .call(xAxis);


      },

      resize: function(width, height) {

        // TODO: code to re-render the widget with a new size

      }

    };
  }
});