ExcelTip.Net留存知识帖 ---【注:附件之前被网盘供应商清空后,现已修复-现已修复-现已修复为本地下载!】
现在位置:首页 > E问E答 > Excel VBA > 如何用vba代码对一曲线求出其包络线?

如何用vba代码对一曲线求出其包络线?

作者:绿色风 分类: 时间:2022-08-17 浏览:160
楼主
kevinchengcw
Q: 如何用vba代码对一曲线求出其包络线?
A: 思路如下:
根据循环源数据,根据数据前两项求出当前数据趋势方向(方向用于决定求取数据段的最大值还是最小值),根据当前加和值求出平均值,当数据方向为减少时,如果当前循环到的数据大于了当前的平均值,则认为数据减少段结束,从此段的起始位置到当前位置求出最小值及位置放入结果数组第一列;当数据方向为增加时,如果当前循环到的数据小于了当前的平均值,则认为数据增加段结束,从此段的起始位置到当前位置求出最大值及位置放入结果数组第二列。最后利用取得的各个结果之间的差与间隔数求出递增或递减的量,并逐一补进空白项中形成各数据点间的直线结果。
实现代码如下:
  1. Sub test()
  2. Dim Arr, Arrt(1 To 2) As Double, Result, N&, I&, A&, T#, Total#, Direct As Boolean, Dic As Object
  3. Arr = Range("b2:b" & Cells(Rows.Count, 2).End(3).Row).Value  '取得源数据
  4. ReDim Result(LBound(Arr) To UBound(Arr), 1 To 2)  '定义同样行数但两列的结果数组,第一列用于存储各数据减少段的最小值,第二列用于存储各数据增加段的最大值
  5. Set Dic = CreateObject("scripting.dictionary")  '创建字典项目用于存储增加或减少值对应的位置
  6. Direct = Arr(LBound(Arr), 1) >= Arr(LBound(Arr) + 1, 1)  '以最开始两个数据的对比确定初始化方向变量的值
  7. Total = 0  '初始化加和值为0
  8. I = LBound(Arr)  '初始化数据段起始点位置为数据第一项
  9. For N = LBound(Arr) To UBound(Arr) - 1  '循环数据段各项,因是两两比较,所以只循环到倒数第二项
  10.     Total = Total + Arr(N, 1)  '取得加和值
  11.     If Direct Then  '如果是减少的趋势,则
  12.         If Arr(N, 1) > Total / N Then  '如果当前项的值大于了平均值,则
  13.             Direct = Not Direct  '趋势反向(意味着当前趋势段结束)
  14.             Arrt(1) = Arr(I, 1)  '初始化本段最小值的数值为当前趋势的数据段第一项
  15.             Arrt(2) = I  '位置为当前趋势的数据段第一项的位置
  16.             For A = I + 1 To N - 1  '循环当前趋势段
  17.                 If Arr(A, 1) < Arrt(1) Then  '判断提取出最小值及位置赋值给数组
  18.                     Arrt(1) = Arr(A, 1)
  19.                     Arrt(2) = A
  20.                 End If
  21.             Next A
  22.             I = N  '数据段起始位置调整到当前位置
  23.             Result(Arrt(2), 1) = Arr(Arrt(2), 1)  '将最小值及位置结果数据写入结果数组
  24.             If Dic.exists(CStr(Direct)) Then  '将对应位置存入字典中(以趋势逻辑值转化为文本作为key来储存)
  25.                 Dic(CStr(Direct)) = Dic(CStr(Direct)) & vbTab & Arrt(2)
  26.             Else
  27.                 Dic.Add CStr(Direct), Arrt(2)
  28.             End If
  29.         End If
  30.     Else  '如果是增加的趋势,则操作基本同上,但找出的是最大值及位置
  31.         If Arr(N, 1) < Total / N Then
  32.             Direct = Not Direct
  33.             Arrt(1) = Arr(I, 1)
  34.             Arrt(2) = I
  35.             For A = I + 1 To N - 1
  36.                 If Arr(A, 1) > Arrt(1) Then
  37.                     Arrt(1) = Arr(A, 1)
  38.                     Arrt(2) = A
  39.                 End If
  40.             Next A
  41.             I = N
  42.             Result(Arrt(2), 2) = Arr(Arrt(2), 1)
  43.             If Dic.exists(CStr(Direct)) Then
  44.                 Dic(CStr(Direct)) = Dic(CStr(Direct)) & vbTab & Arrt(2)
  45.             Else
  46.                 Dic.Add CStr(Direct), Arrt(2)
  47.             End If
  48.         End If
  49.     End If
  50. Next N
  51. Direct = True  '提取出各个极值位置数据,取得其间的落差值及平均落差,依次补上数组空白项
  52. Arr = Split(Dic(CStr(Direct)), vbTab)
  53. For N = LBound(Arr) To UBound(Arr) - 1
  54.     If Val(Arr(N + 1)) - Val(Arr(N)) > 1 Then
  55.         T = Abs(Result(Val(Arr(N + 1)), 2) - Result(Val(Arr(N)), 2)) / (Val(Arr(N + 1)) - Val(Arr(N)))
  56.         For I = 1 To Val(Arr(N + 1)) - Val(Arr(N)) - 1
  57.             Result(Val(Arr(N)) + I, 2) = Result(Val(Arr(N)), 2) - I * T
  58.         Next I
  59.         If N = LBound(Arr) Then
  60.             For I = 1 To Val(Arr(N)) - 1
  61.                 Result(I, 2) = Result(Val(Arr(N)), 2) + T * (Val(Arr(N)) - I)
  62.             Next I
  63.         ElseIf N = UBound(Arr) - 1 Then
  64.             For I = Val(Arr(N + 1)) To UBound(Result)
  65.                 Result(I, 2) = Result(Val(Arr(N + 1)), 2) - T * (I - Val(Arr(N + 1)))
  66.             Next I
  67.         End If
  68.     End If
  69. Next N
  70. Direct = Not Direct
  71. Arr = Split(Dic(CStr(Direct)), vbTab)
  72. For N = LBound(Arr) To UBound(Arr) - 1
  73.     If Val(Arr(N + 1)) - Val(Arr(N)) > 1 Then
  74.         T = Abs(Result(Val(Arr(N + 1)), 1) - Result(Val(Arr(N)), 1)) / (Val(Arr(N + 1)) - Val(Arr(N)))
  75.         For I = 1 To Val(Arr(N + 1)) - Val(Arr(N)) - 1
  76.             Result(Val(Arr(N)) + I, 1) = Result(Val(Arr(N)), 1) - I * T
  77.         Next I
  78.         If N = LBound(Arr) Then
  79.             For I = 1 To Val(Arr(N)) - 1
  80.                 Result(I, 1) = Result(Val(Arr(N)), 1) + T * (Val(Arr(N)) - I)
  81.             Next I
  82.         ElseIf N = UBound(Arr) - 1 Then
  83.             For I = Val(Arr(N + 1)) To UBound(Result)
  84.                 Result(I, 1) = Result(Val(Arr(N + 1)), 1) - T * (I - Val(Arr(N + 1)))
  85.             Next I
  86.         End If
  87.     End If
  88. Next N
  89. [c2].Resize(UBound(Result), 2) = Result  '结果数组写入工作表
  90. End Sub
以上代码在写时为了逻辑清晰,未进行优化,同时平均曲线方式会忽略掉小的波动,从而从大的趋势上得到几个极值的位置.
以上代码及思路希望大家进一步补充.
详见附件及素材源帖.
Demo.rar
2楼
海洋之星
经悍的K哥,写那么长的代码

免责声明

有感于原ExcelTip.Net留存知识的价值及部分知识具有的时间限定性因素, 经与ExcelTip.Net站长Apolloh商议并征得其同意, 现将原属ExcelTip.Net的知识帖采集资料于本站点进行展示, 供有需要的人士查询使用,也慰缅曾经的论坛时代。 所示各个帖子的原作者如对版权有异议, 可与本人沟通提出,或于本站点留言,我们会尽快处理。 在此,感谢ExcelTip.Net站长Apolloh的支持,感谢本站点所有人**绿色风(QQ:79664738)**的支持与奉献,特此鸣谢!
------本人网名**KevinChengCW(QQ:1210618015)**原ExcelTip.Net总版主之一

评论列表
sitemap