Unpivot Excel矩阵/数据透视表?

时间:2015-08-20 10:01:10

标签: excel matrix pivot unpivot

是否有快速的方式来解决问题" Excel矩阵/数据透视表(在Excel或其他地方),无需编写宏或其他代码
同样,我可以编写自己做的代码(C#或VBA或其他)。
我想知道是否可以快速执行没有代码?

E.g。我需要转换此权限矩阵(以Excel表格/矩阵形式给出)

Pivoted

进入这个半规范化的表(所以我可以将它插入到SQL数据库中):

Unpivoted

e.g。在SQL中,我可以这样做:

CREATE TABLE dbo.T_DocumentMatrix
(
    [Function] [varchar](255) NULL,
    [GROUP-Admin] [varchar](255) NULL,
    [GROUP-SuperUser] [varchar](255) NULL,
    [GROUP-Manager] [varchar](255) NULL,
    [GROUP-OLAP] [varchar](255) NULL,
    [GROUP-1] [varchar](255) NULL,
    [GROUP-2] [varchar](255) NULL,
    [GROUP-3] [varchar](255) NULL,
    [GROUP-4] [varchar](255) NULL,
    [GROUP-5] [varchar](255) NULL,
    [GROUP-6] [varchar](255) NULL,
    [GROUP-7] [varchar](255) NULL,
    [GROUP-8] [varchar](255) NULL,
    [Externals] [varchar](255) NULL
); 

从excel复制粘贴数据,然后

SELECT * 
FROM 
(
    SELECT 
         [Function]
        ,[GROUP-Admin]
        ,[GROUP-SuperUser]
        ,[GROUP-Manager]
        ,[GROUP-OLAP]
        ,[GROUP-1]
        ,[GROUP-2]
        ,[GROUP-3]
        ,[GROUP-4]
        ,[GROUP-5]
        ,[GROUP-6]
        ,[GROUP-7]
        ,[GROUP-8]
        ,[Externals]
    FROM T_DocumentMatrix
) AS p
UNPIVOT
(
    Rights FOR GroupName IN 
    (
         [GROUP-Admin]
        ,[GROUP-SuperUser]
        ,[GROUP-Manager]
        ,[GROUP-OLAP]
        ,[GROUP-1]
        ,[GROUP-2]
        ,[GROUP-3]
        ,[GROUP-4]
        ,[GROUP-5]
        ,[GROUP-6]
        ,[GROUP-7]
        ,[GROUP-8]
        ,[Externals]
    )
) AS unpvt
;

但是,这需要我为组中的每个更改更改表创建脚本和unpivot脚本...

5 个答案:

答案 0 :(得分:4)

哦,好吧,这有点复杂。 其中一个问题是,向导调用快捷方式在非英语版本的excel中不起作用(该死的,在家我会有英文版,但在这里工作......)

这是一个很好的视频: https://www.youtube.com/watch?v=pUXJLzqlEPk

但是可以删除YouTube视频,以便使其成为可靠的答案:

首先,您需要转到“选项”,然后添加menuband-item“Pivot table and PivotChart Wizard”。

Options

Wizard

创建多个合并透视表 Multiple-consolid

并使用自定义变体
custom variant

并选择范围,并在新工作表中 range

然后删除行和列字段

delete rows and columns

双击NUMBER(图中的54)

values consolidtions

和excel将为您提供中途标准化数据。

halfway

答案 1 :(得分:1)

虽然这是一个非常古老的问题,而且 Stefan 在当天找到了一个开明的答案,但值得重新审视。我自己就遇到了对这种无代码、动态 Unpivot 方法的需求,谷歌搜索将我带到了这里。是的,Power Query 可以完成这项工作,但这并不是完全无代码的,因为在 Power BI 中运行了一个脚本化的后台解决方案,它需要用户刷新数据(因此,它在工作簿中不是自动的)并且它不会在 Excel for Mac (tmk) 上运行。

以下是基于动态数组并使用 LET 函数的方法,因此需要 Excel 2016 或 Microsoft 365。

假设 Stefan 的数据位于单元格 A1 到 N8 中。在 Power Query 中,我们会说 Stefan 想要“Unpivot B1:N8 By A1:A8”。

<块引用>

注意:以下方法也可以接受 By 的多个列,例如您可能需要“逆透视 D1:N8 通过 A1:C8”。

=LET( unPivMatrix, B1:N8,
      byMatrix, A1:A8,
       upC, COLUMNS( unPivMatrix ),
       byC, COLUMNS( byMatrix ),
       dmxR, MIN( ROWS( unPivMatrix ), ROWS( byMatrix ) ) - 1,
       upCells, dmxR * upC,
       upSeq, SEQUENCE( upCells,, 0 ),
       upHdr, INDEX( INDEX( unPivMatrix, 1, ),  1,  SEQUENCE( upC ) ),
       upBody, INDEX( unPivMatrix,  SEQUENCE( dmxR ) + 1,  SEQUENCE( 1, upC ) ),
       byBody, INDEX( byMatrix,  SEQUENCE( dmxR ) + 1,  SEQUENCE( 1, byC ) ),
       attr, INDEX( upHdr, MOD( upSeq, upC ) + 1 ),
       mux, INDEX( upBody, upSeq/upC + 1, MOD( upSeq, upC ) + 1 ),
       demux, IFERROR( INDEX(
                             IFERROR( INDEX( byBody,
                                             IFERROR( INT( SEQUENCE( upCells, byC,0 )/byC/upC ) + 1, MOD( upSeq, upC ) + 1 ),
                                                      SEQUENCE( 1, byC + 1 ) ),
                                       attr ),
                             upSeq + 1, SEQUENCE( 1, byC + 2 ) ),
                        mux ),
       demux
     )

工作原理 - 读取输入

输入是您要取消旋转的范围,我称之为 unPivMatrix B1:N8(可以是您需要的任何维度)以及您要取消旋转的列,我称之为byMatrix A1:A8。

<块引用>

通常,byMatrix 的行需要与 unPivMatrix 相同,因此您必须从 A1 开始而不是 A2。我决定采取 这个约定,因为它们可能是 A1 中的标题,并且有一些 小模组,这个公式可以产生与 Power Query,但这不是 Stefan 要求的。

该公式首先计算每个矩阵中的列数:upC 反透视列和 byC 列。从这些它计算 dmxR(多路分解行):通过取 unPivMatrix 和 byMatrix 的行中的最小值并减去 1,因为 unPivMatrix 有一个标题,将在 unpivot 中传递多少行值。如果输入具有不同的行数(根据定义,这是错误的),则采用 MIN 是一个错误预防步骤。

这些值用于创建稍后将用于形成输出的整形变量。 upCells 是将被取消旋转的值的数量,用于生成名为 upSeq 的索引模式,该模式从 0 开始计算将在 INDEX 中使用的值的数量稍后发挥作用。我们从 0 开始,因为 upSeq 将被调制以形成输入和输出的正确索引。

现在我们将分解矩阵的各个部分,以便我们可以对它们进行多路复用。部分看起来像这样:

enter image description here

有一个 unpivot 标头 (upHdr),其中包含值数据的标识符(在 Stefan 的例子中,GROUP-Admin、GROUP-SuperUser 等)。这些将被多路复用到一个列中,该列稍后将放置在每个未旋转的值旁边。 upHdr 是通过将整个 unPivMatrix 放入一个 INDEX 函数并读取第 1 行和所有列来创建的。我将该 INDEX 函数嵌套到另一个 INDEX 中,该 INDEX 使用大小为 upC 的垂直序列将水平数组重塑为垂直数组。

upBody 包含我们要通过多路复用反透视的值。它是通过将 upMatrix 放入一个 INDEX 并通过将输出 SEQUENCE( dmxR ) + 1 § 针对要读取的每一列 SEQUENCE( upC ) 的行数对其进行整形来创建的。 byBody 包含将针对 upBody 中的每个值进行多路复用的数据。它的创建方式与 upBody 相同。

§ - 添加 1 以跳过标题行

工作原理 - 塑造和编写输出

输出将是这样的:

enter image description here

我们现在将 upHdr 多路复用为 attr 或属性(使用 Power Query 术语),方法是将 upHdr 放入 INDEX 并应用基于 upSeq 的调制序列,该序列重复每 upC 次(例如 {{1} })。 注意:这就是 upSeq 中从 0 开始很重要的地方。 attr 的输出看起来像(在 Stefan 的情况下){1;2;3;4;5;6;7;8;1;2;...}

mux 是将针对每个属性和 byBody 行进行多路复用的值(使用 Power Query 术语)。它是通过将 valBody 放入 INDEX 然后将其整形为由行创建的多路复用模式来创建的

{GROUP-Admin; GROUP-SuperUser, etc.} 产生一行 upSeq/upC + 1

的列

{1,1,1,1,1,1,1,1,2,2,...} + 1 产生一列 MOD( upSeq, upC )

mux 的输出将是 unPivMatrix 的内容。在 Stefan 的情况下,这会有点特殊,因为他使用 * 和空白作为数据。此公式会将空格转换为 0。因此,如果这是一个问题,您可以将多路复用器包装到 {1;2;3;4;5;6;7;8;1;...} 中,但我不会添加它,因为我想要一个通用的逆向旋转,因为我确信 Stefan 早就开始了。

工作原理 - 将部分解复用到输出中

现在简单的部分已经完成,是时候处理困难的部分了——将所有这些整合到一个动态数组中。将多个数组放在一起需要一个技巧,这个技巧必须应用两次,因为如您所见,我们将三个表放在一起。诀窍就像有一个 APPEND 函数,如:

IF( ISBLANK( mux ), "", mux )

要合并两个数组,请将第一个数组放入 INDEX,然后引用数组外部的单元格以强制执行 #REF!错误。例如,如果我有一个 3 x 2 的字母 A 到 F 数组,并且我引用了单元格 3、3,它将抛出引用错误。

enter image description here

现在,您可以通过将 INDEX 包装在 IFERROR 中来将错误替换为要附加的表来利用这些错误。有点像:

APPEND( APPEND( table1, table2 ), table3 )

从这个意义上说,上面的公式等效于 APPEND( table1, table2 ),其中 APPEND 是我们想要的两个表的逐行追加。 (注意:切换序列模式,您可以按列进行追加。)

所以,希望这个解释可以清楚地说明在一个名为 demux 的变量的最后阶段发生了什么,它提供了结果。我为结果命名,然后引用它,以便您可以轻松探索、修改或优化公式。所以,demux 真的很像:

IFERROR( INDEX( table1,
                SEQUENCE( table1.rows ),
                SEQUENCE( 1, table1.columns + table2.columns ) ),
         table2 )

我不会深入讨论这个最后阶段的工作原理,因为这已经是一个很长的答案,但简短的总结是这个附加使用由 upCells、upC 和 byC 创建的维度来形成输出。

我已经对此进行了测试,但我没有对其进行性能优化或使其达到#SwissEngineering 标准。

答案 2 :(得分:0)

现在有另一种方法可以通过Power Query:

  • 选择您的单元格
  • 菜单Data> From a table or a range screenshot
  • 在Power Query编辑器中,选择所有列并保存第一个列,然后选择Transform> Unpivot screenshot
  • 该表未旋转。转到Home> Close and load screenshot
  • 您未透视的桌子在这里。右键单击它,然后选择Refresh(如果原始表已更新screenshot

答案 3 :(得分:0)

我正在使用此VBA代码

Sub Unpivot()
'
Dim Rowlabel As Range
Dim Columnlabel As Range
Dim Pap As Range
Dim Tabl As Range
Dim i As Integer
Dim j As Integer
Dim a As Integer
Dim b As Integer
Dim Data As Range
Dim k As Integer
Dim Label As Range
Dim pvtCache As PivotCache
Dim pvt As PivotTable
Dim SrcData As String
'
ActiveSheet.Copy Before:=Worksheets(1)
Set Tabl = Selection
    For Each Pap In Tabl
     If Pap.MergeCells Then
        With Pap.MergeArea
            .UnMerge
            .Value = Pap.Value
        End With
    End If
    Next
i = Application.InputBox("Number of row contain label:", "Excel", i, Type:=2)
j = Application.InputBox("Number of column contain label:", "Excel", j, Type:=2)
On Error Resume Next
Sheets("Unpivot_Table").Delete
Sheets.Add.Name = "Unpivot_Table"
Set Pap = Range("Unpivot_Table!B2")
b = Tabl.Rows.Count
a = Tabl.Columns.Count
Set Data = Range(Tabl.Cells(i + 1, j + 1), Tabl.Cells(b, a))
Set Columnlabel = Range(Tabl.Cells(i + 1, 1), Tabl.Cells(b, j))
Set Rowlabel = Range(Tabl.Cells(1, j + 1), Tabl.Cells(i, a))
Pap.Select
For Each Column In Data.Columns
    Column.Copy
    Selection.PasteSpecial Paste:=xlPasteValues
    Columnlabel.Copy
    Selection.Offset(0, 1).PasteSpecial Paste:=xlPasteValues
    Column.Copy
    Selection.Offset(b - i, -1).Select
Next Column
Pap.Offset(0, j + 1).Select
For Each Column In Rowlabel.Columns
    Column.Copy
    Range(Selection, Selection.Offset(b - i - 1, 0)).PasteSpecial Paste:=xlPasteValues, Transpose:=True
    Selection.End(xlDown).Offset(1, 0).Select
Next Column
Set Label = Range(Pap.Offset(-1, 0), Pap.Offset(0, i + j + 1))
    For k = 1 To i + j + 1
    Label.Cells(1, k).Value = Application.InputBox(Label.Cells(2, k).Value & " is belong to Fieldname", "Hoang", k, Type:=2)
    Next
Range(Pap.End(xlUp), Pap.End(xlDown).End(xlToRight)).Select
SrcData = ActiveSheet.Name & "!" & Selection.Address
On Error Resume Next
    Sheets("Pivot").Delete
    Sheets.Add.Name = "Pivot"
  Set pvtCache = ActiveWorkbook.PivotCaches.Create( _
    SourceType:=xlDatabase, _
    SourceData:=SrcData)
  Set pvt = pvtCache.CreatePivotTable( _
    TableDestination:="Pivot!" & Sheets("Pivot").Range("A3").Address(ReferenceStyle:=xlR1C1), _
    TableName:="PivotTable1")
End Sub

答案 4 :(得分:0)

我相信您可以使用以下一种模块化算术。使用cols和row图例将数据放入此UDF的参数中。

Function MyUnpivot(matice As Range) As Variant
    Dim I As Integer
    Dim J As Integer

    Dim radka As Integer
    Dim sloupec As Integer

    I = matice.Rows.Count - 1
    J = matice.Columns.Count - 1

    Dim returnVal()
    ReDim Preserve returnVal(1 To I * J, 1 To 3)

    For x = 1 To I * J
        radka = ((x - 1) Mod I) + 2
        sloupec = WorksheetFunction.Floor_Math((x - 1 / 2) / I) + 2
        returnVal(x, 1) = matice.Cells(1, sloupec)
        returnVal(x, 2) = matice.Cells(radka, 1)
        returnVal(x, 3) = matice.Cells(radka, sloupec)
    Next

    MyUnpivot = returnVal
End Function