javascript - D3 dendrogram straight edges - Stack Overflow

Looking through the documentation on d3.cluster() I did not see anything that the developer can use to

Looking through the documentation on d3.cluster() I did not see anything that the developer can use to set the type of connecting line. It seems that there is only a curved spline kind of connection, but this is not the most conventional styling for dendrograms -- at least for my situation. What I'm after is a vertically oriented, 90 degree angle joined nodes:

Question

Judging by the documentation, there is no direct built-in solution, but is there anything that d3.cluster() can offer to achieve the above result? Or am I better off coding everything from scratch?

Looking through the documentation on d3.cluster() I did not see anything that the developer can use to set the type of connecting line. It seems that there is only a curved spline kind of connection, but this is not the most conventional styling for dendrograms -- at least for my situation. What I'm after is a vertically oriented, 90 degree angle joined nodes:

Question

Judging by the documentation, there is no direct built-in solution, but is there anything that d3.cluster() can offer to achieve the above result? Or am I better off coding everything from scratch?

Share Improve this question asked Apr 24, 2019 at 6:54 Arash HowaidaArash Howaida 2,6174 gold badges23 silver badges56 bronze badges
Add a ment  | 

3 Answers 3

Reset to default 3

This issue has been asked before in "d3js Tree square". However, I do not really consider this a duplicate as that old question was using D3 v3 and, as it turns out, is not easily adapted to v5. Furthermore, you are explicitly asking for a vertical layout.

Nonetheless, the basic approach stays the same: just use a custom path generator. Building upon the old v3 Block this could be done as follows:

svg.selectAll(".link")
  .data(root.links())
  .enter().append("path")
    .attr("d", elbow);   // Appended paths use the custom path generator.

// Custom path generator    
function elbow(d) {
  return "M" + d.source.x + "," + d.source.y + "H" + d.target.x + "V" + d.target.y;
}

With some minor modifications to work with the v5 API Mike Bostock's demo can be rewritten as a Vertical "Elbow" Dendrogram.

Note, that this is the same approach Mike Bostock used in his Tree of Life notebook where he used multiple custom path generators to create radial layouts.

just had a similar need for creating an R-style dendrogram in d3 (→ JS). The dendrogram function (bottom of this answer) produces dendrograms in the following style given the result of a call to ml-hclust

dendrogram(hclust_result, { h: 2.5 })

(Here's an an acpanying notebook discussing hierarchical clustering in JS https://observablehq./@chrispahm/hierarchical-clustering)

function dendrogram(data, options = {}) {
  const {
    width: width = 420,
    height: height = 320,
    hideLabels: hideLabels = false,
    paddingBottom: paddingBottom = hideLabels ? 20 : 80,
    innerHeight = height - paddingBottom,
    innerWidth = width - 10,
    paddingLeft = 30,
    h: cutHeight = undefined,
    yLabel: yLabel = "↑ Height",
    colors: colors = d3.schemeTableau10,
    fontFamily: fontFamily = "Inter, sans-serif",
    linkColor: linkColor = "grey",
    fontSize: fontSize = 10,
    strokeWidth: strokeWidth = 1
  } = options;

  const svg = d3
    .create("svg")
    .attr("width", width)
    .attr("height", height)
    .attr("viewBox", [0, 0, width, innerHeight])
    .attr("style", "max-width: 100%; height: auto; height: intrinsic;");

  var clusterLayout = d3.cluster().size([width - paddingLeft * 2, innerHeight]);

  const root = d3.hierarchy(data);
  const maxHeight = root.data.height;

  const yScaleLinear = d3
    .scaleLinear()
    .domain([0, maxHeight])
    .range([hideLabels ? innerHeight - 35 : innerHeight, 0]);

  const yAxisLinear = d3.axisLeft(yScaleLinear).tickSize(5);

  function transformY(data) {
    const height = hideLabels ? innerHeight - 15 : innerHeight;
    return height - (data.data.height / maxHeight) * height;
  }

  // traverse through first order children and assign colors
  if (cutHeight) {
    let curIndex = -1;
    root.each((child) => {
      if (
        child.data.height <= cutHeight &&
        child.data.height > 0 &&
        child.parent &&
        !child.parent.color
      ) {
        curIndex++;
        child.color = colors[curIndex];
      } else if (child.parent && child.parent.color) {
        child.color = child.parent.color;
      }
    });
  }

  clusterLayout(root);

  // y-axis
  svg
    .append("g")
    .attr("transform", `translate(0, ${hideLabels ? 20 : 0})`)
    .append("g")
    .attr("class", "axis")
    .attr("transform", `translate(${paddingLeft},${hideLabels ? 20 : 0})`)
    .call(yAxisLinear)
    .call((g) => g.select(".domain").remove())
    .call((g) =>
      g
        .append("text")
        .attr("x", -paddingLeft)
        .attr("y", -20)
        .attr("fill", "currentColor")
        .attr("text-anchor", "start")
        .style("font-family", fontFamily)
        .text(yLabel)
    )
    .selectAll(".tick")
    .classed("baseline", (d) => d == 0)
    .style("font-size", `${fontSize}px`)
    .style("font-family", fontFamily);

  // Links
  root.links().forEach((link) => {
    svg
      .append("path")
      .attr("class", "link")
      .attr("stroke", link.source.color || linkColor)
      .attr("stroke-width", `${strokeWidth}px`)
      .attr("fill", "none")
      .attr("transform", `translate(${paddingLeft}, ${hideLabels ? 20 : 0})`)
      .attr("d", elbow(link));
  });

  // Nodes
  root.descendants().forEach((desc) => {
    /*
    svg
      .append("circle")
      .classed("node", true)
      .attr("fill", desc.color)
      .attr("cx", desc.x)
      .attr("cy", transformY(desc))
      .attr("transform", `translate(${paddingLeft})`);
      .attr("r", 4);
    */
    if (desc.data.isLeaf && !hideLabels) {
      svg
        .append("text")
        //.attr("x", desc.x)
        .attr("dx", -5)
        .attr("dy", 3)
        .attr("text-anchor", "end")
        .style("font-size", `${fontSize}px`)
        .style("font-family", fontFamily)
        .attr(
          "transform",
          `translate(${desc.x + paddingLeft},${transformY(desc)}) rotate(270)`
        )
        .text(desc.name || desc.data.index);
    }
  });

  // Custom path generator
  function elbow(d) {
    return (
      "M" +
      d.source.x +
      "," +
      transformY(d.source) +
      "H" +
      d.target.x +
      "V" +
      transformY(d.target)
    );
  }

  return svg.node();
}

What worked for me take "Most basic dendrogram in d3.js" https://d3-graph-gallery./graph/dendrogram_basic.html

adjust curve parameters to smth like

           .attr("d", function(d) {
            return "M" + d.y + "," + d.x
                    + "L" + (d.parent.y + 0) + "," + d.x
                    + " " + d.parent.y + "," + d.parent.x;
                    })

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745422062a4627007.html

相关推荐

  • javascript - D3 dendrogram straight edges - Stack Overflow

    Looking through the documentation on d3.cluster() I did not see anything that the developer can use to

    5小时前
    30

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信