简体   繁体   English

D3 v4 平行坐标图画笔选择

[英]D3 v4 Parallel Coordinate Plot Brush Selection

The parallel coordinate plot we are using and the data for the plot can be found here .我们正在使用的平行坐标图和该图的数据可以在这里找到。 This parallel coordinate plot does not work with version 4 of d3.此平行坐标图不适用于 d3 的第 4 版。 We have made changes based on the API changes from v3 to v4.我们根据从 v3 到 v4 的 API 更改进行了更改。 I think the main issue is in the brush function shown below.我认为主要问题在于如下所示的画笔功能。

function brush() {
    let actives = dimensions.filter(function (p) {
        return d3.brushSelection(y[p]) !== null;
    });

    console.log(actives);
    let extents = actives.map(function (p) {
            return d3.brushSelection(y[p]);
    });

    foreground.style("display", function (d) {
        return actives.every(function (p, i) {
            return extents[i][0] <= d[p] && d[p] <= extents[i][1];
        }) ? null : "none";
    });
}

The log shows "Array []" for actives.日志显示活动的“数组 []”。 Currently we set each dimensions brush extent to be [[-8,0],[8,height]], which may be an issue as well.目前我们将每个维度的笔刷范围设置为 [[-8,0],[8,height]],这也可能是一个问题。 The full code is provided below.下面提供了完整的代码。

<!DOCTYPE html>
<meta charset="utf-8">
<style>
svg {
    font: 10px sans-serif;
}

.background path {
    fill: none;
    stroke: #ddd;
    shape-rendering: crispEdges;
}

.foreground path {
    fill: none;
    stroke: steelblue;
}

.brush .extent {
    fill-opacity: .3;
    stroke: #fff;
    shape-rendering: crispEdges;
}

.axis line,
.axis path {
    fill: none;
    stroke: #000;
    shape-rendering: crispEdges;
}

.axis text {
    text-shadow: 0 1px 0 #fff, 1px 0 0 #fff, 0 -1px 0 #fff, -1px 0 0       #fff;
    cursor: move;
}
</style>
<body>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
let margin = {top: 30, right: 10, bottom: 10, left: 10},
    width = 960 - margin.left - margin.right,
    height = 500 - margin.top - margin.bottom;

let x = d3.scalePoint().range([0, width]).padding(1),
    y = {},
    dragging = {};

let line = d3.line(),
    axis = d3.axisLeft(), //Argument for axisLeft? Compare to code on original plot
    background,
    foreground;

let svg = d3.select("body").append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
    .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

d3.csv("cars.csv", function (error, cars) {

    // Extract the list of dimensions and create a scale for each.
    x.domain(dimensions = d3.keys(cars[0]).filter(function (d) {
        return d !== "name" && (y[d] = d3.scaleLinear()
            .domain(d3.extent(cars, function (p) {
                return +p[d];
            }))
            .range([height, 0]));
    }));

    // Add grey background lines for context.
    background = svg.append("g")
        .attr("class", "background")
        .selectAll("path")
        .data(cars)
        .enter().append("path")
        .attr("d", path);

    // Add blue foreground lines for focus.
    foreground = svg.append("g")
        .attr("class", "foreground")
        .selectAll("path")
        .data(cars)
        .enter().append("path")
        .attr("d", path);

    // Add a group element for each dimension.
    let g = svg.selectAll(".dimension")
        .data(dimensions)
        .enter().append("g")
        .attr("class", "dimension")
        .attr("transform", function (d) {
            return "translate(" + x(d) + ")";
        })
        .call(d3.drag()
            .subject(function (d) {
                return {x: x(d)};
            })
            .on("start", function (d) {
                dragging[d] = x(d);
                background.attr("visibility", "hidden");
            })
            .on("drag", function (d) {
                dragging[d] = Math.min(width, Math.max(0, d3.event.x));
                foreground.attr("d", path);
                dimensions.sort(function (a, b) {
                    return position(a) - position(b);
                });
                x.domain(dimensions);
                g.attr("transform", function (d) {
                    return "translate(" + position(d) + ")";
                })
            })
            .on("end", function (d) {
                delete dragging[d];
                transition(d3.select(this)).attr("transform", "translate(" + x(d) + ")");
                transition(foreground).attr("d", path);
                background
                    .attr("d", path)
                    .transition()
                    .delay(500)
                    .duration(0)
                    .attr("visibility", null);
            }));

    // Add an axis and title.
    g.append("g")
        .attr("class", "axis")
        .each(function (d) {
            d3.select(this).call(axis.scale(y[d]));
        })
        .append("text")
        .style("text-anchor", "middle")
        .attr("y", -9)
        .text(function (d) {
            return d;
        });

    // Add and store a brush for each axis.
    g.append("g")
        .attr("class", "brush")
        .each(function (d) {
            d3.select(this).call(y[d].brush = d3.brushY().extent([[-8,0],[8,height]]).on("start", brushstart).on("brush", brush));
                      })
        .selectAll("rect")
        .attr("x", -8)
        .attr("width", 16);
});

function position(d) {
    let v = dragging[d];
    return v == null ? x(d) : v;
}

function transition(g) {
    return g.transition().duration(500);
}

// Returns the path for a given data point.
function path(d) {
    return line(dimensions.map(function (p) {
        return [position(p), y[p](d[p])];
    }));
}

function brushstart() {
    d3.event.sourceEvent.stopPropagation();
}

// Handles a brush event, toggling the display of foreground lines.
function brush() {
    //return !y[p].brush.empty was the original return value.

    let actives = dimensions.filter(function (p) {
        return d3.brushSelection(y[p]) !== null;
    });

    console.log(actives);
    let extents = actives.map(function (p) {
            return d3.brushSelection(y[p]);
    });

    foreground.style("display", function (d) {
        return actives.every(function (p, i) {
            return extents[i][0] <= d[p] && d[p] <= extents[i][1];
        }) ? null : "none";
    });
}
</script>

If anyone is familiar with d3 and could offer any guidance it would be greatly appreciated.如果有人熟悉 d3 并且可以提供任何指导,我们将不胜感激。 We also tried using d3.event.selection and y[p].brush.selection in the brush function.我们还尝试在画笔功能中使用 d3.event.selection 和 y[p].brush.selection。

I stumbled upon the exact same issue but managed to resolve it after below changes.我偶然发现了完全相同的问题,但在以下更改后设法解决了它。

Add brush for each axis this way:以这种方式为每个轴添加画笔:

y[d] = d3.scaleLinear().domain(d3.extent(data, function(p) {
  return +p[d];
})).range([height, 0]);
y[d].brush = d3.brushY()
  .extent([[-8, y[d].range()[1]], [8, y[d].range()[0]]])
  .on('brush', brush);

Subsequently, give above as the brush callback when adding the brush group:随后,将上面作为添加画笔组时的画笔回调给出:

g.append('g')
  .attr('class', 'brush')
  .each(function(d) {
    d3.select(this).call(y[d].brush);
  })
  .selectAll('rect')
  .attr('x', -8)
  .attr('width', 16);

Finally, change the brush handler to be:最后,将画笔处理程序更改为:

function brush() {
  const actives = [];
  // filter brushed extents
  svg.selectAll('.brush')
    .filter(function(d): any {
      return d3.brushSelection(this as any);
    })
    .each(function(d) {
      actives.push({
        dimension: d,
        extent: d3.brushSelection(this as any)
      });
    });
  // set un-brushed foreground line disappear
  foreground.style('display', function(d) {
    return actives.every(function(active) {
      const dim = active.dimension;
      return active.extent[0] <= y[dim](d[dim]) && y[dim](d[dim]) <= active.extent[1];
    }) ? null : 'none';
  });
}

If above is confusing, see this standalone example that helped me with correctly brushing on parallel coordinates with d3 v4 : https://gist.github.com/kotomiDu/d1fd0fe9397db41f5f8ce1bfb92ad20d如果以上令人困惑,请参阅此独立示例,该示例帮助我正确刷刷 d3 v4 的平行坐标: https : //gist.github.com/kotomiDu/d1fd0fe9397db41f5f8ce1bfb92ad20d

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM