d3.js 从chorddiag交互图输出基因列表

czq61nw1  于 11个月前  发布在  其他
关注(0)|答案(1)|浏览(126)

我们目前正在实验室中探索chorddiag,以更好地显示大型蛋白质组学和CRISPR屏幕数据集。图表的初始显示似乎很好。然而,我们有一个基本的问题:chorddiag中是否有任何内置功能,允许与图表交互的用户单击给定的chord并输出结果比较的列表或CSV?也就是说,如果单击连接数据集A和数据集B的弦,它将给出给予重叠值的输出?我们已经浏览了chorddiag文档,但还没有看到任何明显的东西。如果它是我们必须自己构建的东西,那很好-我们只是想在意外地重新发明轮子之前确定一下。
干杯!干杯!
下面是这个图。我们感兴趣的是识别某种方法,通过这种方法,我们可以单击突出显示的弦,并查看该弦表示的重叠项的精确列表(即数据集A和数据集B之间的重叠项)。
x1c 0d1x的数据

xfb7svmp

xfb7svmp1#

你可以为每个和弦添加一个click处理程序(又名“ribbon”):

const matrixToCsv = (matrix, delimiter = ',') =>
  matrix.map(row => row.join(delimiter)).join('\n');

.on("click", function(d) {
  const { source, target } = d;
  const csv = matrixToCsv([
   'source,target,value'.split(','),
    [office[source.index], office[target.index], target.value],  
    [office[target.index], office[source.index], source.value]])
  console.log(csv);
});

字符串
点击和弦后,源/目标/值的CSV数据将打印到控制台。

完整示例

下面是一个修改过的Chord图,我在组间的chord(也叫“ribbon”)中添加了click处理器(如上图所示)。

// Source: https://stackoverflow.com/a/43778409/1762224
function fade(opacity) {
  return function(d, i) {
    ribbons
      .filter(function(d) {
        return d.source.index != i && d.target.index != i;
      })
      .transition()
      .style("opacity", opacity);
  };
}
/********************************/
var matrix = [
  [11975,  5871, 8916, 2868],
  [ 1951, 10048, 2060, 6171],
  [ 8010, 16145, 8090, 8045],
  [ 1013,   990,  940, 6907]
];

var office = ["A", "B", "C", "D"]

//Initialize canvas and inner/outer radii of the chords
var svg = d3.select("svg"),
  width = +svg.attr("width"),
  height = +svg.attr("height"),
  outerRadius = Math.min(width, height) * 0.5 - 65,
  innerRadius = outerRadius - 20;

//Set number format to show $M for millions with thousands separator (i.e. $1,000M). 
var formatValue = d3.formatPrefix("$,.0", 1e3);

// Initialize chord diagram
var chord = d3.chord()
  .padAngle(0.05)
  .sortSubgroups(d3.descending);

// Set Arc Raddii
var arc = d3.arc()
  .innerRadius(innerRadius)
  .outerRadius(outerRadius);

// Set Ribbbons
var ribbon = d3.ribbon()
  .radius(innerRadius);

// Initialize colors to an ordinal scheme with 10 categories
var color = d3.scaleOrdinal(d3.schemeCategory10);

// Center origin
var g = svg.append("g")
  .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")")
  .datum(chord(matrix));

// Defines each "group" in the chord diagram
var group = g.append("g")
  .attr("class", "groups")
  .selectAll("g")
  .data(function(chords) {
    return chords.groups;
  })
  .enter().append("g")

// Draw the radial arcs for each group
group.append("path")
  .style("fill", function(d) {
    return color(d.index);
  })
  .style("stroke", function(d) {
    return d3.rgb(color(d.index)).darker();
  })
  .attr("d", arc)
  .on("mouseover", fade(.1))
  .on("mouseout", fade(1))

group.append("title").text(function(d) {
  return groupTip(d);
});

// Add labels to each group
group.append("text")
  .attr("dy", ".35em") // Width
  .attr("class", "office-label")
  .attr("transform", function(d, i) { // Angle
    d.angle = (d.startAngle + d.endAngle) / 2; // Calculate the average of the start angle and the end angle
    d.name = office[i]; // Assignment for the city
    return "rotate(" + (d.angle * 180 / Math.PI) + ")" +
      "translate(0," + -1.1 * (outerRadius + 30) + ")" +
      ((d.angle > Math.PI * 3 / 4 && d.angle < Math.PI * 5 / 4) ? "rotate(180)" : "");
  }) // To spin when the angle between 135 to 225 degrees
  .text(function(d) {
    return d.name;
  });

const matrixToCsv = (matrix, delimiter = ',') =>
  matrix.map(row => row.join(delimiter)).join('\n');

// Draw the ribbons that go from group to group
var ribbons = g.append("g")
  .attr("class", "ribbons")
  .selectAll("path")
  .data(function(chords) {
    return chords;
  })
  .enter().append("path")
  .attr("d", ribbon)
  .style("fill", function(d) {
    return color(d.target.index);
  })
  .style("stroke", function(d) {
    return d3.rgb(color(d.target.index)).darker();
  })
  .on("click", function(d) {
    const { source, target } = d;
    const csv = matrixToCsv([
      'source,target,value'.split(','),
      [office[source.index], office[target.index], target.value],
      [office[target.index], office[source.index], source.value]
    ])
    console.log(csv);
  });

ribbons.append("title").
text(function(d) {
  return chordTip(d);
});

// Define tick marks to run along each arc
var groupTick = group.selectAll(".group-tick")
  .data(function(d) {
    return groupTicks(d, 1e3);
  })
  .enter().append("g")
  .attr("class", "group-tick")
  .attr("transform", function(d) {
    return "rotate(" + (d.angle * 180 / Math.PI - 90) + ") translate(" + outerRadius + ",0)";
  });

groupTick.append("line")
  .attr("x2", 6);

groupTick
  .filter(function(d) {
    return d.value % 2e3 === 0;
  })
  .append("text")
  .attr("x", 8)
  .attr("dy", ".35em")
  .attr("transform", function(d) {
    return d.angle > Math.PI ? "rotate(180) translate(-16)" : null;
  })
  .style("text-anchor", function(d) {
    return d.angle > Math.PI ? "end" : null;
  })
  .text(function(d) {
    return formatValue(d.value);
  });

// Returns an array of tick angles and values for a given group and step.
function groupTicks(d, step) {
  var k = (d.endAngle - d.startAngle) / d.value;
  return d3.range(0, d.value, step).map(function(value) {
    return {
      value: value,
      angle: value * k + d.startAngle
    };
  });
}

function chordTip(d) {
  var p = d3.format(".2%"),
    q = d3.formatPrefix("$,.2", 1e3)
  return "Flow Info:\n" +
    office[d.source.index] + " → " + office[d.target.index] + ": " + q(d.target.value) + "\n" +
    office[d.target.index] + " → " + office[d.source.index] + ": " + q(d.source.value);
}

function groupTip(d) {
  var q = d3.formatPrefix("$,.2", 1e3)
  return "Total Managed by " + office[d.index] + ":\n" + q(d.value)
}
body {
  font: 10px sans-serif;
}

.group-tick line {
  stroke: #000;
}

.office-label {
  font-size: 25px;
  font: sans-serif;
  text-anchor: middle;
}

.ribbons {
  fill-opacity: 0.6;
}
<script src="https://d3js.org/d3.v4.js"></script>
<svg width="450" height="450"></svg>

相关问题