【问题标题】:VBA Script to Extract data every 30s Interval每 30 秒间隔提取数据的 VBA 脚本
【发布时间】:2021-03-12 21:21:56
【问题描述】:

我有一个电压输出列表和记录它们的时间戳。对于某些背景,我的测试从 0-5V 每 30 秒将电压增加 1mV。系统每秒读取一次左右,可能不一致。该工作表有超过 70000 行,但我只需要超过 5000 行。

到目前为止,我已经使用 =RIGHT(TEXT(A1, "hh:mm:ss"),2) 从时间中提取秒数。不幸的是,时间戳总是每 30 秒一次,所以我不能简单地过滤每 0 秒和 30 秒。有时会跳过 30 秒,它会显示为 35 秒。

如何创建一个 VBA 脚本,以每 30 秒间隔提取增量,如果没有完美的 30 秒跳跃,请选择最接近的匹配项(即 29 或 31)?

任何指导将不胜感激!

【问题讨论】:

  • 我不认为你可以为此做一个直截了当的“过滤器”。但是,您可以编写一个 VBA 宏(或一个数组函数),它可以 1)将适当的 5000 行复制到不同的工作表,或 2)为应该选择/过滤的 70,000 行中的 5000 设置一个标志列为了。这可以接受吗?
  • 这正是我要找的:)
  • @RBarryYoung 你如何建议我完成这个?
  • 也许MRound 在这里有用。您可以将所有时间戳四舍五入为 30 或 0 的倍数,然后选择它们
  • 我认为这不会很好,因为如果有一个间隔已经有一个完美的 30 秒,它会将 29 舍入到 30 并扭曲我的最终行数......目标是能够将测试数据与理论数据相匹配,显示与实际数据相比的确切增量。

标签: excel vba


【解决方案1】:

好的,我认为以下将起作用。请注意,这需要作为 Excel 公式数组输入到 Flags 输出列中,函数的输入参数是包含时间的相应列。 (所以输出列的公式应该是:{=FlagEvery30Sec(D2:D36)}

' Returns 1 if the corresponding row should be used, and 0 otherwise
Public Function FlagEvery30Sec(SourceTimes As Range) As Integer()
    Dim Times() As Variant
    Dim Flags() As Long
    
    ' get the source values
    Times = SourceTimes
    
    ' set the output values array to the same size
    Dim First As Long, Last As Long
    First = LBound(Times, 1)
    Last = UBound(Times, 1)
    ReDim Flags(First To Last, 1 To 1)
    
    Dim curr As Long, prev As Long
    prev = First
    curr = First
    Flags(curr, 1) = 1
    curr = curr + 1
    
    Dim currTime As Date
    Dim currSecs As Double, prevSecs As Double, lastFlagSecs As Double
    lastFlagSecs = CDbl(Times(curr, 1)) * 24 * 60 * 60
    
    While curr <= Last
        Flags(curr, 1) = 0    ' assume not flagged, change later
        currSecs = CDbl(Times(curr, 1)) * 24 * 60 * 60
        If (currSecs - lastFlagSecs) >= 30 Then
            If ((currSecs - lastFlagSecs) - 30) <= (30 - (prevSecs - lastFlagSecs)) Then
                Flags(curr, 1) = 1
                lastFlagSecs = currSecs
            Else
                Flags(prev, 1) = 1
                lastFlagSecs = prevSecs
            End If
        End If
        
        prevSecs = currSecs
        prev = curr
        curr = curr + 1
    Wend
    
    FlagEvery30Sec = Flags
End Function

请注意,基本上有两种方法:

  1. 取第一个时间戳,每 30 秒标记一次,然后找到最接近每个标记的条目并标记它。或者,

  2. 获取第一个时间戳并找到最接近其后 30 秒的以下条目。然后将该条目的时间作为基本时间戳,并找到最接近其后 30 秒的下一个条目。重复这个直到你到达终点。

我上面的函数使用了第二种方法。您应该注意,这可能会发生偏差,最终结果可能比 TotalSeconds/30 多或少。

【讨论】:

  • 这正是我所需要的,非常感谢!我遇到的唯一问题是,如果我计算整个 70335 行,它会起作用并为每个标志提供“#VALUE”,我必须将其拆分为最多 30000 行的部分,然后粘贴到新工作表中。再次感谢!
  • 这确实需要使用Long 而不是Integer。否则,正如 @QuadeHowald 所经历的那样,如果 SourceTimes 包含超过 32,767 行,First = LBound(Times, 1)Last = UBound(Times, 1) 将引发溢出错误。
  • @BigBen 对,我忘了 VBA 中的整数是 16 位的。
  • @QuadeHowald 仅供参考,我已经更正了代码。
  • @RBarryYoung 输出的数据似乎有点偏,过滤后,我最终得到4998行数据,但我正在寻找5004。是否有可能每30s取消一次标记准时 0?所以增量 1 是时间 0 +30s,增量 2 是时间 0 + 60s,依此类推。也许这可能对此有所帮助...谢谢!
猜你喜欢
  • 2021-06-06
  • 2022-01-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-22
  • 2022-01-18
  • 2019-04-12
  • 1970-01-01
相关资源
最近更新 更多