我在excel中有3个时间段-我需要知道最长连续时间段的持续时间

问题描述

请帮忙!

理想情况下,我真的只想使用公式来解决这个问题 - 而不是 VBA 或任何我认为“花哨”的东西。

我为一个为持续参与提供奖金的计划工作。我们有三个(有时更多)参与时间段,它们可能重叠和/或可能有不参与的空间。神奇的数字是连续订婚 84 天。我们一直在手动检查每条线路(数百条线路),以查看这些时间段加起来是否为 84 天的连续参与天数,没有不活动的时间段。

链接中有一张我们工作内容摘要图片。例如,第 3 行在 3 个时间段中的任何一个中都没有 84 天,但前 2 个时间段组合包括连续 120 天。日期不会按日期顺序出现 - 例如早期参与可能会列在第 3 期。

deletion

非常期待您的建议。

安妮

解决方法

不幸的是,Gap 和 Island 似乎不是初学者,因为我认为如果没有 VBA 或大量帮助列,您就无法使用它,而且开始日期需要按顺序排列。很遗憾,因为最长的连续任务时间(又名最大岛)很容易从 VBA 版本中删除,并且可以说它比 see this 下面的数组公式版本更容易理解。

转到选项 2,如果您有 Excel 365,则可以使用 Sequence 生成特定范围内的日期列表,然后检查每个日期是否属于这样的参与期之一:

=LET(array,SEQUENCE(Z$2-Z$1+1,1,Z$1),period1,(array>=A3)*(array<=C3),period2,(array>=E3)*(array<=G3),period3,(array>=I3)*(array<=K3),SUM(--(period1+period2+period3>0)))

假设 Z1 和 Z2 包含您感兴趣的日期范围的开始和结束(我使用了 1/1/21 和 31/7/21)。

如果您没有 Excel 365,则可以使用 Row 函数来生成日期列表。我建议使用名称管理器创建一个命名范围日期:

=INDEX(Sheet1!$A:$A,Sheet1!$Z$1):INDEX(Sheet1!$A:$A,Sheet1!$Z$2)

enter image description here

那么公式为:

= SUM(--(((ROW(Dates)>=A3) * (ROW(Dates)<=C3)  +( ROW(Dates)>=E3) * (ROW(Dates)<=G3) + (ROW(Dates)>=I3) * (ROW(Dates)<=K3))>0))

您可能需要使用 CtrlShiftEnter 或使用 Sumproduct 而不是 Sum 来输入。

enter image description here

编辑

正如@Qualia 敏锐地指出的那样,您希望持续 参与的时间最长。这可以通过将频率应用于第一个公式来找到:

=LET(array,onDays,period1+period2+period3>0,MAX(FREQUENCY(IF(onDays,array),IF(NOT(onDays),array)))
)

非365版本变成

=MAX(FREQUENCY(IF((ROW(Dates)>=A3)*(ROW(Dates)<=C3)+(ROW(Dates)>=E3)*(ROW(Dates)<=G3)+(ROW(Dates)>=I3)*(ROW(Dates)<=K3),ROW(Dates)),IF( NOT(  (ROW(Dates)>=A3)*(ROW(Dates)<=C3)+(ROW(Dates)>=E3)*(ROW(Dates)<=G3)+(ROW(Dates)>=I3)*(ROW(Dates)<=K3) ),ROW(Dates))))

enter image description here

,

@TomSharpe 向您展示了一种使用公式解决此问题的方法。如果您有三个以上的时间段,则必须对其进行修改。

不确定您是否认为 Power Query 解决方案“太花哨”,但它确实允许无限数量的时间段,如您在示例中所示。

有了 PQ,我们

  • 为每对开始/结束构建所有连续日期的列表
  • 合并每一行的列表,删除重复项
  • 对每一行的结果日期列表应用间隙和孤岛技术
  • 计算每个“岛”的条目数并返回最大值

请注意:我计算了开始日期和结束日期。在您的日子专栏中,您没有(除了一个实例)。如果您想同时计算两者,请保持代码不变;如果你不这样做,我们可以做一个小的修改

使用 Power Query

  • 创建一个表格,排除合并单元格的第一行
  • 按照我在屏幕截图中显示的格式重命名表格列,因为表格中的每个列标题必须具有不同的名称。
  • 选择该数据表中的某个单元格
  • Data => Get&Transform => from Table/Range
  • 当 PQ 编辑器打开时:Home => Advanced Editor
  • 记下第 2 行中的表名称
  • 将下面的 M 代码粘贴到您看到的位置
  • 将第 2 行中的表名改回最初生成的名称。
  • 阅读评论并探索Applied Steps以更好地理解算法

M 代码
编辑代码以对日期列表进行排序以处理某些情况

let
    Source = Excel.CurrentWorkbook(){[Name="Table2"]}[Content],#"Changed Type" = Table.TransformColumnTypes(Source,{{"Start P1",type datetime},{"Comment1",type text},{"End P1",{"Days 1",Int64.Type},{"Start P2",{"Comment2",{"End P2",{"Days 2",{"Start P3",{"Comment3",{"End P3",{"Days 3",Int64.Type}}),//set data types for columns 1/5/9... and 3/7/11/... as date
dtTypes = List.Transform(List.Alternate(Table.ColumnNames(#"Changed Type"),1),each {_,Date.Type}),typed = Table.TransformColumnTypes(#"Changed Type",dtTypes),//add Index column to define row numbers
rowNums = Table.AddIndexColumn(typed,"rowNum",//Unpivot except for rowNum column
    #"Unpivoted Other Columns" = Table.UnpivotOtherColumns(rowNums,{"rowNum"},"Attribute","Value"),//split the attribute column to filter on Start/End => just the dates
//then filter and remove the attributes columns
    #"Split Column by Delimiter" = Table.SplitColumn(#"Unpivoted Other Columns",Splitter.SplitTextByEachDelimiter({" "},QuoteStyle.Csv,false),{"Attribute.1","Attribute.2"}),#"Changed Type1" = Table.TransformColumnTypes(#"Split Column by Delimiter",{{"Attribute.1",{"Attribute.2",type text}}),#"Removed Columns" = Table.RemoveColumns(#"Changed Type1",{"Attribute.2"}),#"Filtered Rows" = Table.SelectRows(#"Removed Columns",each ([Attribute.1] = "End" or [Attribute.1] = "Start")),#"Removed Columns1" = Table.RemoveColumns(#"Filtered Rows",{"Attribute.1"}),#"Changed Type2" = Table.TransformColumnTypes(#"Removed Columns1",{{"Value",type date},{"rowNum",//group by row number
//generate date list from each pair of dates
//combine into a single list of dates with no overlapped date ranges for each row
    #"Grouped Rows" = Table.Group(#"Changed Type2",{
        {"dateList",(t)=> List.Sort(
            List.Distinct(
                List.Combine(
                    List.Generate(
                        ()=>[dtList=List.Dates(
                                t[Value]{0},Duration.TotalDays(t[Value]{1}-t[Value]{0})+1,#duration(1,0)),idx=0],each [idx] < Table.RowCount(t),each [dtList=List.Dates(
                                    t[Value]{[idx]+2},Duration.TotalDays(t[Value]{[idx]+3}-t[Value]{[idx]+2})+1,idx=[idx]+2],each [dtList]))))}
            }),//determine Islands and Gaps
    #"Expanded dateList" = Table.ExpandListColumn(#"Grouped Rows","dateList"),//Duplicate the date column and turn it into integers
    #"Duplicated Column" = Table.DuplicateColumn(#"Expanded dateList","dateList","dateList - Copy"),#"Changed Type3" = Table.TransformColumnTypes(#"Duplicated Column",{{"dateList - Copy",//add an Index column
//Then subtract the index from the integer date
// if the dates are consecutive the resultant ID column will => the same value,else it will jump
    #"Added Index" = Table.AddIndexColumn(#"Changed Type3","Index",Int64.Type),#"Added Custom" = Table.AddColumn(#"Added Index","ID",each [#"dateList - Copy"]-[Index]),#"Removed Columns2" = Table.RemoveColumns(#"Added Custom",{"dateList - Copy","Index"}),//Group by the date ID column and a Count will => the consecutive days
    #"Grouped Rows1" = Table.Group(#"Removed Columns2","ID"},{{"Count",each Table.RowCount(_),#"Removed Columns3" = Table.RemoveColumns(#"Grouped Rows1",{"ID"}),//Group by the Row number and return the Maximum Consecutive days
    #"Grouped Rows2" = Table.Group(#"Removed Columns3",{{"Max Consecutive Days",each List.Max([Count]),type number}}),//combine the Consecutive Days column with original table
    result = Table.Join(rowNums,#"Grouped Rows2","rowNum"),#"Removed Columns4" = Table.RemoveColumns(result,{"rowNum"})
in
    #"Removed Columns4"

enter image description here