Excel 公式部分从表数据动态构建数组

use*_*578 5 excel excel-formula dynamic-arrays

我不知道使用单个单元格公式是否可以实现这一点。需要 Excel 2010 兼容(及更高版本)的公式。目的是使用如下公式

{=SUM(INDEX(built_Array;N(IF(1;ROW(INDIRECT(x1 &":"& x2)))))} 
Run Code Online (Sandbox Code Playgroud)

其中 x1 和 x2 是对包含与构建数组兼容的起始索引和结束索引的单元格的引用。

Excel 公式的“构建数组”部分应根据其他地方的两个表中的数据构建:表上的标识符用于选择实际内容(使用查找 Excel 函数)。一张表包含重复值的数量,另一张表包含实际值。

例如:

P1 P2 P3 P1 P2 P3
i01 2 4 i01 20.0 20.6
i02 3 i02 10.0
i03 2 7 9 i03 30.0 30.4 30.2
i04 4 2 i04 15.0 15.1
i05 5 i05 10.0

因此 i03 的构建数组将是

{30.0;30.0;30.4;30.4;30.4;30.4;30.4;30.4;30.4;30.2;30.2;30.2;30.2;30.2;30.2;30.2;30.2;30.2} 
Run Code Online (Sandbox Code Playgroud)

对于 i04 来说是

{15.0;15.0;15.0;15.0;15.1;15.1}
Run Code Online (Sandbox Code Playgroud)

那么,对于 i04 取第 3 到第 5 个值,上面的数组 (ctrl-shift-enter) 公式的结果将是 45.1,而对于 i04 取第 2 到第 3 个值,结果将是 30。

我发现困难的是从公式内的表构建数组,特别是因为每个标识符可能具有不同数量的组件。

我有预感,这应该是可行的,在其他编程语言中,会使用迭代或递归,但我想探索这种方式,而不是恢复到 VBA(如果存在概念上的原因,这在 Excel 公式中是不可能的,我会也非常感兴趣,以防我应该改变方法,因为我对公式结果感兴趣)。

use*_*578 3

经过一些研究后,我们找到了该问题的完整工作答案(stackoverflow 和贡献者的建议中最有帮助,谢谢)。

\n

方法1(首选)

\n

下面的单单元格 CSE(数组)公式使用以下几种变体

\n
=((ROW(INDIRECT("1:"& e1))>=lb1)*(ROW(INDIRECT("1:"& e1))<=ub1))\n
Run Code Online (Sandbox Code Playgroud)\n

其中e1是 1 列数组中的元素数量,lb1是需要 1 的下限,ub1是上限,超出范围的元素值为 0。

\n

OP 数据需要简单地转换为建议公式的累积指数

\n
        P1  P2  P3          P1      P2      P3\ni01     2   6   6   i01     20.0    20.6    \ni02     3   3   3   i02     10.0        \ni03     2   9   18  i03     30.0    30.4    30.2\ni04     4   6   6   i04     15.0    15.1    \ni05     5   5   5   i05     10.0        \n
Run Code Online (Sandbox Code Playgroud)\n

以下是一个 CSE 公式,应用于与现有列具有相同列数的范围(上面的示例:3 个连续列),然后在公式栏中插入公式后,使用 ctrl-shift-enter。

\n

公式

\n
{=TRANSPOSE(MMULT(TRANSPOSE(((ROW(INDIRECT("1:"&e1))>=IF(COLUMN(set_of_pos)-COLUMN(INDEX(set_of_pos;1;0)=0;0;OFFSET(set_of_pos;0;-1))+1)*(ROW(INDIRECT("1:"&e1))<=set_of_pos))*((ROW(INDIRECT("1:"&e1))>=x2+1)*(ROW(INDIRECT("1:"&e1))<=x1)));--(ROW(INDIRECT("1:"&e1))>0)))}\n
Run Code Online (Sandbox Code Playgroud)\n

其中e1是 1 列数组中的元素数量(也是最后位置的值),x1x2对包含起始索引和结束索引的单元格的引用;set_of_pos是告诉每个部分中每个的最后位置的 RANGE(例如,对于 i03:2,9,18)

\n

PARTS\n((ROW(INDIRECT("1:"&e1))>=x2+1)*(ROW(INDIRECT("1:"&e1))<=x1)))创建一个(公式内)数组,其中包含连续数量的 1(来自布尔值),以 0 为界,表示需要保留或注册的索引。(尺寸 e1 \xc3\x97 1。)

\n

((ROW(INDIRECT("1:"&e1))>=IF(set_of_pos=1;0;OFFSET(set_of_pos;0;-1))+1)*(ROW(INDIRECT("1:"&e1))<=set_of_pos))创建一个维度为 e1 \xc3\x97 columns(set_of_pos) 的(公式内)数组,其中每个部分单元格将值扩展为一堆 1。[这部分,对于每一列,上界是实际值,下界是左边的值加1——因为set_of_pos是一个RANGE,所以只要使用CSE输入,set_of_pos中的每一列都是这样做的公式; 注:如果是最左边的单元格,则使用 0 代替。]

\n

然后两个部分都用 * 进行操作,从而获得一个维度为 e1 \xc3\x97 columns(set_of_pos) 的(公式内)数组,其位置满足位于节堆栈中和要保留的索引集中(方便地分隔在列)。

\n

我们需要转置这个结果以获得维度列(set_of_pos)\xc3\x97 e1 的(公式内)数组。MMULT(以及由 定义的全 1 e1 \xc3\x97 1 --(ROW(INDIRECT("1:"&e1))>0)使我们能够计算压缩数组中每列 1 的数量以及不同列中的数量 [columns(set_of_pos) \xc3\x97 1]。[注意,我们需要包含双负数“--”以将布尔值转换为数字,因为 MMULT 需要数字数组,否则会抛出错误 - 根据函数规范,MMULT 不接受布尔值。]

\n

需要额外的转置才能水平放置数组 [1 \xc3\x97 columns(set_of_pos)]。

\n

现在,通过以下方式获得最终结果,=SUMPRODUCT(previous_result;set_of_val)其中 previous_result 是上面的结果,set_of_val 是带有值的范围。

\n

有办法保留哪些堆栈被修改可以用于其他目的(可以计算已用堆栈),并且获得最终结果是函数 SUMPRODUCT 的简单应用。列或节的数量不是硬编码的 - 它是通过选择公式范围内的 set_of_pos 并在应用 (CSE) 数组公式时选择适当的连续列数来设置的。

\n

方法 2\n为了完整起见,我们探索了第二种方法。如果这被认为资源密集程度较低,我当然想知道。我的预感是,尽管打字次数较少,但它的资源密集程度更高,但我无法对其进行测试。

\n

基本上,包含所有值的字符串被构建到单元格中(例如,使用 REPT 和管道 |,对于 i03,我们将具有“30.0|30.0|30.4|30.4|30.4|30.4|30.4|30.4|30.4|30.2|30.2” |30.2|30.2|30.2|30.2|30.2|30.2|30.2|")

\n

可行的解决方案(不需要 CSE - SUMPRODUCT 负责充分构建的三个 1 列数组)

\n
=SUMPRODUCT(((ROW(INDIRECT("1:"& e1))<=x1)*(ROW(INDIRECT("1:"&e1))>=x2+1))*TRIM(MID(SUBSTITUTE(s1;"|";REPT(" ";LEN(s1)));(ROW(INDIRECT("1:"& e1))-1)*LEN(s1)+1;LEN(s1))))\n
Run Code Online (Sandbox Code Playgroud)\n

其中 x1、x2 和 e1 是对包含起始索引和结束索引以及总元素数(如上定义)的单元格的引用,s1 是所有值按顺序排列的字符串。

\n

部分

\n
(ROW(INDIRECT("1:"& e1))<=x1)*(ROW(INDIRECT("1:"&e1))>=x2+1))\n
Run Code Online (Sandbox Code Playgroud)\n

本质上存在于方法 1 中:由连续数量的 1(来自布尔值)组成的(公式内)数组,以代表需要保留的索引的 0 为界。(尺寸 e1 \xc3\x97 1。)

\n
TRIM(MID(SUBSTITUTE(s1;"|";REPT(" ";LEN(s1)));(ROW(INDIRECT("1:"& e1))-1)*LEN(s1)+1;LEN(s1)))\n
Run Code Online (Sandbox Code Playgroud)\n

该公式采用字符串 (s1) 并将每个分隔符替换为等于字符串长度的空格数,然后在 ROW(INDIRECT("1:"& e1)) 创建的数组的每个元素上放置不同的部分

\n

[更通用的解决方案是通过获取字符串和去掉分隔符的字符串之间的差异来获取元素的数量,但在我的小项目中需要元素的数量,并在标签 e1 下定义。该技术在其他地方进行了简单的解释,将原始字符串的长度部分从 ROW(INDIRECT("1:"& e1)) 给出的序列号开始放置,乘以原始字符串的长度,然后删除所有周围的空格与修剪。]

\n

致谢和参考文献

\n

感谢答案和评论,我们可以找到两个有相关评论的 stackoverflow 问题:

\n

/sf/ask/1772126611/ [在此处输入链接描述][1]\nand\n有没有一种方法可以在不使用VBA的情况下在Excel中连接两个数组?

\n

两者都值得一读。方法 1 部分是从头开始开发的,但方法 2 通过仔细阅读已得到很大改进,特别是在将字符串转换为公式内数组时。由于需要与 Excel 2010 兼容,因此选择了 ROW(INDIRECT()) 而不是 SEQUENCE()。

\n