d3、jsの力指向グラフにおけるノードの「ハード」フィルタリング

ウォーレンブルーメンオー

(多数の)ノードとそれらの間の多くのリンクを含む力指向グラフがあります。ノードのサブセット(およびノー​​ド間のリンク)のみが残るように、インタラクティブにフィルターを適用したいと思います。ただし、グラフが大きいため、ノードを非表示にするのではなく、フィルターで除外されたノードをシミュレーションから削除します(結果のグラフのパフォーマンスが向上するようにします)。したがって、新しい配列としてノードのフィルタリングされたリストを作成し、これらのノードのみでシミュレーションを再初期化することを検討しています。同じことが、エッジに適用されます-私はこれをまだ行っていないが、私はおそらく前にフィルタリングの同じ種類を維持して行うためにエッジを決定プログラム的に必要となる新しいノードを持つグラフを再初期化し、エッジ。元のノード/エッジ配列を保持して、「リセット」を開始状態に戻せるようにしたい。

この時点でハードコードされたフィルタリングを実行している簡単な例をまとめましたが、フィルタリングされた配列を使用してシミュレーションを再初期化するのに苦労しています。フィルタリングされたノードをシミュレーションから削除しているようです(円「3」はドラッグできなくなります)が、レンダリングされたグラフには引き続き表示されます。

「リセット」ロジックでの私の試みは、これまでのところうまくいくようです。

私は何が間違っているのですか?これを達成するためのより良い方法はありますか?(d3.js v3)

私のサンプルコードは次のとおりです。

var links = [{
    source: 0,
    target: 1,
    type: "c"
  },
  {
    source: 1,
    target: 2,
    type: "d"
  },
  {
    source: 2,
    target: 0,
    type: "d"
  }
];

var nodes = [{
    name: "one",
    type: "a"
  },
  {
    name: "two",
    type: "a"
  },
  {
    name: "three",
    type: "b"
  }
];

var width = 300;
var height = 300;

var force = d3.layout.force()
  .nodes(nodes)
  .links(links)
  .size([width, height])
  .linkDistance(200)
  .charge(-400)
  .on("tick", tick)
  .start();

var svg = d3.select("#graph").append("svg")
  .attr("width", width)
  .attr("height", height);

function colours(n) {
  var colours = ["#3366cc", "#dc3912", "#ff9900", "#109618", "#990099", "#0099c6", "#dd4477", "#66aa00", "#b82e2e", "#316395",
    "#994499", "#22aa99", "#aaaa11", "#6633cc", "#e67300", "#8b0707", "#651067", "#329262", "#5574a6", "#3b3eac"
  ];
  return colours[n % colours.length];
}

var path = svg.append("g").selectAll("path")
  .data(force.links())
  .enter().append("line")
  .attr('class', 'link')
  .attr('stroke', function(d, i) {
    return colours(i);
  })

var circles = svg.append("g");
var circle = circles.selectAll("circle")
  .data(force.nodes())
  .enter().append("circle")
  .attr("r", 8)
  .attr('class', 'circle')
  .attr('fill', function(d, i) {
    return colours(i + 3);
  })
  .call(force.drag);

var text = svg.append("g").selectAll("text")
  .data(force.nodes())
  .enter().append("text")
  .attr("x", 14)
  .attr("y", ".31em")
  .text(function(d) {
    return d.name;
  });

function tick() {
  path.attr({
    x1: function(d) {
      return d.source.x;
    },
    y1: function(d) {
      return d.source.y;
    },
    x2: function(d) {
      return d.target.x;
    },
    y2: function(d) {
      return d.target.y;
    }
  });
  circle.attr("transform", transform);
  text.attr("transform", transform);
}

function transform(d) {
  return "translate(" + d.x + "," + d.y + ")";
}

var nodeText = "";

function nodeTypeA(node) {
  return (node.type == "a");
}

function linkTypeC(link) {
  return (link.type == "c");
}

function applyFilter() {
  force.nodes(nodes.filter(nodeTypeA));
  force.links(links.filter(linkTypeC));
  circle.data(force.nodes());
  text.data(force.nodes());
  path.data(force.links());
  d3.selectAll("circle").each(
    function(d) {
      console.log(d.name);
    }
  );
  console.log("");
}

function resetFilter() {
  force.nodes(nodes);
  force.links(links);
  circle.data(force.nodes());
  text.data(force.nodes());
  path.data(force.links());
  d3.selectAll("circle").each(
    function(d) {
      console.log(d.name);
    }
  )
  console.log("");
}
#buttons {
  position: absolute;
  top: 10px;
  left: 20px;
  height: 100px;
  width: 400px;
  z-index: 99;
}

#graph {
  position: absolute;
  top: 50px;
  left: 20px;
  height: 300px;
  width: 300px;
  z-index: 98;
}
<script src="https://d3js.org/d3.v3.min.js"></script>
<!DOCTYPE html>
<meta charset="utf-8">
<body>
  <div id="root">
    <div id="buttons">
      <button id="filter" onclick="applyFilter()">Apply</button>
      <button id="reset" onclick="resetFilter()">Reset</button>
    </div>
    <div id="graph">
    </div>
  </div>
</body>
<div id="node_details">
</div>
</body>

アンドリューリード

フィルタおよびリセット機能では、選択範囲のデータを更新しますが、新しい要素を追加/削除するために選択範囲の入力または終了を使用しないでください。

最初に要素を追加するときは、Enter選択を使用します。例:

var path = svg.append("g").selectAll("path")
 .data(force.links())
 .enter().append("line")
 ...

ただし、更新するときは、次を使用するだけです。

path.data(force.links());

最初にノードを追加したときと同じように、追加する内容と追加方法を指定する必要があります。

ノードを終了するのは非常に簡単.exit().remove()です。フィルターを適用するときに上記の行に追加するだけです。.exit()データ配列に対応する項目がなくなった選択範囲の要素を選択します。.remove()それらをDOMから削除するだけです。

var links = [
    {source: 0, target: 1, type: "c"},
    {source: 1, target: 2, type: "d"},
    {source: 2, target: 0, type: "d"}
];

var nodes = [
    {name: "one", type: "a"},
    {name: "two", type: "a"},
    {name: "three", type: "b"}
];


var width = 300;
var height = 300;

var force = d3.layout.force()
    .nodes(nodes)
    .links(links)
    .size([width, height])
    .linkDistance(200)
    .charge(-400)
    .on("tick", tick)
    .start();

var svg = d3.select("#graph").append("svg")
  .attr("width", width)
  .attr("height", height);

function colours(n) {
  var colours = ["#3366cc", "#dc3912", "#ff9900", "#109618", "#990099", "#0099c6", "#dd4477", "#66aa00", "#b82e2e", "#316395",
                 "#994499", "#22aa99", "#aaaa11", "#6633cc", "#e67300", "#8b0707", "#651067", "#329262", "#5574a6", "#3b3eac"];
  return colours[n % colours.length];
}

var path = svg.append("g").selectAll("path")
  .data(force.links())
  .enter().append("line")
  .attr('class', 'link')
  .attr('stroke', function(d, i) {
    return colours(i);
  })

var circles = svg.append("g");
var circle = circles.selectAll("circle")
  .data(force.nodes())
  .enter().append("circle")
  .attr("r", 8)
  .attr('class', 'circle')
  .attr('fill', function(d, i) {
    return colours(i + 3);
  })
  .call(force.drag);

var text = svg.append("g").selectAll("text")
  .data(force.nodes())
  .enter().append("text")
  .attr("x", 14)
  .attr("y", ".31em")
  .text(function(d) {
    return d.name;
  });

function tick() {
  path.attr({
    x1: function(d) {
      return d.source.x;
    },
    y1: function(d) {
      return d.source.y;
    },
    x2: function(d) {
      return d.target.x;
    },
    y2: function(d) {
      return d.target.y;
    }
  });
  circle.attr("transform", transform);
  text.attr("transform", transform);
}

function transform(d) {
  return "translate(" + d.x + "," + d.y + ")";
}

var nodeText = "";

function nodeTypeA(node) {
    return (node.type == "a");
}

function linkTypeC(link) {
    return (link.type == "c");
}

function applyFilter() {
    force.nodes(nodes.filter(nodeTypeA));
    force.links(links.filter(linkTypeC));
    
    circle.data(force.nodes()).exit().remove();
    text.data(force.nodes()).exit().remove();
    path.data(force.links()).exit().remove();
    
    d3.selectAll("circle").each(
        function(d) {
            console.log(d.name);
        }
    );
    console.log("");
}

function resetFilter() {
    force.nodes(nodes);
    force.links(links);
    circle.data(force.nodes());
    text.data(force.nodes());
    path.data(force.links());
    d3.selectAll("circle").each(
        function(d) {
            console.log(d.name);
        }
    )
    console.log("");
}
#buttons {
    position: absolute;
    top: 10px;
    left: 20px;
    height: 100px;
    width: 400px;
    z-index: 99;
}

#graph {
    position: absolute;
    top: 50px;
    left: 20px;
    height: 300px;
    width: 300px;
    z-index: 98;
}
<div id="root">
    <div id="buttons">
      <button id="filter"  onclick="applyFilter()">Apply</button>
      <button id="reset" onclick="resetFilter()">Reset</button>
    </div>
    <div id="graph">
    </div>
</div>
</body>
<script src="https://d3js.org/d3.v3.min.js"></script>

リセット関数の最初の入力に使用するコードを複製して要素を入力することもできますが(いくつかの小さな変更を加えて)、これは少し反復的です。同じことを行うコードの2つのセクションがあります。

代わりに、入力と終了を更新関数に入れましょう。更新機能は、フォースレイアウトからノードとリンクを取得し、必要に応じて出入りします。

var links = [
    {source: 0, target: 1, type: "c"},
    {source: 1, target: 2, type: "d"},
    {source: 2, target: 0, type: "d"}
];

var nodes = [
    {name: "one", type: "a"},
    {name: "two", type: "a"},
    {name: "three", type: "b"}
];


var width = 300;
var height = 300;

var force = d3.layout.force()
    .nodes(nodes)
    .links(links)
    .size([width, height])
    .linkDistance(200)
    .charge(-400)
    .on("tick", tick)
    .start();

var svg = d3.select("#graph").append("svg")
  .attr("width", width)
  .attr("height", height);

function colours(n) {
  var colours = ["#3366cc", "#dc3912", "#ff9900", "#109618", "#990099", "#0099c6", "#dd4477", "#66aa00", "#b82e2e", "#316395",
                 "#994499", "#22aa99", "#aaaa11", "#6633cc", "#e67300", "#8b0707", "#651067", "#329262", "#5574a6", "#3b3eac"];
  return colours[n % colours.length];
}



var paths = svg.append("g");
var circles = svg.append("g");
var texts = svg.append("g");

update();

function tick() {
  paths.selectAll("line").attr({
    x1: function(d) {
      return d.source.x;
    },
    y1: function(d) {
      return d.source.y;
    },
    x2: function(d) {
      return d.target.x;
    },
    y2: function(d) {
      return d.target.y;
    }
  });
  circles.selectAll("circle").attr("transform", transform);
  texts.selectAll("text").attr("transform", transform);
}

function transform(d) {
  return "translate(" + d.x + "," + d.y + ")";
}

var nodeText = "";

function nodeTypeA(node) {
    return (node.type == "a");
}

function linkTypeC(link) {
    return (link.type == "c");
}

function applyFilter() {
    force.nodes(nodes.filter(nodeTypeA));
    force.links(links.filter(linkTypeC));
	
	update();
}
function resetFilter() {
    force.nodes(nodes);
    force.links(links);
	
	update();
	force.start(); // start the force layout again.
}

function update() {   

  // update the data for the lines:
  var path = paths.selectAll("line")
    .data(force.links());
  
  // enter new lines:
  path.enter().append("line")
    .attr('class', 'link')
    .attr('stroke', function(d, i) {
      return colours(i);
  })
  
  // exit unneeded lines:
  path.exit().transition().style("opacity",0).remove();

  // update the data for the circles:
  var circle = circles.selectAll("circle")
    .data(force.nodes());
  
  // enter new circles:
  circle.enter().append("circle")
    .attr("r", 8)
    .attr('class', 'circle')
    .attr('fill', function(d, i) {
      return colours(i + 3);
    })
    .call(force.drag);
  
  // remove unneeded circles:
  circle.exit().transition().style("opacity",0).remove();

  // update the text data:
  var text = texts.selectAll("text")
    .data(force.nodes());
  
  // enter new text
  text.enter().append("text")
    .attr("x", 14)
    .attr("y", ".31em")
    .text(function(d) {
      return d.name;
    });
  
  // exit old text:
  text.exit().transition().style("opacity",0).remove();
}
#buttons {
    position: absolute;
    top: 10px;
    left: 20px;
    height: 100px;
    width: 400px;
    z-index: 99;
}

#graph {
    position: absolute;
    top: 50px;
    left: 20px;
    height: 300px;
    width: 300px;
    z-index: 98;
}
<script src="https://d3js.org/d3.v3.min.js"></script>
<div id="root">
    <div id="buttons">
      <button id="filter"  onclick="applyFilter()">Apply</button>
      <button id="reset" onclick="resetFilter()">Reset</button>
    </div>
    <div id="graph">
    </div>
</div>
</body>



    <div id="node_details">
    </div>

オリジナルからの変更点:フィルター関数とリセット関数は、フォースのノードとリンクを設定した後に更新関数を呼び出します(ノードは最初に更新関数で描画されます)。ノードを追加すると、力がリセットされます(シミュレーションが冷却されたかのように再起動するために、ティックは呼び出されず、ノードは適切に配置されません)。

最後に、テキスト、円、および線は、それぞれ、、およびgという名前の選択に含まれtexts circlesていlinesます。ティック関数はg、ティックごとに各親の子を再選択するように変更されましたが、これは別の方法で最適化できます。

最後の注意として、データの識別子の指定またはデータ内のノード/リンクプロパティの指定を検討する価値があるかもしれません-リンク/ノードの色付けの削除/追加および/またはインデックスによるプロパティの設定が問題になる可能性があります。

この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。

侵害の場合は、連絡してください[email protected]

編集
0

コメントを追加

0

関連記事

分類Dev

D3力指向グラフの「ピン留め」ノード

分類Dev

D3有向グラフのノードを動的にフィルタリングする

分類Dev

D3力指向グラフ:ノード位置の更新

分類Dev

d3力指向グラフノードは、フィルター後も固定位置に留まります

分類Dev

スタイリングノードラベルがd3力指向グラフで機能しない

分類Dev

3d.js-力指向グラフのノードを更新

分類Dev

d3力指向グラフのノードをシングルクリックとダブルクリックの両方

分類Dev

検索後の力指向グラフでのd3.jsノードの位置

分類Dev

cytoscape.jsの特定のノードへの接続によるグラフのフィルタリング

分類Dev

d3.jsにノードを動的に追加する力指向グラフ

分類Dev

d3.jsの力指向グラフ:ノードサイズをリンクの値に依存させる方法は?

分類Dev

D3ノードの色を動的に変更します(力指向グラフ)

分類Dev

d3力指向グラフレイアウトでノードのグループを視覚化する方法

分類Dev

d3力指向ネットワークグラフのラベルでノードとリンクを更新しても、ノードが適切に削除されません

分類Dev

d3の力指向グラフに新しいノードのみを追加するにはどうすればよいですか?

分類Dev

ノード間のリンクを持つグーグルマップ上のd3グラフ

分類Dev

D3力指向グラフ:ドラッグ中にノードがマウスの位置に留まらない

分類Dev

d3の力指向グラフでノードとして使用される場合、divのサイズを動的に変更します

分類Dev

D3v4の力指向グラフ-localStorageはリンクとノードを切断します

分類Dev

D3の力指向グラフ(v4)はノードとしてimgを受け入れていません

分類Dev

d3力レイアウトグラフのノード上にテキストを表示する

分類Dev

d3:入力フィールドのあるブラシ

分類Dev

グラフのD3ノードにカスタムカラーを追加する方法

分類Dev

軸ラベルがデータからのハイパーリンクであるD3棒グラフ

分類Dev

暗号内の特定のノードに関連するノードのフィルタリング

分類Dev

D3グラフでCSVファイルの代わりにハードコードされたデータを使用する

分類Dev

D3.jsでCSVの複数の行をフィルタリングし、キーボード入力のフィルターを更新します

分類Dev

D3グラフのノードの検索機能

分類Dev

力指向グラフのドラッグフリッカー

Related 関連記事

  1. 1

    D3力指向グラフの「ピン留め」ノード

  2. 2

    D3有向グラフのノードを動的にフィルタリングする

  3. 3

    D3力指向グラフ:ノード位置の更新

  4. 4

    d3力指向グラフノードは、フィルター後も固定位置に留まります

  5. 5

    スタイリングノードラベルがd3力指向グラフで機能しない

  6. 6

    3d.js-力指向グラフのノードを更新

  7. 7

    d3力指向グラフのノードをシングルクリックとダブルクリックの両方

  8. 8

    検索後の力指向グラフでのd3.jsノードの位置

  9. 9

    cytoscape.jsの特定のノードへの接続によるグラフのフィルタリング

  10. 10

    d3.jsにノードを動的に追加する力指向グラフ

  11. 11

    d3.jsの力指向グラフ:ノードサイズをリンクの値に依存させる方法は?

  12. 12

    D3ノードの色を動的に変更します(力指向グラフ)

  13. 13

    d3力指向グラフレイアウトでノードのグループを視覚化する方法

  14. 14

    d3力指向ネットワークグラフのラベルでノードとリンクを更新しても、ノードが適切に削除されません

  15. 15

    d3の力指向グラフに新しいノードのみを追加するにはどうすればよいですか?

  16. 16

    ノード間のリンクを持つグーグルマップ上のd3グラフ

  17. 17

    D3力指向グラフ:ドラッグ中にノードがマウスの位置に留まらない

  18. 18

    d3の力指向グラフでノードとして使用される場合、divのサイズを動的に変更します

  19. 19

    D3v4の力指向グラフ-localStorageはリンクとノードを切断します

  20. 20

    D3の力指向グラフ(v4)はノードとしてimgを受け入れていません

  21. 21

    d3力レイアウトグラフのノード上にテキストを表示する

  22. 22

    d3:入力フィールドのあるブラシ

  23. 23

    グラフのD3ノードにカスタムカラーを追加する方法

  24. 24

    軸ラベルがデータからのハイパーリンクであるD3棒グラフ

  25. 25

    暗号内の特定のノードに関連するノードのフィルタリング

  26. 26

    D3グラフでCSVファイルの代わりにハードコードされたデータを使用する

  27. 27

    D3.jsでCSVの複数の行をフィルタリングし、キーボード入力のフィルターを更新します

  28. 28

    D3グラフのノードの検索機能

  29. 29

    力指向グラフのドラッグフリッカー

ホットタグ

アーカイブ