d3.js - 按其中一个的值排序两个系列

时间:2017-05-11 21:19:08

标签: javascript csv d3.js

我有一个从CSV文件加载的两个系列。我想在x轴上按照其中一个的y值来订购这两个系列,但我的订单例程不起作用。有人知道如何订购这些系列吗?

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <title>D3 Teste Template</title>
    <script type="text/javascript" src="d3/d3.js"></script>

    <style type="text/css"> 
        div.bar {
        display: inline-block;
        width: 20px;
        height: 75px; /* We'll override height later */
        background-color: teal;
        margin-right: 2px;
        }

    </style>

</head>
<body>

<p>Click to sort!!</p>

<script type="text/javascript"> 

    var w = 800;
    var h = 500;
    var padding = 30;

    //Define scale
    var x = d3.scaleLinear().range([padding, w-padding*2]);
    var y = d3.scaleLinear().range([h-padding, padding]);
    var z = d3.scaleOrdinal(d3.schemeCategory10);

    var xAxis = d3.axisBottom().scale(x).ticks(10);
    var yAxis = d3.axisLeft().scale(y).ticks(5);

    //Define Canvas     
    var svg = d3.select("body").append("svg")
    .attr("width",w).attr("height", h)

    //Define clipping path
    svg.append("clipPath").attr("id", "chart-area").append("rect")
    .attr("x", padding).attr("y", padding)
    .attr("width", w - padding*3).attr("height", h - padding*2);

    //Loading CSV
    d3.csv("eval_silver_standard.csv", function(error, data){
        if (error) throw error;

        // Extract yn using key
        var seriesNames = d3.keys(data[0])
        .filter(function(d) { return d !== "x"; })
        .sort();

        // Map data to cartesian tuple {x,y}
        var series = seriesNames.map(function(series) {
        return data.map(function(d) {
        return {x: +d.x, y: +d[series]};
        });
        });

    // Compute domains
    x.domain(d3.extent(d3.merge(series), function(d) { 
    return d.x; })).nice();
    y.domain(d3.extent(d3.merge(series), function(d) { 
    return d.y; })).nice();

    // X axis.
    svg.append("g").attr("class", "axis").attr("transform",
    "translate(0," + (h - padding) + ")").call(xAxis)

    // Y axis.
    svg.append("g").attr("class", "axis").attr("transform",
    "translate(" + padding + ",0)").call(yAxis)

    // Append circles
    svg.selectAll(".series")
    .data(series)
    .enter().append("g")
    .attr("class", "series")
    .style("fill", function(d, i) { return z(i); })
    .selectAll(".point")
    .data(function(d) { return d; })
    .enter().append("circle")
    .attr("class", "point")
    .attr("cx", function(d) { return x(d.x); })
    .attr("cy", function(d) { return y(d.y); })
    .attr("r", 4);

    //On click, update order
    d3.select("p").on("click", function() {
        // Order circles
        // Code goes here
    });
    });

</script>

</body>
</html>

CSV文件:

x,y1,y2
0,0.85337,0.10198
1,0,0.30274
2,0.85311,0.08623
3,0.82759,0.08711
4,0.89602,0.03472
5,0.8,0.16295
6,0,0.27028
7,0.76167,0.2155
8,0.75,0.08359
9,0.81775,0.16535
10,0,0.2311

我的排序功能没有运行,我不知道为什么呢

1 个答案:

答案 0 :(得分:0)

嗯,如果您有一个用于在x轴上定位圆的点刻度,这可能会更容易。由于你有线性刻度,我写的函数有一些繁琐的部分......

首先要做的事情是:您必须创建一个重新绘制SVG中元素的函数。在下面的演示中,它名为draw,我们将series数组传递给该函数。

然而,最重要的部分是对series数组进行排序。例如,这是对蓝点进行分类并相应地重新排列橙色点的功能:

d3.select("#button1").on("click", function() {
    series[0] = series[0].sort(function(a, b) {
        return d3.ascending(a.y, b.y)
    }).map(function(e, i) {
        return {
            x: i,
            fx: e.fx,
            y: e.y,
            u: e.u
        }
    });
    series[1].forEach(function(d, i, arr) {
        d.x = order0.indexOf(d.fx)
    });
    draw(series);
});

正如您所看到的,单独排序不会起作用,因为x属性将被排序在一起。因此,我们必须根据y进行排序,然后将x从0重新分配到10。

为了保持对象的持久性,我使用u创建了一个名为Math.random()的属性。两个属性具有相同值的可能性很小。但是,我建议您在每个对象中创建自己的唯一属性(否则您无法保持对象的持久性)。 fx属性只是为了跟踪原始序列,order0 / order1是具有每个序列的有序序列的数组。

以下是演示,使用大部分代码但创建draw并缩小SVG大小,以便更好地适应Stack片段。单击按钮进行排序:

&#13;
&#13;
var data = d3.csvParse(d3.select("#csv").text());

var w = 600;
var h = 200;
var padding = 30;

//Define scale
var x = d3.scaleLinear().range([padding, w - padding * 2]);
var y = d3.scaleLinear().range([h - padding, padding]);
var z = d3.scaleOrdinal(d3.schemeCategory10);

var xAxis = d3.axisBottom().scale(x).ticks(10);
var yAxis = d3.axisLeft().scale(y).ticks(5);

//Define Canvas     
var svg = d3.select("body").append("svg")
  .attr("width", w).attr("height", h)

//Define clipping path
svg.append("clipPath").attr("id", "chart-area").append("rect")
  .attr("x", padding).attr("y", padding)
  .attr("width", w - padding * 3).attr("height", h - padding * 2);



// Extract yn using key
var seriesNames = d3.keys(data[0])
  .filter(function(d) {
    return d !== "x";
  })
  .sort();

// Map data to cartesian tuple {x,y}
var series = seriesNames.map(function(series) {
  return data.map(function(d, i) {
    return {
      x: +d.x,
      fx: +d.x,
      y: +d[series],
      u: Math.random()
    };
  });
});

var order0 = series[0].sort(function(a, b) {
  return d3.ascending(a.y, b.y)
}).map(function(e) {
  return e.x
});

var order1 = series[1].sort(function(a, b) {
  return d3.ascending(a.y, b.y)
}).map(function(e) {
  return e.x
});

// Compute domains
x.domain(d3.extent(d3.merge(series), function(d) {
  return d.x;
})).nice();
y.domain(d3.extent(d3.merge(series), function(d) {
  return d.y;
})).nice();

// X axis.
svg.append("g").attr("class", "axis").attr("transform",
  "translate(0," + (h - padding) + ")").call(xAxis)

// Y axis.
svg.append("g").attr("class", "axis").attr("transform",
  "translate(" + padding + ",0)").call(yAxis);

draw(series);

function draw(data) {

  // Append circles
  var groups = svg.selectAll(".series")
    .data(data);

  var groupsEnter = groups.enter()
    .append("g")
    .attr("class", "series")
    .style("fill", function(d, i) {
      return z(i);
    });

  var circlesEnter = groupsEnter.selectAll(".point")
    .data(function(d) {
      return d;
    }, function(e) {
      return e.u
    }).enter().append("circle")
    .attr("class", "point")
    .attr("cx", function(d) {
      return x(d.x);
    })
    .attr("cy", function(d) {
      return y(d.y);
    })
    .attr("r", 4);

  groups.selectAll(".point").data(function(d) {
      return d;
    }, function(e) {
      return e.u
    })
    .transition()
    .duration(2500)
    .attr("cx", function(d) {
      return x(d.x);
    })
    .attr("cy", function(d) {
      return y(d.y);
    })
    .attr("r", 4);

}

//On click, update order
d3.select("#button1").on("click", function() {
  series[0] = series[0].sort(function(a, b) {
    return d3.ascending(a.y, b.y)
  }).map(function(e, i) {
    return {
      x: i,
      fx: e.fx,
      y: e.y,
      u: e.u
    }
  });
  series[1].forEach(function(d, i, arr) {
    d.x = order0.indexOf(d.fx)
  });
  draw(series);
});

d3.select("#button2").on("click", function() {
  series[1] = series[1].sort(function(a, b) {
    return d3.ascending(a.y, b.y)
  }).map(function(e, i) {
    return {
      x: i,
      fx: e.fx,
      y: e.y,
      u: e.u
    }
  });
  series[0].forEach(function(d, i, arr) {
    d.x = order1.indexOf(d.fx)
  });
  draw(series);
});
&#13;
pre {
  display: none;
}

button {
  margin-right: 5px;
}
&#13;
<script src="https://d3js.org/d3.v4.min.js"></script>
<button id="button1">Click to sort by Blue</button><button id="button2">Click to sort by Orange</button>
<pre id="csv">x,y1,y2
0,0.85337,0.10198
1,0,0.30274
2,0.85311,0.08623
3,0.82759,0.08711
4,0.89602,0.03472
5,0.8,0.16295
6,0,0.27028
7,0.76167,0.2155
8,0.75,0.08359
9,0.81775,0.16535
10,0,0.2311</pre>
&#13;
&#13;
&#13;