KDB - 时态函数结果成为后续行函数的输入

KDB - Temporal function results become input for function on subsequent row

我一直在与 KDB 合作,从一组表示模型分段函数的输入中创建时态数据。我面临的挑战是,对于一个特定的 ID,有几个片段,其中第一个片段的时间结果的最后一个值成为(但不总是)后续片段的输入。

//Create sample table
t:([id:`AAA`AAA`AAA`BBB`CCC`CCC;seg:1 2 3 1 1 2];aa: 1500 0n 0n 40 900 0N;bb:150 200 30 40 10 15;cc: .40 .25 .35 .5 .35 .45; Fname:`Fx`Fy`Fy`Fy`Fz`Fz);

下面的简单虚拟函数return5个数据周期但实际上每个函数可以return几千个数据点

//Dummy functions to generate temporal data
Fx:{[aa;bb;cc] (aa%bb)*(1-exp(neg cc*1+til 5))*100};
Fy:{[aa;bb;cc] (aa%cc)*(1*(1-exp(neg cc*1+til 5)))};
Fz:{[aa;bb;cc] (aa%bb)*(1-exp(neg cc*1+til 5))};

当我 运行 每个函数的结果时,我们可以看到我们在几个片段上缺少 aa 的地方。 aa 应该是前一段(即 aa = 864.6647 for AAA seg 2aa= 74.36035f for CCC seg 2)

的前一个(最后一个 t[result])
show update result:first[Call_Function]'[aa;bb;cc] by Call_Function from t

id  seg| aa   bb  cc   Fname result
-------| ----------------------------------------------------------------
AAA 1  | 1500 150 0.4  Fx    329.68   550.671  698.8058 798.1035 864.6647
AAA 2  |      200 0.25 Fy
AAA 3  |      30  0.35 Fy
BBB 1  | 40   40  0.5  Fy    31.47755 50.56964 62.14959 69.17318 73.4332
CCC 1  | 900  10  0.35 Fz    26.57807 45.30732 58.5056  67.80627 74.36035
CCC 2  |      15  0.45 Fz

我试过尝试引用之前的片段 prev(last(t[result]) 但列表结果没有参考意义。同样,我知道 / (over) 迭代器会很有用,但我一直没有成功实现它。

我考虑将其分解为几个步骤(所有片段 1,然后是片段 2,依此类推),然后将它们全部附加到最后的 table。同样,我想跟踪每个段的累积值和时间计数(时间)以作为限制器传递给函数,因此成功引用前一行有多种用途。

最终,一旦填充,我将取消 table 的分组,将其放入类似于下面的输出中,然后我可以根据需要重新排序。

q)show ungroup t  
id  seg aa   bb  cc   Fname result  
------------------------------------
AAA 1   1500 150 0.4  Fx    329.68
AAA 1   1500 150 0.4  Fx    550.671
AAA 1   1500 150 0.4  Fx    698.8058
AAA 1   1500 150 0.4  Fx    798.1035
AAA 1   1500 150 0.4  Fx    864.6647
AAA 2        200 0.25 Fy
AAA 2        200 0.25 Fy
AAA 2        200 0.25 Fy
AAA 2        200 0.25 Fy
AAA 2        200 0.25 Fy
AAA 3        30  0.35 Fy
AAA 3        30  0.35 Fy
AAA 3        30  0.35 Fy
AAA 3        30  0.35 Fy
AAA 3        30  0.35 Fy
BBB 1   40   40  0.5  Fy    31.47755
BBB 1   40   40  0.5  Fy    50.56964
BBB 1   40   40  0.5  Fy    62.14959
BBB 1   40   40  0.5  Fy    69.17318
BBB 1   40   40  0.5  Fy    73.4332

TL;DR 我认为以下是您想要的:

q)t:update result:count[t]#enlist`float$() from t; // table extended to already contain a results column

q)applyF:{[t] update result:first[Fname]'[aa;bb;cc] by Fname from t where not null aa, 0=count each result} //applies each Fname function when needed
q)updateA:{[t]update aa:prev[last each result]^aa by id from t}; // updates column aa based on previous results
q)myUpd:updateA applyF ::; // helper function applying the two above

q)ungroup myUpd over t;
id  seg aa       bb  cc   Fname result  
----------------------------------------
AAA 1   1500     150 0.4  Fx    329.68  
AAA 1   1500     150 0.4  Fx    550.671 
AAA 1   1500     150 0.4  Fx    698.8058
AAA 1   1500     150 0.4  Fx    798.1035
AAA 1   1500     150 0.4  Fx    864.6647
AAA 2   864.6647 200 0.25 Fy    765.0526
AAA 2   864.6647 200 0.25 Fy    1360.876
AAA 2   864.6647 200 0.25 Fy    1824.904
AAA 2   864.6647 200 0.25 Fy    2186.289
AAA 2   864.6647 200 0.25 Fy    2467.737
AAA 3   2467.737 30  0.35 Fy    2082.149
AAA 3   2467.737 30  0.35 Fy    3549.414
AAA 3   2467.737 30  0.35 Fy    4583.378
AAA 3   2467.737 30  0.35 Fy    5312.001
AAA 3   2467.737 30  0.35 Fy    5825.452
BBB 1   40       40  0.5  Fy    31.47755
BBB 1   40       40  0.5  Fy    50.56964
BBB 1   40       40  0.5  Fy    62.14959
BBB 1   40       40  0.5  Fy    69.17318
BBB 1   40       40  0.5  Fy    73.4332 
CCC 1   900      10  0.35 Fz    26.57807
CCC 1   900      10  0.35 Fz    45.30732
CCC 1   900      10  0.35 Fz    58.5056 
CCC 1   900      10  0.35 Fz    67.80627
CCC 1   900      10  0.35 Fz    74.36035
CCC 2   74.36035 15  0.45 Fz    1.796406
CCC 2   74.36035 15  0.45 Fz    2.941846
CCC 2   74.36035 15  0.45 Fz    3.67221 
CCC 2   74.36035 15  0.45 Fz    4.137911
CCC 2   74.36035 15  0.45 Fz    4.434855

现在进行解释,希望不要太冗长。

我将在这里做几个假设:

  1. 只有第 aa 列会有空值
  2. 对于尚未定义 aa 的行,我们可以推迟评估 result

为了方便起见,我启动 t 以便它有一个空的 result

q)t:update result:count[t]#enlist`float$() from t;
id  seg| aa   bb  cc   Fname result
-------| --------------------------
AAA 1  | 1500 150 0.4  Fx          
AAA 2  |      200 0.25 Fy          
AAA 3  |      30  0.35 Fy          
BBB 1  | 40   40  0.5  Fy          
CCC 1  | 900  10  0.35 Fz          
CCC 2  |      15  0.45 Fz          

并定义一个函数,该函数将为任何已定义 aa 且尚未计算

的行计算 result
q)applyF:{[t] update result:first[Fname]'[aa;bb;cc] by Fname from t where not null aa};

现在生成结果就像调用函数一样简单

q)applyF t;
id  seg| aa   bb  cc   Fname result                                     
-------| ---------------------------------------------------------------
AAA 1  | 1500 150 0.4  Fx    329.68 550.671 698.8058 798.1035 864.6647  
AAA 2  |      200 0.25 Fy    `float$()                                  
AAA 3  |      30  0.35 Fy    `float$()                                  
BBB 1  | 40   40  0.5  Fy    31.47755 50.56964 62.14959 69.17318 73.4332
CCC 1  | 900  10  0.35 Fz    26.57807 45.30732 58.5056 67.80627 74.36035
CCC 2  |      15  0.45 Fz    `float$()                                  

要从 result 中获取下一个 aa 值,您可以执行类似

的操作
q)update aa:prev[last each result]^aa by id from applyF t;
id  seg| aa       bb  cc   Fname result                                     
-------| -------------------------------------------------------------------
AAA 1  | 1500     150 0.4  Fx    329.68 550.671 698.8058 798.1035 864.6647  
AAA 2  | 864.6647 200 0.25 Fy    `float$()                                  
AAA 3  |          30  0.35 Fy    `float$()                                  
BBB 1  | 40       40  0.5  Fy    31.47755 50.56964 62.14959 69.17318 73.4332
CCC 1  | 900      10  0.35 Fz    26.57807 45.30732 58.5056 67.80627 74.36035
CCC 2  | 74.36035 15  0.45 Fz    `float$()    

我们可以通过编写另一个更新函数来简化 aa

q)updateA:{[t]update aa:prev[last each result]^aa by id from t};
q)updateA applyF t
id  seg| aa       bb  cc   Fname result                                     
-------| -------------------------------------------------------------------
AAA 1  | 1500     150 0.4  Fx    329.68 550.671 698.8058 798.1035 864.6647  
AAA 2  | 864.6647 200 0.25 Fy    `float$()                                  
AAA 3  |          30  0.35 Fy    `float$()                                  
BBB 1  | 40       40  0.5  Fy    31.47755 50.56964 62.14959 69.17318 73.4332
CCC 1  | 900      10  0.35 Fz    26.57807 45.30732 58.5056 67.80627 74.36035
CCC 2  | 74.36035 15  0.45 Fz    `float$()               

现在,为了获得您想要的结果,我们需要一遍又一遍地应用这些更新。您对 over 迭代器的直觉在这里是正确的。此处的用法应用更新,直到 table 停止更改(又名 converge

q)myUpd:updateA applyF ::; // both update functions combined into one or convenience 
q)myUpd over t
id  seg| aa       bb  cc   Fname result                                      
-------| --------------------------------------------------------------------
AAA 1  | 1500     150 0.4  Fx    329.68   550.671  698.8058 798.1035 864.6647
AAA 2  | 864.6647 200 0.25 Fy    765.0526 1360.876 1824.904 2186.289 2467.737
AAA 3  | 2467.737 30  0.35 Fy    2082.149 3549.414 4583.378 5312.001 5825.452
BBB 1  | 40       40  0.5  Fy    31.47755 50.56964 62.14959 69.17318 73.4332 
CCC 1  | 900      10  0.35 Fz    26.57807 45.30732 58.5056  67.80627 74.36035
CCC 2  | 74.36035 15  0.45 Fz    1.796406 2.941846 3.67221  4.137911 4.434855
q)(myUpd myUpd myUpd t) ~ (myUpd over t)
1b

您可以将 ungroup 应用于上面的结果以获得您想要的输出。

另一种使用 over 的方法:

q)update res:{z .@[y;0;{y^x};last x]}\[0n;flip(aa;bb;cc);Fname] from t
id  seg| aa   bb  cc   Fname res
-------| ----------------------------------------------------------------
AAA 1  | 1500 150 0.4  Fx    329.68   550.671  698.8058 798.1035 864.6647
AAA 2  |      200 0.25 Fy    765.0526 1360.876 1824.904 2186.289 2467.737
AAA 3  |      30  0.35 Fy    2082.149 3549.414 4583.378 5312.001 5825.452
BBB 1  | 40   40  0.5  Fy    31.47755 50.56964 62.14959 69.17318 73.4332
CCC 1  | 900  10  0.35 Fz    26.57807 45.30732 58.5056  67.80627 74.36035
CCC 2  |      15  0.45 Fz    1.796406 2.941846 3.67221  4.137911 4.434855

我从你的问题中不清楚的是,是否允许“最后一个值”从一个 ID 溢出到另一个 ID。如果不应该,您可以简单地在我的解决方案中添加一个“by id”