d3js多线散点图缩放

Posted

技术标签:

【中文标题】d3js多线散点图缩放【英文标题】:d3js multi-line scatterplot zoom 【发布时间】:2022-01-05 03:32:00 【问题描述】:

我正在使用 d3 v6 绘制带有缩放功能的多线散点图。我是 d3 的新手,根据不同的示例,我可以让缩放功能适用于图像/点。问题是线条没有缩放。我查看了许多类似的问题,但这些解决方案都不适合我。

我正在使用的代码:

    var margin = 
      top: 50,
      right: 30,
      bottom: 30,
      left: 210,
    ;
    var svg = d3.select("svg"),
      width = 1410 - margin.left - margin.right,
      height = 620 - margin.top - margin.bottom;

    svg
      .append("defs")
      .append("clipPath")
      .attr("id", "clip")
      .append("rect")
      .attr("width", width)
      .attr("height", height);

    d3.csv("CSV_files/NSW_pathway.csv").then(function (data1) 
      var groupData = d3.group(data1, (d) => d.pathway_name);

      var xScale = d3.scaleLinear().domain([0, 1]).range([0, width]);
      var yScale = d3.scaleLinear().domain([0, 1]).range([height, 0]);

      var xAxis = d3.axisBottom(xScale).ticks(0).tickSize(-height);
      var yAxis = d3.axisLeft(yScale).ticks(0).tickSize(-width);

      var gX = svg
        .append("g")
        .attr(
          "transform",
          "translate(" + margin.left + "," + (margin.top + height) + ")"
        )
        .call(xAxis);

      var gY = svg
        .append("g")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")")
        .call(yAxis);

      var focus = svg
        .append("g")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")")
        .attr("class", "line")
        .attr("clip-path", "url(#clip)");

      const color = d3
        .scaleOrdinal()
        .range(["#e41a1c", "#377eb8", "#4daf4a", "#984ea3"]);

      var points_g = svg
        .append("g")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")")
        .attr("clip-path", "url(#clip)")
        .classed("points_g", true);

      var label = svg
        .append("g")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")")
        .attr("class", "label")
        .attr("clip-path", "url(#clip)");

      var div = d3
        .select("body")
        .append("div")
        .attr("class", "tooltip")
        .style("opacity", 0);

      const mouseover = function (event, d) 
        div.style("opacity", 1);
      ;

      const mousemove = function (event, d) 
        div
          .html(function (d1) 
            if (d.type != "learner")
              return `The resource name is $d.resource_name`;
            else return `This is $d.name`;
          )
          .style("position", "absolute")
          .style("left", event.pageX + 15 + "px")
          .style("top", event.pageY + 15 + "px");
      ;

      const mouseleave = function (event, d) 
        div.transition().duration(200).style("opacity", 0);
      ;

      var points = points_g.selectAll("point").data(data1);

      points = points
        .enter()
        .append("image")
        .attr("xlink:href", function (d) 
          if (d.type == "video") return "Images/3.jpg";
          else if (d.type == "pdf") return "Images/4.png";
          else if (d.type == "none") return "Images/5.png";
        )
        .attr("x", function (d) 
          return xScale(+d.x) - 10;
        )
        .attr("y", function (d) 
          return yScale(+d.y) - 10;
        )
        .attr("width", 20)
        .attr("height", 20)
        .on("mouseover", mouseover)
        .on("mousemove", mousemove)
        .on("mouseleave", mouseleave);

      label
        .selectAll(".text")
        .data(data1)
        .enter()
        .append("text")
        .text(function (d) 
          return d.topic;
        )
        .attr("x", function (d) 
          return xScale(+d.x) + 10;
        )
        .attr("y", function (d) 
          return yScale(+d.y) + 10;
        );

      focus
        .selectAll("line")
        .data(groupData)
        .enter()
        .append("path")
        .attr("fill", "none")
        .attr("stroke", function (d) 
          return color(d[0]);
        )
        .attr("stroke-width", 1)
        .attr("d", function (d) 
          return d3
            .line()
            .curve(d3.curveMonotoneX)
            .x(function (d) 
              return xScale(+d.x);
            )
            .y(function (d) 
              return yScale(+d.y);
            )(d[1]);
        );

      var zoom = d3
        .zoom()
        .scaleExtent([0.5, 20])
        .extent([
          [0, 0],
          [width, height],
        ])
        .on("zoom", zoomed);

      svg
        .append("rect")
        .attr("width", width)
        .attr("height", height)
        .style("fill", "none")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")")
        .lower();

      svg.call(zoom).call(zoom.transform, d3.zoomIdentity);

      function zoomed( transform ) 
        var new_xScale = transform.rescaleX(xScale);
        var new_yScale = transform.rescaleY(yScale);

        gX.call(xAxis.scale(new_xScale));
        gY.call(yAxis.scale(new_yScale));

        points
          .data(data1)
          .attr("x", function (d) 
            return new_xScale(d.x) - 10;
          )
          .attr("y", function (d) 
            return new_yScale(d.y) - 10;
          );

        label
          .selectAll("text")
          .data(data1)
          .attr("x", function (d) 
            return new_xScale(d.x) + 15;
          )
          .attr("y", function (d) 
            return new_yScale(d.y) + 15;
          );

        focus.selectAll("line").attr("d", function (d) 
          return d3
            .line()
            .curve(d3.curveMonotoneX)
            .x(function (d) 
              return xScale(+d.x);
            )
            .y(function (d) 
              return yScale(+d.y);
            )(d[1]);
        );
      
    );

csv 文件示例:

x,y,name,type,topic,resource_name,pathway_name
0,0,start,none,Sponsored Search Markets,Networks Crowd and Markets_NCMch15.pdf,pathwayOne
0,0,start,none,Sponsored Search Markets,Networks Crowd and Markets_NCMch15.pdf,pathwayTwo
0.086511627906977,0.16,horse,pdf,Graphs,Networks Crowd and Markets_NCMch2.pdf,pathwayOne
0.12,0.283768436578171,choice,pdf,Network Centrality,Notes_CGT BASED network CENTRALITY - L2.pdf,pathwayTwo
0.32,0.27217943628424,plex,video,Network Models,Network Analysis_LNch13.pdf,pathwayOne
0.775398773006135,0.33,social,pdf,Clustering,Network Analysis_LNch8.pdf,pathwayTwo
1,1,end,none,Allocation in Networks,Notes_Allocation in networks with DON-L3.pdf,pathwayOne
1,1,end,none,Allocation in Networks,Notes_Allocation in networks with DON-L3.pdf,pathwayTwo

感谢您的帮助。

【问题讨论】:

【参考方案1】:

它不是缩放整个页面,而是缩放整个 svg,您的大边距超出了图表区域。一种解决方案是添加 g 元素,而不是在您的 svg 上,而仅在您的图表区域上。

但是使用您的代码,有两件事会阻止您的线条缩放。

1:您的选择是空的 - 行是返回路径的 d3 抽象

function zoomed() 
   ...
   // empty selection
   console.log(focus.selectAll('line')) 
   // try instead 
   console.log(focus.selectAll('path'))

2:简单的错误 - 您使用的是旧秤而不是新秤

function zoomed() 
    ... 
    focus.selectAll('path').attr('d', d => 
        return d3.line()
            // using old scale
            .x(di => xScale(+di.x))
            // change to 
            .x(di => new_xScale(+di.x))
    )

【讨论】:

非常感谢,我没有意识到选择。【参考方案2】:

我没有您的 csv 文件的样本,因此未对此进行测试,但如果您想放大整个图表,只需在您的 svg 和 transform 之后添加父级 g ..

...
svg
  .append("defs")
  .append("clipPath")
  .attr("id", "clip")
  .append("rect")
  .attr("width", width)
  .attr("height", height);
  
   // NEW - add g 
   .append('g')

// NEW - adjust scaleExtent to your needs
const zoom = d3.zoom()
      .scaleExtent([1, 8])
      .on('zoom', updateChart)
svg.call(zoom)

function updateChart(event) 
    svg.attr('transform', event.transform)

请注意,这也会添加平移,但如果您只想缩放,可以使用:

let scale = 1 
...
function updateChart(event) 
    if(event.transform.k === scale)  return 
    svg.attr('transform', event.transform)
    scale = event.transform.k

【讨论】:

感谢您的回复。我添加了一些示例 csv 数据,添加父“g”不是缩放图表,而是缩放整个页面本身。

以上是关于d3js多线散点图缩放的主要内容,如果未能解决你的问题,请参考以下文章

matplotlib 中带有散点图和使用 set_offsets 的动画:图形的自动缩放不起作用

ggplot2 散点图标签

matplotlib:通过用于为散点图着色的对数颜色条值对 2D 线进行着色

还在用Excel画散点图?不试试极坐标散点图?

100天精通Python(可视化篇)——第82天:matplotlib绘制不同种类炫酷散点图参数说明+代码实战(二维散点图三维散点图散点图矩阵)

echarts散点图