Vba 如何";“展平”;或;“崩溃”;将二维Excel表格转换为一维?

Vba 如何";“展平”;或;“崩溃”;将二维Excel表格转换为一维?,vba,excel,Vba,Excel,我有一个二维表格,在Excel中显示国家和年份。例如 1961 1962 1963 1964 USA a x g y France u e h a Germany o x n p 我想把它“展平”,这样我的国家在第一列,年份在第二列,然后

我有一个二维表格,在Excel中显示国家和年份。例如

        1961        1962        1963        1964
USA      a           x            g           y
France   u           e            h           a
Germany  o           x            n           p
我想把它“展平”,这样我的国家在第一列,年份在第二列,然后价值在第三列

Country      Year       Value
USA          1961       a
USA          1962       x
USA          1963       g
USA          1964       y
France       1961       u
              ...
我在这里给出的示例只是一个3x4矩阵,但我拥有的实际数据集要大得多(大约50x40左右)


您对我如何使用Excel执行此操作有何建议?

您可以使用Excel数据透视表功能来反转数据透视表(基本上就是您在这里看到的):

这里有很好的说明:

如果您不想手动执行说明,请将以下VBA代码链接(放入模块中):

Sub ReversePivotTable()
'   Before running this, make sure you have a summary table with column headers.
'   The output table will have three columns.
    Dim SummaryTable As Range, OutputRange As Range
    Dim OutRow As Long
    Dim r As Long, c As Long

    On Error Resume Next
    Set SummaryTable = ActiveCell.CurrentRegion
    If SummaryTable.Count = 1 Or SummaryTable.Rows.Count < 3 Then
        MsgBox "Select a cell within the summary table.", vbCritical
        Exit Sub
    End If
    SummaryTable.Select
    Set OutputRange = Application.InputBox(prompt:="Select a cell for the 3-column output", Type:=8)
'   Convert the range
    OutRow = 2
    Application.ScreenUpdating = False
    OutputRange.Range("A1:C3") = Array("Column1", "Column2", "Column3")
    For r = 2 To SummaryTable.Rows.Count
        For c = 2 To SummaryTable.Columns.Count
            OutputRange.Cells(OutRow, 1) = SummaryTable.Cells(r, 1)
            OutputRange.Cells(OutRow, 2) = SummaryTable.Cells(1, c)
            OutputRange.Cells(OutRow, 3) = SummaryTable.Cells(r, c)
            OutputRange.Cells(OutRow, 3).NumberFormat = SummaryTable.Cells(r, c).NumberFormat
            OutRow = OutRow + 1
        Next c
    Next r
End Sub
Sub-ReversePivotTable()
'在运行此操作之前,请确保您有一个包含列标题的摘要表。
'输出表将有三列。
Dim SummaryTable作为范围,OutputRange作为范围
昏昏欲睡
变暗r为长,c为长
出错时继续下一步
设置SummaryTable=ActiveCell.CurrentRegion
如果SummaryTable.Count=1或SummaryTable.Rows.Count<3,则
MsgBox“选择汇总表中的单元格”,vbCritical
出口接头
如果结束
SummaryTable.选择
Set OutputRange=Application.InputBox(提示:=“为三列输出选择一个单元格”,类型:=8)
'转换范围
OutRow=2
Application.ScreenUpdating=False
OutputRange.Range(“A1:C3”)=数组(“Column1”、“Column2”、“Column3”)
对于r=2,则为SummaryTable.Rows.Count
对于c=2,将其添加到SummaryTable.Columns.Count
OutputRange.Cells(OutRow,1)=汇总表单元格(r,1)
OutputRange.Cells(OutRow,2)=汇总表单元格(1,c)
OutputRange.Cells(OutRow,3)=汇总表单元格(r,c)
OutputRange.Cells(OutRow,3).NumberFormat=SummaryTable.Cells(r,c).NumberFormat
OutRow=OutRow+1
下一个c
下一个r
端接头

-Adam

@Adam Davis的答案很完美,但如果你和我一样对Excel VBA一无所知,那么下面是我为让代码在Excel 2007中工作所做的:

  • 打开包含需要展平到表格的矩阵的工作簿,然后导航到该工作表
  • 按Alt-F11打开VBA代码编辑器
  • 在左侧窗格的“项目”框中,您将看到一个树结构,表示excel对象和任何已存在的代码(称为模块)。在框中的任意位置单击鼠标右键,然后选择“插入->模块”以创建一个空白模块文件
  • 复制并粘贴Adman Davis的代码,从上面进入空白页,打开并保存它。
  • 关闭VBA编辑器窗口并返回到电子表格
  • 单击矩阵中的任何单元格以指示要使用的矩阵
  • 现在需要运行宏。此选项的位置将根据您的Excel版本而有所不同。在使用2007时,我可以告诉您,它将其宏作为最右边的控件保留在“视图”功能区中。单击它,您将看到一个宏列表,只需双击名为“ReversePivotTable”的宏即可运行它
  • 然后,它将显示一个弹出窗口,要求您告诉它在哪里创建展开的表。只需将其指向电子表格中的任何空白处,然后单击“确定”

  • 你完了!第一列是行,第二列是列,第三列是数据。

    VBA解决方案在某些情况下可能不可接受(例如,由于安全原因无法嵌入宏等)。对于这些情况,或者一般来说,我更喜欢使用公式而不是宏

    我试图在下面描述我的解决方案

    • 如问题所示输入数据(B2:F5)
    • 列标题(C2:F2)
    • 行标题(B3:B5)
    • 数据矩阵(C3:F5)
    • 无数据行(I2)=计数A(行标题)+计数空白(行标题)
    • 数据列的数量(I3)=计数A(列标题)+计数空白(列标题)
    • 无输出行(I4)=无数据行*无数据列
    • 种子面积为K2:M2,为空白,但被引用,因此不被删除

    • K3(拖动至K100,请参见注释说明)=ROW()-ROW($K$2),适用于希望使用数据透视表执行此操作并遵循以下指南的任何人:

      如果要在Excel 2007或2010中执行此操作,则首先需要启用数据透视表向导

      要找到该选项,您需要通过Excel主窗口图标转到“Excel选项”,并查看“自定义”部分中选择的选项,然后从“选择命令来源:”下拉列表中选择“功能区中没有的命令”,并需要将“数据透视表和数据透视图向导”添加到右侧。。见下图

      完成后,Excel窗口顶部的快速栏菜单中应有一个小的“数据透视表向导”图标,然后您可以按照上面链接中所示的相同过程进行操作


      在Excel 2013中,需要执行以下步骤:

      • 选择数据并转换为表格(插入->表格
      • 调用表的查询编辑器(powerquery->fromtable
      • 选择包含年份的列
      • 在关联菜单中选择“取消PIVOT列””-命令

      在Excel 2016中,Power Query被称为Get&Transform,可在Data选项卡中找到它。

      可以使用一个数组公式1和两个标准公式展平数据矩阵(也称为表)

      G3:I3中的数组公式1和两个标准公式为

      =IFERROR(INDEX(A$2:A$4, MATCH(0, IF(COUNTIF(G$2:G2, A$2:A$4&"")<COUNT($1:$1), 0, 1), 0)), "")
      =IF(LEN(G3), INDEX($B$1:INDEX($1:$1, MATCH(1E+99,$1:$1 )), , COUNTIF(G$3:G3, G3)), "")
      =INDEX(A:J,MATCH(G3,A:A,0),MATCH(H3,$1:$1,0))
      

      =IFERROR(INDEX(A$2:A$4,MATCH(0,IF)(COUNTIF(G$2:G2,A$2:A$4&“”)我开发了另一个宏,因为我需要经常刷新输出表(输入表由其他表填充),我想在输出表中有更多信息(更多复制的列和一些公式)


      更新了ReversePivotTable函数,因此我可以指定标题列的数量
      Sub TableConvert()
      
      Dim tbl As ListObject 
      Dim t
      Rows As Long
      Dim tCols As Long
      Dim userCalculateSetting As XlCalculation
      Dim wrksht_in As Worksheet
      Dim wrksht_out As Worksheet
      
      '##block calculate and screen refresh
      Application.ScreenUpdating = False
      userCalculateSetting = Application.Calculation
      Application.Calculation = xlCalculationManual
      
      '## get the input and output worksheet
      Set wrksht_in = ActiveWorkbook.Worksheets("ressource_entry")'## input
      Set wrksht_out = ActiveWorkbook.Worksheets("data")'## output.
      
      
      '## get the table object from the worksheet
      Set tbl = wrksht_in.ListObjects("Table14")  '## input
      Set tb2 = wrksht_out.ListObjects("Table2") '## output.
      
      '## delete output table data
      If Not tb2.DataBodyRange Is Nothing Then
          tb2.DataBodyRange.Delete
      End If
      
      '## count the row and col of input table
      
      With tbl.DataBodyRange
           tRows = .Rows.Count
           tCols = .Columns.Count
      End With
      
      '## check every case of the input table (only the data part)
      For j = 2 To tRows '## parse all row from row 2 (header are not checked)
          For i = 5 To tCols '## parse all column from col 5 (first col will be copied in each record)
              If IsEmpty(tbl.Range.Cells(j, i).Value) = False Then
                  '## if there is time enetered create a new row in table2 by using the first colmn of the selected cell row and cell header plus some formula
                  Set oNewRow = tb2.ListRows.Add(AlwaysInsert:=True)
                  oNewRow.Range.Cells(1, 1).Value = tbl.Range.Cells(j, 1).Value
                  oNewRow.Range.Cells(1, 2).Value = tbl.Range.Cells(j, 2).Value
                  oNewRow.Range.Cells(1, 3).Value = tbl.Range.Cells(j, 3).Value
                  oNewRow.Range.Cells(1, 4).Value = tbl.Range.Cells(1, i).Value
                  oNewRow.Range.Cells(1, 5).Value = tbl.Range.Cells(j, i).Value
                  oNewRow.Range.Cells(1, 6).Formula = "=WEEKNUM([@Date])"
                  oNewRow.Range.Cells(1, 7).Formula = "=YEAR([@Date])"
                  oNewRow.Range.Cells(1, 8).Formula = "=MONTH([@Date])"
              End If
         Next i
      Next j
      ThisWorkbook.RefreshAll
      
      '##unblock calculate and screen refresh
      Application.ScreenUpdating = True 
      Application.Calculate
      Application.Calculation = userCalculateSetting
      
      End Sub
      
      Sub ReversePivotTable()
      '   Before running this, make sure you have a summary table with column headers.
      '   The output table will have three columns.
          Dim SummaryTable As Range, OutputRange As Range
          Dim OutRow As Long
          Dim r As Long, c As Long
      
          Dim lngHeaderColumns As Long, lngHeaderRows As Long, lngHeaderLoop As Long
      
          On Error Resume Next
          Set SummaryTable = ActiveCell.CurrentRegion
          If SummaryTable.Count = 1 Or SummaryTable.Rows.Count < 3 Then
              MsgBox "Select a cell within the summary table.", vbCritical
              Exit Sub
          End If
          SummaryTable.Select
      
          Set OutputRange = Application.InputBox(prompt:="Select a cell for the 3-column output", Type:=8)
          lngHeaderColumns = Application.InputBox(prompt:="Header Columns")
          lngHeaderRows = Application.InputBox(prompt:="Header Rows")
      '   Convert the range
          OutRow = 2
          Application.ScreenUpdating = False
          'OutputRange.Range("A1:D3") = Array("Column1", "Column2", "Column3", "Column4")
          For r = lngHeaderRows + 1 To SummaryTable.Rows.Count
              For c = lngHeaderColumns + 1 To SummaryTable.Columns.Count
                  ' loop through all header columns and add to output
                  For lngHeaderLoop = 1 To lngHeaderColumns
                      OutputRange.Cells(OutRow, lngHeaderLoop) = SummaryTable.Cells(r, lngHeaderLoop)
                  Next lngHeaderLoop
                  ' loop through all header rows and add to output
                  For lngHeaderLoop = 1 To lngHeaderRows
                      OutputRange.Cells(OutRow, lngHeaderColumns + lngHeaderLoop) = SummaryTable.Cells(lngHeaderLoop, c)
                  Next lngHeaderLoop
      
                  OutputRange.Cells(OutRow, lngHeaderColumns + lngHeaderRows + 1) = SummaryTable.Cells(r, c)
                  OutputRange.Cells(OutRow, lngHeaderColumns + lngHeaderRows + 1).NumberFormat = SummaryTable.Cells(r, c).NumberFormat
                  OutRow = OutRow + 1
              Next c
          Next r
      End Sub
      
          Option Explicit
          Private ws_Sour As Worksheet, ws_Dest As Worksheet
          Private arr_2d_Sour() As Variant, arr_2d_Dest() As Variant
          ' https://stackoverflow.com/questions/52594461/find-next-available-value-in-excel-cell-based-on-criteria
          Public Sub PullOut(Optional ByVal msg As Variant)
              ws_Dest_Acr _
                      arr_2d_ws( _
                      arr_2d_Dest_Fill( _
                      arr_2d_Sour_Load( _
                      arr_2d_Dest_Create( _
                      CountA_rng( _
                      rng_2d_For_CountA( _
                      Init))))))
          End Sub
      
          Private Function ws_Dest_Acr(Optional ByVal msg As Variant) As Variant
              ws_Dest.Activate
          End Function
      
          Public Function arr_2d_ws(Optional ByVal msg As Variant) As Variant
              If IsArray(arr_2d_Dest) Then _
                 ws_Dest.Cells(1, 1).Resize(UBound(arr_2d_Dest), UBound(arr_2d_Dest, 2)) = arr_2d_Dest
          End Function
      
          Private Function arr_2d_Dest_Fill(Optional ByVal msg As Variant) As Variant
              Dim y_Sour As Long, y_Dest As Long, x As Long
              y_Dest = 1
              For y_Sour = LBound(arr_2d_Sour) To UBound(arr_2d_Sour)
                  ' without the first column
                  For x = LBound(arr_2d_Sour, 2) + 1 To UBound(arr_2d_Sour, 2)
                      If arr_2d_Sour(y_Sour, x) <> Empty Then
                          arr_2d_Dest(y_Dest, 1) = arr_2d_Sour(y_Sour, 1)    'iD
                          arr_2d_Dest(y_Dest, 2) = arr_2d_Sour(y_Sour, x)    'DTLx
                          y_Dest = y_Dest + 1
                      End If
                  Next
              Next
          End Function
      
          Private Function arr_2d_Sour_Load(Optional ByVal msg As Variant) As Variant
              arr_2d_Sour = ReDuce_rng(ws_Sour.UsedRange, 1, 0).Offset(1, 0).Value
          End Function
      
          Private Function arr_2d_Dest_Create(ByVal iRows As Long)
              Dim arr_2d() As Variant
              ReDim arr_2d(1 To iRows, 1 To 2)
              arr_2d_Dest = arr_2d
              arr_2d_Dest_Create = arr_2d
          End Function
      
          Public Function CountA_rng(ByVal rng As Range) As Double
              CountA_rng = Application.WorksheetFunction.CountA(rng)
          End Function
      
          Private Function rng_2d_For_CountA(Optional ByVal msg As Variant) As Range
              ' without the first line and without the left column
              Set rng_2d_For_CountA = _
              ReDuce_rng(ws_Sour.UsedRange, 1, 1).Offset(1, 1)
          End Function
      
          Public Function ReDuce_rng(rng As Range, ByVal iRow As Long, ByVal iCol As Long) _
                 As Range
              With rng
                  Set ReDuce_rng = .Resize(.Rows.Count - iRow, .Columns.Count - iCol)
              End With
          End Function
      
          Private Function Init()
              With ThisWorkbook
                  Set ws_Sour = .Worksheets("Sour")
                  Set ws_Dest = .Worksheets("Dest")
              End With
          End Function
      
      'https://youtu.be/oTp4aSWPKO0