最近从前一直使用的累加套路开始会给出超栈警告了,优化了一下后发现移动平均也适用而且运行时间还特别少,所以感觉有必要和大家分享一下。以下为优化后的累加代码,DB为拥有唯一列的表格:
let Source = List.Buffer(DB[Val]), Cnt = List.Count(Source), cSum = List.Generate( ()=>[Pre_Val = Source{0}, n = 0], each [n] ﹤ Cnt, each [ Pre_Val = [Pre_Val] + Source{n}, n = [n] + 1 ], each [Pre_Val] ), RSL = Table.FromColumns( { Source, cSum }, type table [Val = number, Cum = number] ) in RSL
以上代码的大意为
(1)把DB表格中的Val字段化为列表并通过List.Buffer()缓冲至内存中,结果存至Source中;
(2)通过List.Count()数Source的项目总数;
(3)通过List.Generate()完成累加的过程,第一次循环会把Source的第一个数作为结果,第二次循环会把上一次循环的结果加上Source的第二个数据作为结果,如此反复直到第二个参数不被满足(第一个参数会跳过第三个参数的指示, 进行第二个参数的判断,如果不满足条件会跳出循环,否则完成第个四参数的指示并输出结果,但从第二次循环开始会完成第三个参数的指示后才把结果传递到第二参数进行判断,如果满足条件会完成第四参数的指示并输出结果,直至第二个参数不再满足前不断重复);
(4)通过Table.FromColumns()把Souce和cSum还原为表格
以上代码即使在百万级的数据下,运行时间也不会超过10秒(第八代i7)且不会超栈,关键在于不像List.Accumulate()一定要在累加的过程中把结果不断拼接至容器中。以下为移动平均的演示代码,由于与以上代码高度类似,就不再赘述了,只提供百万级数据下的运行时间(<10秒)供参考:
let Offset = 5, Vals = List.Buffer(DB[Val]), Cnt = List.Count(Vals), Head = List.FirstN(Vals, Offset), InitialSum = List.Sum(Head), Avgs = List.Generate( ()=> [ Sum = InitialSum, n = Offset - 1 ], each [n] ﹤ Cnt, each [ Sum = [Sum] - Vals{n - Offset} + Vals{n}, n = [n] + 1 ], each [Sum] / Offset ), Final_Avgs = List.Repeat({null}, Offset - 1) ﹠ Avgs, Outcome = Table.FromColumns( { Vals, Final_Avgs }, type table [Val = number, mAvg = number] ) in Outcome