Excel 将重复列重新构造为行VBA

Excel 将重复列重新构造为行VBA,excel,vba,Excel,Vba,我有一个广泛的数据集,由标识符和一系列20组重复列组成,每组中有相同的8列。我想将这些数据重新组织成行,这样每个重复的标识符和每个8序列代表一个唯一的行 我有一些代码可以让我大部分时间都在那里,如果我只在标识符和前两列上运行它,它就会工作。它不会通过每组的第3-8列。下面是在每个组的前两列上成功运行的代码 Sub StackData() Dim Key, Dic As Object, cl As Range, Data As Range, i&, n& Set

我有一个广泛的数据集,由标识符和一系列20组重复列组成,每组中有相同的8列。我想将这些数据重新组织成行,这样每个重复的标识符和每个8序列代表一个唯一的行

我有一些代码可以让我大部分时间都在那里,如果我只在标识符和前两列上运行它,它就会工作。它不会通过每组的第3-8列。下面是在每个组的前两列上成功运行的代码

Sub StackData()
    Dim Key, Dic As Object, cl As Range, Data As Range, i&, n&
    Set Dic = CreateObject("Scripting.Dictionary")
    Dic.CompareMode = vbTextCompare
    i = Cells(Rows.Count, "A").End(xlUp).Row
    n = 1
    Set Data = Range("F2:F" & i & "," & "N2:N" & i & "," & "V2:V" & i & "," & "AD2:AD" & i & "," & "AL2:AL" & i & "," & "AT2:AT" & i & "," & "BB2:BB" & i & "," & "BJ2:BJ" & i & "," & "BR2:BR" & i & "," & "BZ2:BZ" & i & "," & "CH2:CH" & i & "," & "CP2:CP" & i & "," & "CX2:CX" & i & "," & "DF2:DF" & i & "," & "DN2:DN" & i & "," & "DV2:DV" & i & "," & "ED2:ED" & i & "," & "EL2:EL" & i & "," & "ET2:ET" & i & "," & "FB2:FB" & i)
    Dic.Add "|Name", "Var1|Var2|Var3|Var4|Var5|Var6|Var7|Var8"
    For Each cl In Data
        If Cells(cl.Row, "A") <> "" Then
            Dic.Add n & "|" & Cells(cl.Row, "A"), cl.Text & "|" & cl.Offset(, 1).Text
            n = n + 1
        End If
    Next cl
    n = 1
    For Each Key In Dic
        Worksheets("Worksheet").Cells(n, "A") = Split(Key, "|")(1)
        Worksheets("Worksheet").Cells(n, "B") = Split(Dic(Key), "|")(0)
        Worksheets("Worksheet").Cells(n, "C") = Split(Dic(Key), "|")(1)
        n = n + 1
    Next Key
End Sub
子堆栈数据()
尺寸键,Dic作为对象,cl作为范围,数据作为范围,i&,n&
设置Dic=CreateObject(“Scripting.Dictionary”)
Dic.CompareMode=vbTextCompare
i=单元格(Rows.Count,“A”)。结束(xlUp)。行
n=1
“2.2:V”和“i&”,以及“2.2:V”和“我和我”和“2.2:V”和“我和,”以及“AD2:AD和i&”,以及“2:AL和2:AL和i&,”和“2:F和i&”和“2:F”和“F-2:F”和“F-2:F”和数据范围范围的范围,包括包括范围范围,包括:F-2:AD和我和我和我的范围范围范围,以及各种各种数据,以及“F-2:F和我和我和我和我和我的范围,以及“F”和“F-2:F”和我和我和我和我和我和我,以及我,以及“2:F”和我和我,以及我,以及“2:A:F和我和我,以及我,以及“2:A:A.2:L和我和我和我和我,以及我,以及我,以及我,以及我,以及我,以及DN“&i&“,”和“DV2:DV”&i&“,”和“ED2:ED”&i&“,”和“EL2:EL”&i&“,”和“ET2:ET”&i&“,”和“FB2:FB”&i)
Dic.添加“|名称”、“Var1 | Var2 | Var3 | Var4 | Var5 | Var6 | Var7 | Var8”
对于数据中的每个cl
如果单元格(第1行,“A”)“则
Dic.添加n&“|”和单元格(第1行,“A”)、第1行文本和第1行偏移(,1).Text
n=n+1
如果结束
下一个cl
n=1
对于Dic中的每个键
工作表(“工作表”)。单元格(n,“A”)=拆分(键“|”)(1)
工作表(“工作表”)。单元格(n,“B”)=拆分(Dic(键),“|”)(0)
工作表(“工作表”)。单元格(n,“C”)=拆分(Dic(键),“|”)(1)
n=n+1
下一键
端接头

当我添加到“For each Key in Dic”时,我得到一个错误。任何关于我做错了什么的输入?也可以使用不同的方法来解决这个问题,这可能比这个笨拙的方法更简洁。

您也可以使用Excel 2010中提供的Power Query来解决这个问题+

在下面的代码中,我使用了您提供的示例。 您需要进行一些更改以使其适应您的实际数据

例如:

  • 在代码中,我选择了前两列,取消了其他列;在实际数据中,您必须选择前三列

  • let
        Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
        #"Unpivoted Other Columns" = Table.UnpivotOtherColumns(Source, {"Name", "ID"}, "Attribute", "Value"),
        #"Added Index" = Table.AddIndexColumn(#"Unpivoted Other Columns", "Index", 0, 1),
        #"Inserted Integer-Division" = Table.AddColumn(#"Added Index", "Integer-Division", each Number.IntegerDivide([Index], 3), Int64.Type),
        #"Removed Columns" = Table.RemoveColumns(#"Inserted Integer-Division",{"Attribute", "Index"}),
        #"Grouped Rows" = Table.Group(#"Removed Columns", {"Integer-Division"}, {{"Grouped", each _, type table [Name=text, ID=number, Value=text, #"Integer-Division"=number]}}),
        #"Added Custom" = Table.AddColumn(#"Grouped Rows", "Name", each List.First(Table.Column([Grouped],"Name"))),
        #"Added Custom1" = Table.AddColumn(#"Added Custom", "ID", each List.First(Table.Column([Grouped],"ID"))),
        #"Added Custom2" = Table.AddColumn(#"Added Custom1", "Custom", each Table.Column([Grouped],"Value")),
        #"Extracted Values" = Table.TransformColumns(#"Added Custom2", {"Custom", each Text.Combine(List.Transform(_, Text.From), ";"), type text}),
        #"Split Column by Delimiter" = Table.SplitColumn(#"Extracted Values", "Custom", Splitter.SplitTextByDelimiter(";", QuoteStyle.Csv), {"Custom.1", "Custom.2", "Custom.3"}),
        #"Changed Type" = Table.TransformColumnTypes(#"Split Column by Delimiter",{{"Custom.1", type text}, {"Custom.2", type text}, {"Custom.3", type text}}),
        #"Removed Columns1" = Table.RemoveColumns(#"Changed Type",{"Integer-Division", "Grouped"}),
    
        //Rename last columns
            origColNames =List.Buffer(List.Range(Table.ColumnNames(#"Removed Columns1"),2)),
            newNameNum =  List.Generate(() => 1 , each _ <=List.Count(origColNames), each _ + 1),
    
        //There has to be a better way to convert the numbers to strings
            #"Converted to Table" = Table.FromList(newNameNum, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
            #"Changed Type1" = Table.TransformColumnTypes(#"Converted to Table",{{"Column1", type text}}),
    
            newNames = Table.Column(#"Changed Type1","Column1"),
            renameCols = Table.RenameColumns(#"Removed Columns1",List.Zip({origColNames,newNames}))
    
    in 
        renameCols
    
    Sub stackData()
        ' Error Handler
        Const Proc As String = "stackData"
        On Error GoTo cleanError
    
        ' Source
        Const srcName As String = "Sheet1"      ' Worksheet Name
        Const srcFirst As String = "A2"         ' First Cell Address
        Const LRCol As Long = 1                 ' Last Row Column Number
        Const IdentCols As Long = 3             ' Number of Identifier Columns
        Const GroupCols As Long = 8             ' Number of Group Columns
        Const GroupsCount As Long = 20          ' Number of Groups
        ' Target
        Const tgtName As String = "Sheet2"      ' Worksheet Name
        Const tgtFirst As String = "A2"         ' First Cell Address
        ' Workbook
        Dim wb As Workbook: Set wb = ThisWorkbook
    
        ' Write values of Source Range to Source Array.
        Dim ws As Worksheet: Set ws = wb.Worksheets(srcName)
        Dim rng As Range
        Set rng = ws.Columns(LRCol).Find("*", , xlValues, , , xlPrevious)
        If rng Is Nothing Then Exit Sub
        If rng.row < ws.Range(srcFirst).row Then Exit Sub
        Dim LastRow As Long: LastRow = rng.row
        Set rng = Nothing
        Dim LastCol As Long: LastCol = IdentCols + GroupCols * GroupsCount
        Dim Source As Variant
        Source = ws.Range(ws.Range(srcFirst), ws.Cells(LastRow, LastCol))
        Set ws = Nothing
        Dim ubS As Long: ubS = UBound(Source)
    
        ' Write values of Source Array to Target Array.
        Dim Target As Variant
        ReDim Target(1 To ubS * GroupsCount, 1 To IdentCols + GroupCols)
        Dim i As Long, j As Long, k As Long, m As Long
        GoSub writeIdentifiers
        GoSub writeGroups
    
        ' Write values of Target Array to Target Range.
        Set ws = wb.Worksheets(tgtName)
        ws.Range(tgtFirst).Resize(UBound(Target), UBound(Target, 2)).Value = Target
    
        ' Inform user.
        MsgBox "Data stacked.", vbInformation, "Success"
    
        Exit Sub
    
    ' Subroutines
    writeIdentifiers:
        m = 1
        For i = 1 To ubS
            For j = 1 To GroupsCount
                For k = 1 To IdentCols
                    Target(m, k) = Source(i, k)
                Next k
                m = m + 1
            Next j
        Next i
        Return
    
    writeGroups:
        m = 1
        For i = 1 To ubS
            For j = 1 To GroupsCount
                For k = 1 To GroupCols
                    Target(m, k + IdentCols) = _
                      Source(i, k + IdentCols + (j - 1) * GroupCols)
                Next k
                m = m + 1
            Next j
        Next i
        Return
    
    ' Error Handler
    cleanError:
        MsgBox "An unexpected error occurred in '" & Proc & "'." & vbCr _
             & "Run-time error '" & Err.Number & "':" & vbCr & Err.Description _
               , vbCritical, Proc & " Error"
    End Sub
    
  • 对于整数/除法列,我除以3;您可能需要除以8

  • 在代码中,我添加了三个自定义列(每个标识符列一个,其余一个)。您需要添加四个自定义列

无论如何:

MCode

let
    Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
    #"Unpivoted Other Columns" = Table.UnpivotOtherColumns(Source, {"Name", "ID"}, "Attribute", "Value"),
    #"Added Index" = Table.AddIndexColumn(#"Unpivoted Other Columns", "Index", 0, 1),
    #"Inserted Integer-Division" = Table.AddColumn(#"Added Index", "Integer-Division", each Number.IntegerDivide([Index], 3), Int64.Type),
    #"Removed Columns" = Table.RemoveColumns(#"Inserted Integer-Division",{"Attribute", "Index"}),
    #"Grouped Rows" = Table.Group(#"Removed Columns", {"Integer-Division"}, {{"Grouped", each _, type table [Name=text, ID=number, Value=text, #"Integer-Division"=number]}}),
    #"Added Custom" = Table.AddColumn(#"Grouped Rows", "Name", each List.First(Table.Column([Grouped],"Name"))),
    #"Added Custom1" = Table.AddColumn(#"Added Custom", "ID", each List.First(Table.Column([Grouped],"ID"))),
    #"Added Custom2" = Table.AddColumn(#"Added Custom1", "Custom", each Table.Column([Grouped],"Value")),
    #"Extracted Values" = Table.TransformColumns(#"Added Custom2", {"Custom", each Text.Combine(List.Transform(_, Text.From), ";"), type text}),
    #"Split Column by Delimiter" = Table.SplitColumn(#"Extracted Values", "Custom", Splitter.SplitTextByDelimiter(";", QuoteStyle.Csv), {"Custom.1", "Custom.2", "Custom.3"}),
    #"Changed Type" = Table.TransformColumnTypes(#"Split Column by Delimiter",{{"Custom.1", type text}, {"Custom.2", type text}, {"Custom.3", type text}}),
    #"Removed Columns1" = Table.RemoveColumns(#"Changed Type",{"Integer-Division", "Grouped"}),

    //Rename last columns
        origColNames =List.Buffer(List.Range(Table.ColumnNames(#"Removed Columns1"),2)),
        newNameNum =  List.Generate(() => 1 , each _ <=List.Count(origColNames), each _ + 1),

    //There has to be a better way to convert the numbers to strings
        #"Converted to Table" = Table.FromList(newNameNum, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
        #"Changed Type1" = Table.TransformColumnTypes(#"Converted to Table",{{"Column1", type text}}),

        newNames = Table.Column(#"Changed Type1","Column1"),
        renameCols = Table.RenameColumns(#"Removed Columns1",List.Zip({origColNames,newNames}))

in 
    renameCols
Sub stackData()
    ' Error Handler
    Const Proc As String = "stackData"
    On Error GoTo cleanError

    ' Source
    Const srcName As String = "Sheet1"      ' Worksheet Name
    Const srcFirst As String = "A2"         ' First Cell Address
    Const LRCol As Long = 1                 ' Last Row Column Number
    Const IdentCols As Long = 3             ' Number of Identifier Columns
    Const GroupCols As Long = 8             ' Number of Group Columns
    Const GroupsCount As Long = 20          ' Number of Groups
    ' Target
    Const tgtName As String = "Sheet2"      ' Worksheet Name
    Const tgtFirst As String = "A2"         ' First Cell Address
    ' Workbook
    Dim wb As Workbook: Set wb = ThisWorkbook

    ' Write values of Source Range to Source Array.
    Dim ws As Worksheet: Set ws = wb.Worksheets(srcName)
    Dim rng As Range
    Set rng = ws.Columns(LRCol).Find("*", , xlValues, , , xlPrevious)
    If rng Is Nothing Then Exit Sub
    If rng.row < ws.Range(srcFirst).row Then Exit Sub
    Dim LastRow As Long: LastRow = rng.row
    Set rng = Nothing
    Dim LastCol As Long: LastCol = IdentCols + GroupCols * GroupsCount
    Dim Source As Variant
    Source = ws.Range(ws.Range(srcFirst), ws.Cells(LastRow, LastCol))
    Set ws = Nothing
    Dim ubS As Long: ubS = UBound(Source)

    ' Write values of Source Array to Target Array.
    Dim Target As Variant
    ReDim Target(1 To ubS * GroupsCount, 1 To IdentCols + GroupCols)
    Dim i As Long, j As Long, k As Long, m As Long
    GoSub writeIdentifiers
    GoSub writeGroups

    ' Write values of Target Array to Target Range.
    Set ws = wb.Worksheets(tgtName)
    ws.Range(tgtFirst).Resize(UBound(Target), UBound(Target, 2)).Value = Target

    ' Inform user.
    MsgBox "Data stacked.", vbInformation, "Success"

    Exit Sub

' Subroutines
writeIdentifiers:
    m = 1
    For i = 1 To ubS
        For j = 1 To GroupsCount
            For k = 1 To IdentCols
                Target(m, k) = Source(i, k)
            Next k
            m = m + 1
        Next j
    Next i
    Return

writeGroups:
    m = 1
    For i = 1 To ubS
        For j = 1 To GroupsCount
            For k = 1 To GroupCols
                Target(m, k + IdentCols) = _
                  Source(i, k + IdentCols + (j - 1) * GroupCols)
            Next k
            m = m + 1
        Next j
    Next i
    Return

' Error Handler
cleanError:
    MsgBox "An unexpected error occurred in '" & Proc & "'." & vbCr _
         & "Run-time error '" & Err.Number & "':" & vbCr & Err.Description _
           , vbCritical, Proc & " Error"
End Sub
let
Source=Excel.CurrentWorkbook(){[Name=“Table1”]}[Content],
#“unpivottothercolumns”=表.UnpivotOtherColumns(源,{“名称”,“ID”},“属性”,“值”),
#“添加的索引”=表的AddIndexColumn(#“取消插入其他列”,“索引”,0,1),
#“插入的整数除法”=Table.AddColumn(#“添加的索引”、“整数除法”、每个数字.IntegerDivide([Index],3)、Int64.Type),
#“删除的列”=Table.RemoveColumns(#“插入的整数除法”、{“属性”、“索引”}),
#“分组行”=Table.Group(#“已删除列”、{“整型分割”}、{{“分组”,每个#,键入Table[Name=text,ID=number,Value=text,#“整型分割”=number]}),
#“添加的自定义”=Table.AddColumn(#“分组行”、“名称”,每个列表)。首先(Table.Column([分组],“名称”)),
#“Added Custom1”=Table.AddColumn(#“Added Custom”,“ID”,每个列表。首先(Table.Column([分组],“ID”)),
#“添加的Custom2”=Table.AddColumn(#“添加的Custom1”、“Custom”、每个Table.Column([分组]、“值”)),
#“提取的值”=Table.TransformColumns(#“Added Custom2”,{“Custom”,每个Text.Combine(List.Transform(#,Text.From),“;”),键入Text}),
#“按分隔符拆分列”=表.SplitColumn(#“提取值”、“自定义”、拆分器.SplitTextByDelimiter(“;”,QuoteStyle.Csv)、{“自定义.1”、“自定义.2”、“自定义.3”}),
#“更改的类型”=表.TransformColumnTypes(#“按分隔符拆分列”、{{“Custom.1”、类型text}、{“Custom.2”、类型text}、{“Custom.3”、类型text}),
#“Removed Columns1”=Table.RemoveColumns(#“Changed Type”、{“Integer Division”、“Grouped”}),
//重命名最后一列
origColNames=List.Buffer(List.Range(Table.ColumnNames(#“Removed Columns1”),2)),

newNameNum=List.Generate(()=>1,每个p>您也可以使用Excel 2010中提供的Power Query执行此操作+

在下面的代码中,我使用了您提供的示例。 您需要进行一些更改以使其适应您的实际数据

例如:

  • 在代码中,我选择了前两列,取消了其他列;在实际数据中,您必须选择前三列

  • let
        Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
        #"Unpivoted Other Columns" = Table.UnpivotOtherColumns(Source, {"Name", "ID"}, "Attribute", "Value"),
        #"Added Index" = Table.AddIndexColumn(#"Unpivoted Other Columns", "Index", 0, 1),
        #"Inserted Integer-Division" = Table.AddColumn(#"Added Index", "Integer-Division", each Number.IntegerDivide([Index], 3), Int64.Type),
        #"Removed Columns" = Table.RemoveColumns(#"Inserted Integer-Division",{"Attribute", "Index"}),
        #"Grouped Rows" = Table.Group(#"Removed Columns", {"Integer-Division"}, {{"Grouped", each _, type table [Name=text, ID=number, Value=text, #"Integer-Division"=number]}}),
        #"Added Custom" = Table.AddColumn(#"Grouped Rows", "Name", each List.First(Table.Column([Grouped],"Name"))),
        #"Added Custom1" = Table.AddColumn(#"Added Custom", "ID", each List.First(Table.Column([Grouped],"ID"))),
        #"Added Custom2" = Table.AddColumn(#"Added Custom1", "Custom", each Table.Column([Grouped],"Value")),
        #"Extracted Values" = Table.TransformColumns(#"Added Custom2", {"Custom", each Text.Combine(List.Transform(_, Text.From), ";"), type text}),
        #"Split Column by Delimiter" = Table.SplitColumn(#"Extracted Values", "Custom", Splitter.SplitTextByDelimiter(";", QuoteStyle.Csv), {"Custom.1", "Custom.2", "Custom.3"}),
        #"Changed Type" = Table.TransformColumnTypes(#"Split Column by Delimiter",{{"Custom.1", type text}, {"Custom.2", type text}, {"Custom.3", type text}}),
        #"Removed Columns1" = Table.RemoveColumns(#"Changed Type",{"Integer-Division", "Grouped"}),
    
        //Rename last columns
            origColNames =List.Buffer(List.Range(Table.ColumnNames(#"Removed Columns1"),2)),
            newNameNum =  List.Generate(() => 1 , each _ <=List.Count(origColNames), each _ + 1),
    
        //There has to be a better way to convert the numbers to strings
            #"Converted to Table" = Table.FromList(newNameNum, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
            #"Changed Type1" = Table.TransformColumnTypes(#"Converted to Table",{{"Column1", type text}}),
    
            newNames = Table.Column(#"Changed Type1","Column1"),
            renameCols = Table.RenameColumns(#"Removed Columns1",List.Zip({origColNames,newNames}))
    
    in 
        renameCols
    
    Sub stackData()
        ' Error Handler
        Const Proc As String = "stackData"
        On Error GoTo cleanError
    
        ' Source
        Const srcName As String = "Sheet1"      ' Worksheet Name
        Const srcFirst As String = "A2"         ' First Cell Address
        Const LRCol As Long = 1                 ' Last Row Column Number
        Const IdentCols As Long = 3             ' Number of Identifier Columns
        Const GroupCols As Long = 8             ' Number of Group Columns
        Const GroupsCount As Long = 20          ' Number of Groups
        ' Target
        Const tgtName As String = "Sheet2"      ' Worksheet Name
        Const tgtFirst As String = "A2"         ' First Cell Address
        ' Workbook
        Dim wb As Workbook: Set wb = ThisWorkbook
    
        ' Write values of Source Range to Source Array.
        Dim ws As Worksheet: Set ws = wb.Worksheets(srcName)
        Dim rng As Range
        Set rng = ws.Columns(LRCol).Find("*", , xlValues, , , xlPrevious)
        If rng Is Nothing Then Exit Sub
        If rng.row < ws.Range(srcFirst).row Then Exit Sub
        Dim LastRow As Long: LastRow = rng.row
        Set rng = Nothing
        Dim LastCol As Long: LastCol = IdentCols + GroupCols * GroupsCount
        Dim Source As Variant
        Source = ws.Range(ws.Range(srcFirst), ws.Cells(LastRow, LastCol))
        Set ws = Nothing
        Dim ubS As Long: ubS = UBound(Source)
    
        ' Write values of Source Array to Target Array.
        Dim Target As Variant
        ReDim Target(1 To ubS * GroupsCount, 1 To IdentCols + GroupCols)
        Dim i As Long, j As Long, k As Long, m As Long
        GoSub writeIdentifiers
        GoSub writeGroups
    
        ' Write values of Target Array to Target Range.
        Set ws = wb.Worksheets(tgtName)
        ws.Range(tgtFirst).Resize(UBound(Target), UBound(Target, 2)).Value = Target
    
        ' Inform user.
        MsgBox "Data stacked.", vbInformation, "Success"
    
        Exit Sub
    
    ' Subroutines
    writeIdentifiers:
        m = 1
        For i = 1 To ubS
            For j = 1 To GroupsCount
                For k = 1 To IdentCols
                    Target(m, k) = Source(i, k)
                Next k
                m = m + 1
            Next j
        Next i
        Return
    
    writeGroups:
        m = 1
        For i = 1 To ubS
            For j = 1 To GroupsCount
                For k = 1 To GroupCols
                    Target(m, k + IdentCols) = _
                      Source(i, k + IdentCols + (j - 1) * GroupCols)
                Next k
                m = m + 1
            Next j
        Next i
        Return
    
    ' Error Handler
    cleanError:
        MsgBox "An unexpected error occurred in '" & Proc & "'." & vbCr _
             & "Run-time error '" & Err.Number & "':" & vbCr & Err.Description _
               , vbCritical, Proc & " Error"
    End Sub
    
  • 对于整数/除法列,我除以3;您可能需要除以8

  • 在代码中,我添加了三个自定义列(每个标识符列一个,其余一个)。您需要添加四个自定义列

无论如何:

MCode

let
    Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
    #"Unpivoted Other Columns" = Table.UnpivotOtherColumns(Source, {"Name", "ID"}, "Attribute", "Value"),
    #"Added Index" = Table.AddIndexColumn(#"Unpivoted Other Columns", "Index", 0, 1),
    #"Inserted Integer-Division" = Table.AddColumn(#"Added Index", "Integer-Division", each Number.IntegerDivide([Index], 3), Int64.Type),
    #"Removed Columns" = Table.RemoveColumns(#"Inserted Integer-Division",{"Attribute", "Index"}),
    #"Grouped Rows" = Table.Group(#"Removed Columns", {"Integer-Division"}, {{"Grouped", each _, type table [Name=text, ID=number, Value=text, #"Integer-Division"=number]}}),
    #"Added Custom" = Table.AddColumn(#"Grouped Rows", "Name", each List.First(Table.Column([Grouped],"Name"))),
    #"Added Custom1" = Table.AddColumn(#"Added Custom", "ID", each List.First(Table.Column([Grouped],"ID"))),
    #"Added Custom2" = Table.AddColumn(#"Added Custom1", "Custom", each Table.Column([Grouped],"Value")),
    #"Extracted Values" = Table.TransformColumns(#"Added Custom2", {"Custom", each Text.Combine(List.Transform(_, Text.From), ";"), type text}),
    #"Split Column by Delimiter" = Table.SplitColumn(#"Extracted Values", "Custom", Splitter.SplitTextByDelimiter(";", QuoteStyle.Csv), {"Custom.1", "Custom.2", "Custom.3"}),
    #"Changed Type" = Table.TransformColumnTypes(#"Split Column by Delimiter",{{"Custom.1", type text}, {"Custom.2", type text}, {"Custom.3", type text}}),
    #"Removed Columns1" = Table.RemoveColumns(#"Changed Type",{"Integer-Division", "Grouped"}),

    //Rename last columns
        origColNames =List.Buffer(List.Range(Table.ColumnNames(#"Removed Columns1"),2)),
        newNameNum =  List.Generate(() => 1 , each _ <=List.Count(origColNames), each _ + 1),

    //There has to be a better way to convert the numbers to strings
        #"Converted to Table" = Table.FromList(newNameNum, Splitter.SplitByNothing(), null, null, ExtraValues.Error),
        #"Changed Type1" = Table.TransformColumnTypes(#"Converted to Table",{{"Column1", type text}}),

        newNames = Table.Column(#"Changed Type1","Column1"),
        renameCols = Table.RenameColumns(#"Removed Columns1",List.Zip({origColNames,newNames}))

in 
    renameCols
Sub stackData()
    ' Error Handler
    Const Proc As String = "stackData"
    On Error GoTo cleanError

    ' Source
    Const srcName As String = "Sheet1"      ' Worksheet Name
    Const srcFirst As String = "A2"         ' First Cell Address
    Const LRCol As Long = 1                 ' Last Row Column Number
    Const IdentCols As Long = 3             ' Number of Identifier Columns
    Const GroupCols As Long = 8             ' Number of Group Columns
    Const GroupsCount As Long = 20          ' Number of Groups
    ' Target
    Const tgtName As String = "Sheet2"      ' Worksheet Name
    Const tgtFirst As String = "A2"         ' First Cell Address
    ' Workbook
    Dim wb As Workbook: Set wb = ThisWorkbook

    ' Write values of Source Range to Source Array.
    Dim ws As Worksheet: Set ws = wb.Worksheets(srcName)
    Dim rng As Range
    Set rng = ws.Columns(LRCol).Find("*", , xlValues, , , xlPrevious)
    If rng Is Nothing Then Exit Sub
    If rng.row < ws.Range(srcFirst).row Then Exit Sub
    Dim LastRow As Long: LastRow = rng.row
    Set rng = Nothing
    Dim LastCol As Long: LastCol = IdentCols + GroupCols * GroupsCount
    Dim Source As Variant
    Source = ws.Range(ws.Range(srcFirst), ws.Cells(LastRow, LastCol))
    Set ws = Nothing
    Dim ubS As Long: ubS = UBound(Source)

    ' Write values of Source Array to Target Array.
    Dim Target As Variant
    ReDim Target(1 To ubS * GroupsCount, 1 To IdentCols + GroupCols)
    Dim i As Long, j As Long, k As Long, m As Long
    GoSub writeIdentifiers
    GoSub writeGroups

    ' Write values of Target Array to Target Range.
    Set ws = wb.Worksheets(tgtName)
    ws.Range(tgtFirst).Resize(UBound(Target), UBound(Target, 2)).Value = Target

    ' Inform user.
    MsgBox "Data stacked.", vbInformation, "Success"

    Exit Sub

' Subroutines
writeIdentifiers:
    m = 1
    For i = 1 To ubS
        For j = 1 To GroupsCount
            For k = 1 To IdentCols
                Target(m, k) = Source(i, k)
            Next k
            m = m + 1
        Next j
    Next i
    Return

writeGroups:
    m = 1
    For i = 1 To ubS
        For j = 1 To GroupsCount
            For k = 1 To GroupCols
                Target(m, k + IdentCols) = _
                  Source(i, k + IdentCols + (j - 1) * GroupCols)
            Next k
            m = m + 1
        Next j
    Next i
    Return

' Error Handler
cleanError:
    MsgBox "An unexpected error occurred in '" & Proc & "'." & vbCr _
         & "Run-time error '" & Err.Number & "':" & vbCr & Err.Description _
           , vbCritical, Proc & " Error"
End Sub
let
Source=Excel.CurrentWorkbook(){[Name=“Table1”]}[Content],
#“unpivottothercolumns”=表.UnpivotOtherColumns(源,{“名称”,“ID”},“属性”,“值”),
#“添加的索引”=表的AddIndexColumn(#“取消插入其他列”,“索引”,0,1),
#“插入的整数除法”=Table.AddColumn(#“添加的索引”、“整数除法”、每个数字.IntegerDivide([Index],3)、Int64.Type),
#“删除的列”=Table.RemoveColumns(#“插入的整数除法”、{“属性”、“索引”}),
#“分组行”=Table.Group(#“已删除列”,{“整型除法”},{{“分组”,每个#,键入Table[Name=text,ID=number,Value=text,#“整型除法”