【问题标题】:Conditionally change opacity of all nodes and edges recursively (d3)有条件地递归更改所有节点和边的不透明度(d3)
【发布时间】:2017-01-30 11:00:37
【问题描述】:

更新:I have created a JSFiddle here。请发布更新的小提琴与您的答案。

我有动态过滤器,用户可以将其应用于数据,但它们会更改节点的不透明度以指示过滤入和出的内容(过滤后的“out”元素仍然部分可见,实际的 d3 filter() 函数是不使用(故意))。我还在每个被过滤掉的节点上设置了一个属性(例如node = {"name": "test", "isFilteredOut": true};)。 所以对于这个问题,即使我使用“过滤器”这个词,它实际上只是一个有条件的样式更改(我会尝试把这个词在这篇文章中的引号中“过滤”以提醒这一点)。

这一切都很好,但现在我想递归地“过滤”掉所有子节点和“过滤掉”节点的边,以及将初始“过滤掉”节点连接到其未过滤的边 -出父节点。

我能找到的所有示例都以单击事件开头,因此可以使用this 来获取所选初始节点的数据。我没有这种奢侈,因为过滤器是使用图形本身之外的 UI 元素应用的。

我目前像这样“过滤”节点:

node.style("opacity", function(n) {
    if (my_filter_conditions) {
        return 1;
    } else {
        n.isFilteredOut = true;
        return 0.1;
    }
});

我基本上需要做的是:

  1. 递归地选择当前“过滤掉”节点的所有子节点并“过滤”掉这些节点(即将它们的不透明度更改为 0.1 并设置n.isFilteredOut = true;)。

  2. 将所有边缘的不透明度更改为 0.1,其中源节点目标节点被“过滤掉”(即边缘任一端的n.isFilteredOut = true;

我尝试了什么

我不知道如何访问源节点和目标节点的数据,只给定每个边缘的索引(请记住,我没有从点击事件开始的 this 节点)。我尝试通过从边缘获得的节点索引来获取节点数据:

var node_data = d3.select(current_edge.source.index).datum();

但是,这导致 d3 库中与 this.node() 为空相关的错误(因此在此处传递索引不起作用)。

我还尝试通过将用于处理链接的函数嵌套在传递给 node.style() 函数的函数内来处理边缘,但随后它尝试处理 every 上的 所有 边缘节点,我无法让它提供所需的结果。

link.style("opacity", function (e) {
    return ( (n.isFilteredOut)
            && (n.index==e.source.index | n.index==e.target.index) ) ? 0.1 : 1;
});

这是我尝试在“过滤掉”节点的两侧“过滤掉”边缘,但是当我出于某种原因使用它时,没有任何边缘被过滤掉(看起来好像什么也没发生) .

更新:I have created a JSFiddle here

小提琴注意事项:

  • 我知道它很简单(它应该是一个最低限度的工作示例)
  • 实际应用程序包含适用于类型的过滤器(即使只是搜索特定设备/部件/等),因此逻辑能够有条件地遵循“链”仅适用于那些具有node.isFilteredOut = true;
  • 在此示例中,正确答案将导致创建过滤掉设备也会过滤掉所有部分的情况
  • 使用dataSet 本身进行任何过滤的解决方案将不起作用,因为我的大部分数据都是从各种 JSON 源动态填充的。随时与nodesedgeslinksnode 和/或link 合作。
  • 请不要重写我的过滤方法。是的,我知道eval() 声明不是很好。但这不是关于如何最好地应用无限联合过滤器的问题,而是关于基于应用的过滤器递归地改变节点和边的不透明度的问题

【问题讨论】:

  • 不费吹灰之力,很难。可以加个小提琴吗?
  • 我添加了一个小提琴:更新:I have created a JSFiddle here
  • @Dragon_Slayer 仅供参考:JSFiddle here
  • 澄清一下,在我最简单的小提琴中,过滤掉设备也应该导致所有部分(和链接/边缘)也被过滤掉。
  • 棘手的情况。这两个答案实际上都没有被证明可以工作(发布小提琴的那个实际上破坏了现有的功能并且没有做需要做的事情,并且作者承认新发布的一个不能与 D3 一起使用)。然而,不奖励任何人意味着积分被浪费了。精...

标签: javascript dictionary recursion d3.js filtering


【解决方案1】:

这是一种可能的方法,它同时实现递归过滤(如果过滤设备,则过滤其部分)和基于节点过滤的链接过滤:http://jsfiddle.net/Lsr9c8nL/4/

我更改了您实现过滤器的方式。使用字符串来构建过滤器,然后 eval() 现在被认为是非常糟糕的,因为工具不能用 eval() 做很多事情,例如检测错误或优化浏览器上的 JS 代码。

我直接在dataSet 上进行过滤,而不是在节点上(您必须查询节点的type 并比较字符串,这很慢)。直接在数据集上这样做还可以轻松找到给定部件的设备。

诀窍基本上是每次都重新绘制整个图表,并小心使用d3的exitenterupdate选择。如果您愿意,这也将允许您添加动画

【讨论】:

  • 更新后的版本不再过滤节点及其标签,只过滤链接。此外,我的实际代码有许多过滤器,并且合取 AND 逻辑非常重要(它并不总是简单的“关闭所有这种类型”,类型中有过滤器)。它们也不都是简单的开/关过滤器。这“取消”了一些现有的功能。另外,我对这种方法的恐惧是时间。最初“绘制”我的数据集大约需要 5-10 分钟。使用我现有的方法,过滤器是即时的。应用过滤器需要 5-10 分钟。
  • 有什么方法可以保留我现有的过滤方法并直接对节点和链接/边缘进行操作以获得相同的结果?这将变得过于复杂,因为有许多跨类型和在类型内的过滤器,并且重绘将对性能造成巨大影响,因为我实际上是从多个 JSON 源中提取并动态填充许多字段。
  • 似乎您应该首先从优化初始显示开始。没有用户会等待 10 分钟让计算显示图表。我的猜测是,您正在执行的所有字符串操作和 DOM 访问都在减慢速度。似乎您还应该将数据拆分为更小的数据束,以便更有效地计算布局。
  • 是的,有一些方法可以使用您现有的方法,但是现在不推荐使用eval,我绝对建议您考虑其他方法。它速度慢,容易出错,没有工具可以检查或优化您的代码。
  • 这是一个小的用户群,它正在加载大量动态生成的信息(一些速度问题不是图表的错)。
【解决方案2】:

我知道你对在这里使用 eval 感觉很强烈,但你可以重构你的过滤函数来返回一个 lambda:

function filter_devices() {
    return n=>n.type != "mobile device";
}

然后做这样的事情:

function isNTrueForAll(arrayOFuncts,n){
return arrayOFuncts.every(i=>i(n))

}

或者使用reduce,如果你没有Array.every:

function isNTrueForAll(arrayOFuncts,n){
return arrayOFuncts.reduce((current,next)=>{return current && next(n)},true)

}

或者,如果这更适合您,请创建一个递归迭代器。 关键是,条件布尔求值和延迟执行不是您需要 eval 的东西,而且您在运行 eval 时会受到很大的影响。

-#编辑-

这里的 DOM 似乎实际上并未反映您的设备和部件节点之间的关系,因此您不能使用 D3 选择对象将特定部件节点与其父设备节点相关联。

因此,递归地选择孩子是一个有争议的问题。

或者,您可以调整过滤器函数以反映关系 - IE,如果我们按部分过滤,则为设备或部分返回 false。

只有在没有那么多过滤条件并且您已经知道要提前使用的业务逻辑来过滤它们的情况下,这才是真正可持续的解决方案,但它可能对您有用。

【讨论】:

  • 几个问题:1)你能更新小提琴以展示它的实际效果(并证明它有效)吗? 2)如果我的一些过滤器需要 50 多行代码才能工作(它们比我的小提琴中的更复杂),使用 lambda 函数是否仍然有效?我对 Python 中的 lambdas 的理解是它们必须简短且在一行中。
  • 1) 好吧,如果节点之间的 DOM 关系反映了继承,我有一个可以工作的,但是,这一切都是平的,所以,这对我来说是一个糟糕的假设 - D3“选择”不适用于您的示例。 2) JS 中的 lambda 实际上没有任何上限。它将“this”上下文更改为调用父级的“this”上下文,否则,它基本上只是函数 def 的语法糖。它们可以是任意长度或复杂度。
  • python 中的 lambda 必须是短的,但在 javascript 中没有这样的限制。通过返回其他函数并将它们组合起来的函数来构建过滤器可能是一个相当不错的主意。会更快、更安全、更容易被js引擎优化,然后使用eval()
  • 如果我能在小提琴中看到一个例子,那将非常有帮助。对我来说,问题是这些功能并非每次都存在。只有选定的过滤器处于 if 条件中。那么如何在 if 条件中动态包含函数呢?
  • 我无法制作出有效的小提琴,因为我无法从 D3 选择对象将设备节点与其部分节点相关联,因为 DOM 没有说明这种关系。如果你只是想要一些说明使用 lambda 和动态评估的东西,但仍然有同样的问题,这里有一个例子:jsfiddle.net/mbkevb7b/16
猜你喜欢
  • 1970-01-01
  • 2019-01-21
  • 1970-01-01
  • 2016-12-24
  • 2021-09-07
  • 1970-01-01
  • 2021-10-17
  • 2012-07-23
  • 2013-03-04
相关资源
最近更新 更多