Vba 如何";“展平”;或;“崩溃”;将二维Excel表格转换为一维?
我有一个二维表格,在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 我想把它“展平”,这样我的国家在第一列,年份在第二列,然后
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中工作所做的:
你完了!第一列是行,第二列是列,第三列是数据。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列””-命令
=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