层次化透明度传播
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了层次化透明度传播相关的知识,希望对你有一定的参考价值。
参考技术A https://arxiv.org/pdf/2004.03249.pdfhttps://github.com/Yaoyi-Li/HOP-Matting
http://www.alphamatting.com/eval_25_highres.php
摘要: 近年来,深度网络在抠图领域取得巨大成功。和传统的、基于传播的抠图方法相比,一些顶尖的深度网络倾向于隐式地在神经网络中进行传播。一个全新的、能够更直接地在像素间直接传播alpha matte的结构是被需要的。因此,本文提出一种层次化的透明度传播的抠图方法,透明度信息在不同语义层级的每个点的领域传播。层次化结构是基于一个全局的和多个局部的传播块。使用HOP结构后,高分辨率特征图的每个特征点对会根据图像的样子(appearance)连接起来。针对输入图像的大小变化问题,我们进一步提出一种尺度钝感的位置编码算法,并提出一种用于抠图的随机插值的数据增强技术。大量实验和消冗研究表明,我们的方法比最先进的方法性能更好。
关键词: 抠图,层次化传播,层次化变换
1. 引言
抠图是将前景物体和背景分离,是将图像视为一副合成图像,要估计前景物体的透明度,输出结果是alpha matte,alpha matte表示的是每个像素处前景和背景之间的变换[41]。
公式上,一张RGB图像 被建模为前景 和背景 的凸组合(convex combination)[8,41]:
,
其中, 是位置 处要估计的alpha matte值。抠图的原始定义仍然存在定义不清的问题。因此,在绝大多数抠图任务中,三元图(图1(b))作为输入的粗标注,其表示已知的前景、背景区域和有待预测的不确定区域。
传统的基于传播的抠图算法[24,14,23,4,1]是根据图像外观的相似性在像素之间传播透明度,以生成alpha matte。这种归纳偏置(inductive bias)为一些深度网络[37,3]所用,以带来效果上的提升。SampleNet[37]使用inpainting network来执行传播,该方法是预测前景和背景,而不是预测透明度。此外,inpainting部分的传播只在语义信息强的特征图上进行,而不在高分辨率特征上进行。AdaMatting[3]在传播的最后阶段使用了卷积式的LSTM,其中传播是基于卷积和保存在ConvLSTM的cell中的数据进行的。ConvLSTM和直接传播的区别类似于LSTM和transformer的区别。
本文提出一种新的层次化透明度传播结构,其中,透明度信息可以在不同语义级别的特征间进行传递。该结构有两个传播块,称为HOP块和局部HOP块。HOP块通过像Transformer那样的注意力机制和聚合机制来执行信息的传递。尽管如此,全局HOP块和局部HOP块都被设计为双源transformers。更具体地说,注意力图中节点之间的关系是根据外观特征计算的,传递的信息是透明度特征,这和自注意力以及常规注意力[2,44]不同。因为有HOP结构,我们的网络用全局HOP块在分辨率低、语义信息强的特征上学习上下文透明度,用局部HOP块在高分辨率特征上refine模糊的伪影。图1所示是我们的方法与3种最先进方法在现实世界图像上的效果。除了HOP结构,我们还提出一种尺度钝感的位置编码方法,以除了输入图像尺寸的变化,该方法是基于[9,32]提出的相对位置编码方法。我们还提出了随机插值用来数据增强,以进一步提升性能。
具体地说,我们的方法和以往的深度学习抠图方法有以下不同:
(1). 我们提出了一种全新的层次化传递结果,它是利用一系列的全局和局部HOP块,利用不同语义级别的透敏度和外观信息进行信息传播。
(2). 使用随机插值来做训练过程的数据增强,实验表明这样能带来显著提升。
(3). 在Composition-1k和alphamatting.com数据集上的实验表明我们的方法和最先进方法相比是具有竞争力的。
2. 相关工作
大多数抠图方法可以大致分为基于传播的方法[24,14,23,4,1],基于采样的方法[40,11,13,10]和基于学习的方法[45,30,29,37,18,3]。本节会回顾一些和我们工作高度相关的深度学习方法。
一般的基于深度学习的方法是给定图像和三色图直接预测alpha matte。[7]使用了一个端到端的CNN,输入是用closed-form matting[24]和KNN matting[4]得到的mattes以及归一化的原图,以更好地利用局部和全局的结构信息。DeepMatting[15]是一个两阶段网络来预测alpha matte,第一阶段是编码-解码卷积网络,输入是原图和对应的trimap,第二阶段是用一个浅层的网络来refine第一阶段预测的粗alpha matte。[30]使用GAN来抠图,并使用扩张卷积来捕获全局上下文信息。[36]提出一个叫做VDRN的深度残差编码器和一个复杂的解码器。AdaMatting将抠图分为两步:三色图的自适应调整和alpha的预测,并使用带有两个不同解码器分支的深度卷积网络,以多任务方式来抠图。IndexNet[29]将池化中的索引作为特征图的函数,并提出一种索引引导的编码-解码网络,该网络使用可学习的索引引导的池化和上采样。[18]提出一个context-aware的网络来同时预测前景和alpha matte。Context-aware Matting使用两个解码器,一个用于局部特征,一个用于上下文信息。GCA Matting[25]在抠图中提出了引导的上下文注意力机制,类似于图像修复过程。
一些方法没有直接预测alpha matte,这些方法表明改变自由度会影响到网络的性能。[37]提出SampleNet来逐步地估计前景、背景和alpha matte,而不是同步地预测它们。[47]提出用两个解码分支分别估计前景和背景,然后用一个融合分支来获得结果。
以上这些方法都严重地依赖于作为输入的三色图来减小分辨率空间。近年来出现一些针对具体实际问题,利用图像语义信息而不需要任何三色图的方法。[35]将端到端的CNN与closed-form matting相结合,以自动地生成人像的trimap,然后再生成alpha matte。[5]提出一个语义人像抠图网络,使用一个带有抠图网络的语义分割网络来自动地提取人像的alpha mattes。
3. 方法
为了实现在一个高分辨率层级上对输入图像的透明度的传播,我们提出了一个全新的、层次化的透明度传播结构,其中神经网络可以看做是多层的、有不同图的图卷积网络[22],透明度可以在每两个像素之间传播。本节先介绍我们的层次化透明度传播块,接着再介绍用在HOP块中的尺度钝感的位置编码器。再之后介绍实现细节。
3.1 层次化透明度传播块
通常,non-local块[42]或者transformer[38]能够通过其自注意力机制[27]来全局地执行信息传递。然而,如果直接使用原始的non-local块或者图像的transformer在抠图中,会导致两种缺陷。一方面,non-local块计算量很大,虽然有一些减少其计算量的方法,但是在高分辨率特征图上传播透明度信息计算量还是很大,而抠图就是需要高分辨率。另一方面,non-local和transformer都是在输入的特征图上建立一个全连接的图,连边的权重是由输入特征图的节点生成的。在抠图任务中,每个节点的特征就是透明度信息。在视频分类[42]、语义分割[49]或图像修复[46]等语义任务中,基于不同特征节点之间的关系来传播语义特征是很简单的,而抠图任务中的传播需要更多的非语义的外观信息。
受稀疏Transformer[6,19]、局部自注意力[32]和引导上下文注意力[25]的成功经验启发,我们提出的层次化透明度传播结构包括两个不同的传播块,称为全局HOP块和局部HOP块,在这里,外观和透明度预测一起被用于传播。
所提的HOP结构如图2(a)所示。在我们的方法里,有两个编码器分支,一个提供透明度信息,另一个提供图像外观信息。将透明度编码器(opacity)和外观编码器(appearance)的特征图分别表示为 和 ,其位置 处的特征元素分别表示为 和 ,全局HOP块定义为:
其中, 既是key也是query的线性变换器, 是用来对齐传递信息和输入特征图 的变换器,softmax操作是执行在(x,y)维度。此外, 是该注意力机制(不带任何transformation)的value项,我们也可以将 视为value项。图3(c)所示是全局HOP块的详细结构。
在全局HOP块中,key和value是同质的,这一点和自注意力[27,38]或传统注意力机制[2,44]不同。在自注意力中,query,key和value都是从同一个特征计算来的,而在传统的注意力机制中,key和value是来自同一地方。然而,在HOP块中,query和key是共享相同的外观特征,而value则是使用透明度特征。
同样的,我们可以构造只关注每个特征元素的局部领域的局部HOP块:
其中 是元素位置为 的窗口大小为 的领域。
在我们的传播图中,每个节点有两个不同特征:透明度和外观。外观特征仅用来生成图的连边权重,而透明度特征则是实际上要传播的信息。HOP块和自注意力的区别也可从图3中看出。消冗研究中,我们将比较我们的HOP块与全局、局部自注意力的性能。
利用HOP块,我们构造了层次化透明度传播结构来估计alpha matte,如图2(b)所示。HOP结构包括一个全局HOP块和多个局部HOP块。示意图省略了HOP块之间的转置卷积,以显示透明度信息是如何层次化传播的。最底下的全局HOP块会在来自bottleneck的特征图上执行全局的透明度传播,在这里,特征图更多包含的是语义信息,而纹理信息更少。将语义特征全局地传播,以利用整张图像的全部信息,是很直观的。然后,在转置卷积的不同阶段之间,局部HOP块被插入到网络当中,在这里,高分辨率特征图中包含更多的纹理信息。因此,使用局部HOP块只关注每个query位置的领域,以提取纹理信息是很符合我们的动机的。通过我们的HOP结构,透明度信息在不同层级的特征上传播,从语义特征到纹理特征,从低分辨率到高分辨率。
此外,我们提出的HOP结构可以看做是一个4层的图卷积网络[22],在每一层有不同的图,在网络的不同阶段节点数是变化的。全局HOP块的图是全连接图,局部HOP块的图是稀疏图。所有的连边权重是通过注意力机制计算的,类似于图注意力网络[39]。
3.2 位置编码
先前一些工作[38,9,32]中,自注意力机制使用位置编码总是能带来提升。本节介绍我们的方法如何使用位置编码。我们用两种不同的位置编码方法:在全局HOP块中使用尺度不敏感的位置编码,在局部HOP块中使用局部相对位置编码。图4是不同位置编码示意图。
尺度不敏感位置编码 [38]将位置编码引入到Transformer中,以提高NLP性能。Transformer-xl[9]进一步将绝对编码扩展到相对位置编码。直观地,在用于抠图的2维编码中,我们可以将嵌入划分为行和列编码,以扩展相对或绝对位置编码。但是,先前的位置编码存在一个致命的缺陷是关注的领域大小是固定的,一旦推理图像大于训练图像尺寸,会出现一些训练过程从未出现的新的位置编码。抠图中,测试图像尺寸大于训练图像块(patchs)是非常常见的。为了解决这个问题,我们提出一种尺度不敏感的全局HOP块。
在我们的尺度不敏感的位置编码中,我们定义领域半径为 。半径以外的任何点的位置编码是相同的。半径以内的点,使用相对位置编码(图4(a))。因此,全局HOP块可以写为:
其中,为简便起见,我们和[38,9]那样使用正弦编码 ,在实际实现中, 取值为7。有了尺度不敏感位置编码,我们的HOP抠图可处理任何形状和大小的图像。除了位置嵌入,我们还设计了一个trimap嵌入来学习无论前景、背景、未知区域是否有不同注意力权重的情况。
因此公式中的 被修改为 ,其中 是位置 从缩放多的trimap的数据点。
局部相对位置编码 局部HOP块里,领域大小是固定的,因此不需要尺度不敏感位置编码。我们并不提出一个全新的局部相对位置编码方法,而是将[32]提出的局部相对位置编码扩展为一个方向不变性(direction-invariant)版本。局部HOP块中的编码方式是图4(d)。
与以往的位置编码[38,9,32]不同,我们在抠图中用的位置编码都是方向不变性(direction-invariant)的,这意味着嵌入只与query和key位置之间沿行或列的绝对距离有关。这一特性是因为抠图更像是一个语义较少的低级视觉任务,并且应该是旋转不变的(rotation-invariant)。
3.3 损失函数和实现细节
我们的网络的训练仅仅使用alpha matte来构造损失函数,损失函数为估计值和真值的差值的绝对值真值:
其中, 是位置 处估计的alpha matte, 是对应的真值, 是trimap中未知区域像素的个数。
我们选择在ImageNet[34]上预训练的ResNet-34[15]的前11个块作为透明度编码器的主干。外观编码器的主干选择为一些堆叠的步幅卷积层来提取更多的低级特征信息。训练数据由Adobe Image Matting[45]的前景和MS COCO[26]的背景合成。我们使用[25]中提出的基础数据增强。训练阶段的归一化使用批量归一化[20]和频域归一化(spectral normalization)[31]。使用Adam优化器[21]。遵循[16]使用FP16位精度训练。使用Warmup[12]和cosine decay[28]学习率调整策略。
4 实验
我们进行了广泛的实验和消冗研究。报告了在两个广泛使用的数据集上的结果:Composition-1k测试集和alphamatting.com数据集[33]。评价指标为[33]提出的均方误差(MSE)、绝对差和(SAD)、梯度误差(Grad)和连通性误差(Conn)。我们还将HOP结构中的注意力图可视化,以更好地理解它们是怎么起作用的。
4.1 随机插值数据增强
经验上讲,深度抠图的效果很容易受图像大小的影响。这是因为典型抠图方法关注的是图像中细节纹理信息,而对图像的resize操作会模糊边缘或高频信息,导致性能下降。因此大多数抠图方法都是在原图上评估的,没有使用任何resize操作。Context-aware Matting[18]中,作者指出,前景和背景的图片格式不同会导致轻微的伪影,这有助于网络区分前景和背景。本节我们也会给出一些新的发现,也即基于深度网络的抠图方法对插值运算很敏感,我们会介绍我们方法中的随机插值数据增强。
我们在Composition-1k测试集[45]上实验证明这一发现。首先,我们对RGB图像使用选定的插值算法放大1.5倍,再用相同的插值算法缩小至原尺寸。举个例子,假设图像大小是800×800,选定的插值算法是双线性插值,我们先将图像用双线性插值放大到1200×1200,再用双线性插值将图像缩小至800×800。最后,将缩放过的图像输入给网络,并计算预测值和原始真值之间的误差。需要注意的是,我们不是先缩小再放大,因为先缩小会导致更多的信息丢失。
评价结果见表1。我们还比较了真值缩放前后的误差,以供参考。真值缩放前后误差是指,不使用网络推理,直接将真值缩放,计算缩放后的真值和缩放前的真值之间的误差。这个结果表明插值本身就会带来误差,该误差可以作为评估抠图方法的下界。HOP-5×5是我们的基线模型,其表示局部HOP块的邻域大小为5×5,HOP块中不使用位置编码和trimap嵌入。从表1中可以看到,不同的插值测试之间的的差距要大于下界。换言之,相比较插值本身,不同的插值算法会带来更多的误差。还可以看到,HOP-5×5抠图的双线性插值与cubic插值之间的差距,要大于IndexNet抠图的双线性插值与cubic插值之间的差距。我们的解释是,在训练阶段的数据增强中,我们按照DeepMatting[45]提出的将背景图片用cubic插值缩放到和前景图片相同大小。这使得我们的方法在测试的时候用cubic插值表现更佳。
基于前面的发现,我们提出随机插值数据增强用到我们的方法中。训练阶段的数据预处理中,我们随机地、等概率地选择一种插值算法,用于任何插值操作。因此,在训练的一个小批量中,合成图像可能是由不同的插值算法生成的。此外,前景、背景、alpha matte图像在合成前可以用不同的插值算法来缩放。如表1所示,使用随机插值数据增强的训练,不仅提高了性能,而且缩小了双线性插值和cubic插值之间的差距。
4.2 在Composition-1k测试集上的结果
Composition-1k测试集[45]包含1000张合成图像,其合成于50个不同的前景。我们将我们的方法和最先进方法进行比较,结果如表2所示。w/o HOP+RI表示主干网络没有HOP块,训练的时候使用随机插值数据增强。我们方法的不同版本都优于最先进方法。在Composition-1k测试集上的一些定性结果如图5所示。DeepMatting[45]的结果来自源码和IndexNet[29]提供的预训练模型。
此外,我们在表4中与一些最先进模型比较了参数量和模型速度。我们对Composition-1k测试集中的每张图像在NVIDIA RTX 2080 Ti GPU上评估平均推理时间。需要注意的是,Context-aware Matting和DeepMatting需要大于11G GPU来估计Composition-1k测试集的高分辨率图像的alpha matte。因此我们在这两个方法上将图像缩小至0.8倍。
4.3 在Alphamatting.com数据集上的结果
alphamatting.com数据集有8个测试图像用于线上基准评估。每个测试图像有3个trimap,即small、large、user。表3是我们方法在alphamatting.com上的排名的平均值。合计排名是在每种评估指标下所有3种trimap的结果排名的平均值。如表3所示,在不同的评估指标下,我们的HOP Matting 优于其它最先进方法。
4.4 消冗研究
为了验证网络的各个组成部分都是有用的,我们在Composition-1k测试集上进行了3个不同的实验。我们首先去掉不同的HOP块来评估HOP-5×5模型。从表5的结果可以看出,层次化透明度传播结构能够提升抠图的性能。Global & Local Self-attention是指用全局自注意力替换全局HOP、用局部自注意力替换局部HOP的方法。第二项消冗研究中,我们表明了在我们方法中使用位置编码、trimap嵌入和随机插值数据增强的效果。在Composition-1k测试集[45]上评估的定量结果见表6。补充材料中,我们还报告了不同邻域窗口大小的结果。
4.5 HOP结构的可视化
将HOP结构的注意力图可视化能够很方便地有助于理解在我们方法中透明度信息是如何层次化地传播的。为实现此目的,我们在输入图像上的梯度图来可视化我们模型所关注的地方。我们在预测的alpha matte的未知区域上随机选择一个像素。然后将较大的损失分配给该单个像素,并且假设预测的所有其他像素完全正确而没有任何损失。然后,执行反向传播,并将梯度反向传播至输入图像。梯度图显示了输入图像的每个像素与预测中选定的alpha matte像素之间的关系。我们在图6中展示了Composition-1k测试集[45]的图像的梯度图。无HOP块的结果来自表5中我们为消冗研究训练的模型。如图6所示,带有HOP块的模型能够聚合整张图的信息,并且能更加关注具有相似外观的区域,无HOP块的模型则更关注所选预测点周围的局部区域。
5. 结论及未来工作
本文提出层次化透明度传播方法来抠图,使用局部HOP块和全局HOP块来实现不同语义级别的特征图的透明度信息传播。实验结果证明该方法的优越性。此外,消冗研究表明我们的位置编码和随机插值数据增强是有用的。考虑到全连接注意力网络的成功[32],未来研究全连接HOP块网络是由希望的。另外一个有趣的未来工作是堆叠的局部HOP块和全卷积块的混合网络。
SQL/DAX 中的多父层次结构传播
【中文标题】SQL/DAX 中的多父层次结构传播【英文标题】:multi-parent hierarchy propagation in SQL/DAX 【发布时间】:2019-07-17 04:05:06 【问题描述】:假设我有一个表格,其中描述了每个员工的主要和次要报告路线。让我们想象一个组织结构,其中 CEO 员工 0
有 2 位经理(1
和 2
)向他汇报。
经理 2
在她的团队中有 2 名员工(3
和 4
),但员工 4
实际上在经理 1
的时区工作,所以虽然他有 2
作为他的主要成员报告,他还向经理1
报告作为次要报告,以便1
可以履行正常的受托管理义务(提供支持等)。
除了担任员工4
的二级管理角色外,经理2
还有一名团队成员向他汇报工作 (5
)。
编辑:为了说明多亲问题,让我们给团队成员4
一个实习生,工作人员6
。 团队成员6
现在是经理1
和2
的下属 - 后者通过次要报告线继承。
组织结构如下所示:
+--+-------+---------+
|ID|Primary|Secondary|
|0 |NULL |NULL |
|1 |0 |NULL |
|2 |0 |NULL |
|3 |1 |NULL |
|4 |1 |2 |
|5 |2 |NULL |
|6 |4 |NULL |
+--+-------+---------+
现在我想将其扩展为一个 SQL 视图,该视图为我提供了一个下面任何给定员工的人员列表,涵盖主要和次要报告。因此,对于员工2
(具有主要和次要报告的经理),我希望看到团队成员4
和5
,对于首席执行官(0
),我希望看到任何员工总裁以外的成员。我们的新实习生6
是CEO的下属,经理1
和2
,以及他的直属经理4
。
看起来像这样:
+--+-----------+
|ID|Subordinate|
|0 |1 |
|0 |2 |
|0 |3 |
|0 |4 |
|0 |5 |
|0 |6 |
|1 |3 |
|1 |4 |
|1 |6 |
|2 |4 |
|2 |5 |
|2 |6 |
|4 |6 |
+--+-----------+
如何在 SQL 中实现这一点?我正在考虑对 ID 进行某种OUTER APPLY
操作,但我正在努力解决(我认为)解决此问题所需的重入性。我的背景是程序编程,我认为这是我在这里苦苦挣扎的部分原因。
NB:我想在这里预料到的一个明显问题是“这肯定是一个 XY 问题 - 你到底为什么要这样做?”
我想在 PowerBI 中使用row-level security,让每个员工都可以访问有关组织结构中其下属个人的某些信息。不幸的是,RLS 不允许每个人执行存储过程,所以我坚持做这种组合扩展,然后简单地根据登录名过滤上表。
话虽如此,我对解决这个问题的更好方法持开放态度。
【问题讨论】:
【参考方案1】:简单的存储方式,恕我直言。所有诠释。只是一个连接点,但将满足我可以看到的所有需求,并在各个方向上具有很大的灵活性。项目可以是一个小项目,也可以是一组项目,甚至是部门/公司层次结构。似乎动态和适应性是一个优先事项或排序。
+--+-------+---------+-------+--------+
|ID|project|over |under |level |
|0 |14 |0 |9 |1 |
|1 |53 |4 |1 |2 |
|2 |4 |4 |4 |2 |
|3 |1 |4 |2 |3 |
|4 |1 |0 |7 |1 |
|5 |2 |4 |6 |1 |
|6 |4 |4 |8 |5 |
+--+-------+---------+-------+--------+
以扩展方式使用项目的一个示例是为部门/公司/设施/办公室/房间/供应商/职位或您可以想到的任何其他“分组”添加一个正在进行的“使命宣言”项目层次结构需要分辨率。为什么要让生活变得更复杂?如果需要历史信息,您可能有一天需要做的最糟糕的事情是将已完成项目的条目卸载到某种存档中。
【讨论】:
欢迎来到 SO :) 我不太清楚这个回复在说什么。除非你能澄清它,否则我会标记这个。 作为提供功能的链接表,可能不是您想要的,但如果遇到同样的问题,允许未来范围蔓延或只是扩展功能,我会这样做。 不是 dax 解决方案,只是 t-sql,80% 只是内部连接,以获取所需的信息,从您想要启动的任何地方,从人员、项目、关系级别的入口点。成为 sql dev 近 20 年了,从 sql 2000 开始,大约在 1999 年,所以我首先看 sql 并尽可能简单地开始。我没有提出疑问,因为入口点非常多,而且感觉太明显了,并不意味着看起来令人困惑。请接受我的入侵道歉。随着时间的推移,我会在这里站稳脚跟,感谢您的欢迎! :)【参考方案2】:使用 DAX 中的父子层次结构函数很容易解决这个问题。我认为您不需要构建任何额外的表,只需在您的 RLS 规则中添加以下条件:
对于员工N
,您只需检查是否
PATHCONTAINS(PATH('Hierarchy'[ID], 'Hierarchy'[Primary]), N)
或
PATHCONTAINS(PATH('Hierarchy'[ID], 'Hierarchy'[Secondary]), N)
请注意,这允许 Employee N
看到他们自己以及他们的下属,但如果您不希望这样,您可以添加一个额外的条件。
编辑:当你的结构不是一棵树时,问题变得更加困难。这是一种应该可行的方法。
对于每个ID
,查找下属得到Level1
,搜索Level1
下一级下属,以此类推,直到没有下属。 (如果你的结构中有一个循环让你回到更高的层次,那么你就会陷入递归。)
在这种情况下,顶部下方有三个级别,因此我们需要三个步骤。
| ID | Primary | Secondary | Level1 | Level2 | Level3 |
|----|---------|-----------|--------|--------|--------|
| 0 | | | 1 | 4 | 6 |
| 0 | | | 2 | 4 | 6 |
| 0 | | | 2 | 5 | |
| 0 | | | 3 | | |
| 1 | 0 | | 4 | 6 | |
| 2 | 0 | | 4 | 6 | |
| 2 | 0 | | 5 | | |
| 3 | 0 | | | | |
| 4 | 1 | 2 | 6 | | |
| 5 | 2 | | | | |
| 6 | 4 | | | | |
这是在 Power Query 编辑器中执行此操作的 M 代码:
let
Source = Table.FromRows(0,null,null,1,0,null,2,0,null,3,0,null,4,1,2,5,2,null,6,4,null,"ID", "Primary", "Secondary"),
#"Changed Type" = Table.TransformColumnTypes(Source,"ID", Int64.Type, "Primary", Int64.Type, "Secondary", Int64.Type),
SearchNextLevel = ExpandNext(ExpandNext(ExpandNext(#"Changed Type", "Level1", "ID"), "Level2", "Level1"), "Level3", "Level2"),
#"Appended Query" =
Table.Combine(
Table.RenameColumns(Table.SelectColumns(SearchNextLevel, "ID", "Level1"), "Level1","Subordinate"),
Table.RenameColumns(Table.SelectColumns(SearchNextLevel, "ID", "Level2"), "Level2","Subordinate"),
Table.RenameColumns(Table.SelectColumns(SearchNextLevel, "ID", "Level3"), "Level3","Subordinate")
),
#"Filtered Rows" = Table.SelectRows(#"Appended Query", each ([Subordinate] <> null)),
#"Removed Duplicates" = Table.Distinct(#"Filtered Rows"),
#"Sorted Rows" = Table.Sort(#"Removed Duplicates","ID", Order.Ascending, "Subordinate", Order.Ascending)
in
#"Sorted Rows"
这是多次使用以扩展至下一个级别的自定义函数:
let
ExpandToNextLevel = (T as table, NextLevel as text, ThisLevel as text) as table =>
let
SearchNextLevel =
Table.AddColumn(T,
NextLevel,
(C) =>
Table.SelectRows(
T, each Record.Field(C, ThisLevel) <> null and
([Primary] = Record.Field(C, ThisLevel) or
[Secondary] = Record.Field(C, ThisLevel))
)[ID]
),
ExpandColumn = Table.ExpandListColumn(SearchNextLevel, NextLevel)
in
ExpandColumn
in
ExpandToNextLevel
为了通用,我显然需要将扩展和追加放入递归循环中。如果时间允许,我会回到这个。
编辑:这是查询的递归版本,它使用反透视而不是追加。
let
Source = Table.FromRows(0,null,null,1,0,null,2,0,null,3,0,null,4,1,2,5,2,null,6,4,null,"ID", "Primary", "Secondary"),
#"Changed Types" = Table.TransformColumnTypes(Source,"ID", Int64.Type, "Primary", Int64.Type, "Secondary", Int64.Type),
IDCount = List.Count(List.Distinct(#"Changed Types"[ID])),
RecursiveExpand = List.Generate(
() => [i=0, InputTable = #"Changed Types"],
each [i] < IDCount and
List.NonNullCount(List.Last(Table.ToColumns([InputTable]))) > 0,
each [
CurrentLevel = if [i] = 0 then "ID" else "Level" & Text.From([i]),
NextLevel = if [i] = 0 then "Level1" else "Level" & Text.From([i]+1),
InputTable = ExpandNext([InputTable], NextLevel, CurrentLevel),
i = [i] + 1
]
),
FinalTable = List.Last(RecursiveExpand)[InputTable],
#"Unpivoted Other Columns" = Table.UnpivotOtherColumns(FinalTable, "Secondary", "Primary", "ID", "Level", "Subordinate"),
#"Removed Other Columns" = Table.SelectColumns(#"Unpivoted Other Columns","ID", "Subordinate"),
#"Removed Duplicates" = Table.Distinct(#"Removed Other Columns"),
#"Sorted Rows" = Table.Sort(#"Removed Duplicates","ID", Order.Ascending, "Subordinate", Order.Ascending)
in
#"Sorted Rows"
它将不断扩展关卡,直到扩展至下一个关卡产生所有空值或达到最大关卡数以防止无限循环。
【讨论】:
如果4
有一个主要的下属怎么办?在这种情况下,我希望它们也显示为 2
的下属,但这不适用于上述情况,因为这些行被视为单独的。
所以你的层次结构不是一棵树,也没有唯一的向上路径。这肯定会让事情变得更加困难。我建议编辑您的帖子以添加此示例(即添加 6,4,NULL
行)。
是的,没错 - 我添加了一名实习生(员工6
),其经理拥有主要和次要报告,这说明了这一要求。很抱歉之前没有说得足够清楚。【参考方案3】:
要在 SQL 中获得您想要的结果,最简单的方法是使用递归 CTE。
在下面的示例中,我将工作分为两个 CTE。第一个将集合转换为成对的经理和下属。第二个 CTE 从第一个 CTE 获取所有结果,然后使用 UNION ALL 连接到自身,其中第一个 CTE 的经理是递归 CTE 中的下属。这将不断重复,直到无法匹配为止。
因为下属可能有多个经理,所以可能会为每个祖先返回重复的行。因为从递归 CTE 返回结果时使用了 DISTINCT。
WITH all_reports AS (
SELECT [Primary] [ManagerID], ID [Subordinate]
FROM tbl
WHERE [Primary] IS NOT NULL
UNION
SELECT [Secondary], ID
FROM tbl
WHERE [Secondary] IS NOT NULL
)
, recursive_cte AS (
SELECT ManagerID, Subordinate
FROM all_reports
UNION ALL
SELECT ancestor.ManagerID, descendant.Subordinate
FROM recursive_cte ancestor
INNER JOIN all_reports descendant ON descendant.ManagerID = ancestor.Subordinate
)
SELECT DISTINCT ManagerID, Subordinate
FROM recursive_cte
如果你想要经理和下属之间的距离,那么重写递归CTE如下:
SELECT ManagerID, Subordinate, 1 [Distance]
FROM all_reports
UNION ALL
SELECT ancestor.ManagerID, descendant.Subordinate, ancestor.Distance + 1
FROM recursive_cte ancestor
INNER JOIN all_reports descendant ON descendant.ManagerID = ancestor.Subordinate
【讨论】:
干杯这按预期工作。我不明白的一件事是递归的停止条件是什么。为什么descendant
上的ancestor
的INNER JOIN
不会导致无限循环?
这可能是个问题,但有帮助的是在每次迭代中它只会发送上一次迭代的行。因此,只要您在层次结构中没有后代可以成为其祖先经理之一的经理的情况,那么您就可以了。【参考方案4】:
您需要展平报告层次结构和次要报告层次结构,将它们加载到表格模型中的单独表格中。
请参阅DAX Patterns: Parent-Child Hierarchies,了解如何在 DAX 中完全做到这一点。或者,您可以使用使用递归公用表表达式的 SQL Server 查询来展平两个层次结构。
在任何一种情况下,它们都会成为模型中的两个单独的表和两个单独的关系,然后您可以在 RLS 过滤器中引用它们。
【讨论】:
在我的例子中,它们并不是分开的。请参阅我对 Alexis Olson 的回答的评论。 嗯,这类问题有一个通用的解决方案:实现关系中的所有对。因此,对于每个员工,每个员工都可以看到一行。 这就是我希望实现的目标,(请参阅我的问题中的最后一个表格)但我不知道该怎么做。以上是关于层次化透明度传播的主要内容,如果未能解决你的问题,请参考以下文章