【问题标题】:Finding time spent in continuous data查找在连续数据中花费的时间
【发布时间】:2021-03-18 15:12:53
【问题描述】:

我正在寻找在数据集中花费的总时间

我的数据集可能如下所示: (第一次建议后更新数据集)

Starting time Ending time
44224,32869 44224,33603
44224,30975 44224,33616
44224,30965 44224,32824
44223,34859 44223,46875
44223,41349 44223,44875

这将给我 9000 - 4000,50 - 500 的总时间(从没有时间在 5000 到 5500 之间工作)= 4499,5

找到最小开始时间和最大结束时间给了我这个集合的范围,如果下一个开始时间大于上一个结束时间,我可以从中减去数据(例如在5000到6000,减去1000) .但是,从最后一个数据点可以看出,这 1000 个减去的时间单位中的 500 个在总时间中已被使用。有没有什么简单的方法可以从这样的数据集中找到总花费的时间?在 VBA Excel 中编程。感谢您的宝贵时间!

目前正在使用此代码:

    For i = 7 to lastRow

            If timeEnd(i) < timeStart(i - 1) Then

                subtractTime = subtractTime + (timeStart(i - 1) - timeEnd(i))

            End If
            
            If timeStart(i) < firstTime Then

                firstTime = timeStart(i)
                
            End If
            
            
            If timeEnd(i) > lastTime Then
                
                lastTime = timeEnd(i)
                
            End If

            totalTimeSpent(i) = lastTime - firstTime - subtractTime

    Next i

    

找到适用于我的数据集的解决方案:首先对数据进行排序,使其按顺序排列,然后运行上面的代码。

【问题讨论】:

  • 请提供您的代码示例,以便我们为您提供帮助。
  • 已编辑,感谢评论。
  • 查看差距和孤岛分析。大多数引用都是针对 SQL 实现的,例如medium.com/analytics-vidhya/…,但我不久前做了一个基于公式的解决方案stackoverflow.com/questions/53572815/…。无论如何,第一步是按开始时间对数据进行排序,然后开始识别开始时间晚于迄今为止的最新结束时间的行(间隙)。
  • 已经起草了一个差距和孤岛解决方案 - 明天我整理一下后会发布。您修改后的测试数据似乎可以正常工作。

标签: excel vba


【解决方案1】:

这是很长的路要走,它将时间从最低到最高循环并计数:

    Function mytime(stRng As Range, edRng As Range)
        Dim stArr() As Variant
        stArr = Intersect(stRng.Parent.UsedRange, stRng).Value
        
        Dim edArr() As Variant
        edArr = Intersect(edRng.Parent.UsedRange, edRng).Value
        If UBound(stArr, 1) <> UBound(edArr, 1) Then Exit Function
        
        Dim cnt As Long
        cnt = 0
        
        For i = Application.Min(stArr) To Application.Max(edArr)
            For j = 1 To UBound(stArr, 1)
                If i >= stArr(j, 1) And i < edArr(j, 1) Then
                    cnt = cnt + 1
                    Exit For
                End If
            Next j
        Next i
        
        mytime = cnt
        
    End Function

【讨论】:

  • 嗨,Scott,这个集合的代码工作正常,但我正在使用连续值(应该在我的示例数据集中添加这些,我的错),例如 48953,53 到 48953,89。我将添加一个新的非简化数据集。感谢您的帮助!
【解决方案2】:

这是一个 Power Query 解决方案(在 Windows Excel 2010+ 和 Office 365 中可用)

它列出了列表中所有唯一条目的列表,这些条目跨越了所花费的时间;并返回计数。

有关详细信息,请参阅 M 代码和注释。也可以通过Applied Steps

M 码

let
    Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
    #"Changed Type" = Table.TransformColumnTypes(Source,{{"Starting time", Int64.Type}, {"Ending time", Int64.Type}}),

    //add a list of the times. Since we want duration, exclude the last time in the end time.
    #"Added Custom" = Table.AddColumn(#"Changed Type", "timeList", each {[Starting time]..[Ending time]-1}),

    //remove duplicates and count the remainder
    uniqueTimes = List.Count(
                    List.Distinct(
                        List.Combine(#"Added Custom"[timeList]))),

    //convert to a table for display
    #"Converted to Table" = #table({"Time Spent"}, {{uniqueTimes}})
in
    #"Converted to Table"

这是一个使用相同算法的 VBA UDF(用户定义函数):

Option Explicit
Function timeSpent(starts As Range, ends As Range) As Long
    Dim vStart, vEnd
    Dim I As Long, J As Long
    Dim colTimes As Collection
    
'Transfer ranges to vba arrays for faster processing
vStart = starts
vEnd = ends

'Sanity Check
If UBound(vStart) <> UBound(vEnd) Then
    MsgBox "Start and End time ranges must be the same size"
    Exit Function
End If

'create unique list of times

Set colTimes = New Collection

'A collection object will return an error if you try to add an item with a duplicate key
On Error Resume Next
For I = 1 To UBound(vStart, 1)
    For J = vStart(I, 1) To vEnd(I, 1) - 1
        colTimes.Add Item:=J, Key:=CStr(J)
    Next J
Next I
On Error GoTo 0
        
timeSpent = colTimes.Count

End Function

【讨论】:

  • 嗨,Ron,代码在这个集合中运行良好,但我正在使用连续值(应该在我的示例数据集中添加这些,我的错),例如 48953,53 到 48953,89。我将添加一个新的非简化数据集。感谢您的帮助!
  • @Henck 是的,在这种情况下需要使用更复杂的算法。
  • 找到了一个更简单的解决方案:先对数据进行排序,使其按顺序排列,然后运行原始代码。像魅力一样工作。
  • @Henck 如果您遇到问题,请回复。很高兴你有它的工作。
【解决方案3】:

gap and island 解决方案实际上是一段非常短的代码——大部分是输出。我正在使用 Nigel Heffernan 发布的quicksort

Option Base 1
Option Explicit


Sub GapAndIsland()

    Dim varData As Variant
    Dim minStart As Variant
    Dim maxEnd As Variant
    Dim i As Long
    Dim gap As Variant
    


    ' Set the array

    varData = Range("f2:g6").Value

    ' *** Modified code to remove bad data ***
    
    ' Remove elements that are not number or date

    Call RemoveInvalid(varData, lastUsed)

    ' Sort https://stackoverflow.com/questions/4873182/sorting-a-multidimensionnal-array-in-vba

    Call QuickSortArray(varData, 1, lastUsed, 1)

    ' *** end of modified code ***

    ' Initialise
      
    minStart = varData(1, 1)
    maxEnd = varData(1, 1)
    gap = 0
    
    ' Loop over rows
    ' *** This line also modified ***

    For i = 1 To lastUsed
    
    ' If there is a gap, increment total gap length
    
        If (varData(i, 1)) > maxEnd Then
            gap = gap + varData(i, 1) - maxEnd
        End If
        
    ' Update latest end time
        
        If varData(i, 2) > maxEnd Then
            maxEnd = varData(i, 2)
            
        End If
        
    Next i
    
' Output

    Range("I1:j6").Clear
    Range("I1:j1").Font.Bold = True
    Range("I1:J1").HorizontalAlignment = xlCenter
    Range("j2:j3").NumberFormat = "dd/mm/yyyy hh:mm:ss"
    Range("j4:j6").NumberFormat = "[h]:mm:ss"
    
    
    Range("I1").Value = "Measure"
    Range("J1").Value = "Value"
    
    Range("i2").Value = "Start"
    Range("j2").Value = minStart
    
    Range("i3").Value = "End"
    Range("j3").Value = maxEnd
    
    Range("i4").Value = "Duration"
    Range("j4").Value = maxEnd - minStart
    
    Range("i5").Value = "Gaps"
    Range("j5").Value = gap
    
    Range("i6").Value = "Net"
    Range("j6").Value = maxEnd - minStart - gap
    
End Sub

这使用与问题中相同的连续测试数据,但格式为日期时间:

这是一个 UDF 版本,它只返回任务的净时间。

Function TimeOnTask(R As Range) As Variant

    Dim varData As Variant
    Dim minStart As Variant
    Dim maxEnd As Variant
    Dim i As Long
    Dim gap As Variant
    


    ' Set the array

    varData = R.Value

    '  *** Modified code to remove bad data ***
    
    ' Remove elements that are not number or date

    Call RemoveInvalid(varData, lastUsed)

    ' Sort https://stackoverflow.com/questions/4873182/sorting-a-multidimensionnal-array-in-vba

    Call QuickSortArray(varData, 1, lastUsed, 1)

    ' *** end of modified code ***


    ' Initialise
      
    minStart = varData(1, 1)
    maxEnd = varData(1, 1)
    gap = 0
    
    ' Loop over rows

    ' *** This line also modified ***

    For i = 1 To lastUsed
    
    ' If there is a gap, increment total gap length
    
        If (varData(i, 1)) > maxEnd Then
            gap = gap + varData(i, 1) - maxEnd
        End If
        
    ' Update latest end time
        
        If varData(i, 2) > maxEnd Then
            maxEnd = varData(i, 2)
            
        End If
        
    Next i
    
    
    TimeOnTask = maxEnd - minStart - gap
    
End Function

** 编辑 **

我试图通过添加一个简短的例程来删除开始时间或停止时间不是数字或日期的行来使其更加健壮(另请参阅上面的修改代码):

Sub RemoveInvalid(ByRef arr As Variant, ByRef lastUsed As Long)
    Dim i As Long
    Dim j As Long
    
    j = 0
    
    For i = 1 To UBound(arr, 1)

    ' Increment and copy row if valid

        If (IsNumeric(arr(i, 1)) Or IsDate(arr(i, 1))) And Not (IsEmpty(arr(i, 1))) And _
           (IsNumeric(arr(i, 2)) Or IsDate(arr(i, 2))) And Not (IsEmpty(arr(i, 2))) Then
            j = j + 1
            arr(j, 1) = arr(i, 1)
            arr(j, 2) = arr(i, 2)
        End If
    Next i
    
    lastUsed = j
              
        
End Sub

没有必要重新调整数组,因为您可以在快速排序调用以及间隙和孤岛循环中指定最后使用的行。

【讨论】:

    猜你喜欢
    • 2018-05-22
    • 1970-01-01
    • 2019-05-26
    • 2023-02-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-08-06
    相关资源
    最近更新 更多