取消透视 Excel 矩阵/数据透视表?

Posted

技术标签:

【中文标题】取消透视 Excel 矩阵/数据透视表?【英文标题】:Unpivot an Excel matrix/pivot-table? 【发布时间】:2015-08-20 10:01:09 【问题描述】:

有没有一种快速“取消透视” Excel 矩阵/数据透视表(在 Excel 或其他地方)的方法,无需编写宏或其他代码? 同样,我可以自己编写代码(C# 或 VBA 或其他)。 我想知道是否有可能无需代码快速完成?

例如我需要转换这个权限矩阵(以 Excel 表格/矩阵形式给出)

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

例如在 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-script...

【问题讨论】:

可以很快完成:superuser.com/a/78464(但您可能需要先将* 替换为1)。 @user3964075:实际上你不需要替换 *,它可以作为字符串正常工作。之前找到了那个帖子,但对我没有用,因为我无法启动枢轴向导。在 youtube 上找到了答案。 【参考方案1】:

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

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

但是可以删除 youtube 视频,所以要让它成为一个可靠的答案:

首先,您需要转到“选项”,然后添加菜单带项“数据透视表和数据透视图向导”。

创建多个合并数据透视表

并使用自定义变体

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

然后删除行和列字段

双击数字(图中为 54)

Excel 会为您提供中途标准化数据。

【讨论】:

【参考方案2】:

虽然这是一个非常古老的问题,并且 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 的多个列,例如您可能需要“Unpivot D1:N8 By 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(可以是您需要的任何维度)以及您想要通过我调用的方式取消透视它们的列按矩阵 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 将被调制以形成输入和输出的正确索引。

现在我们将分解矩阵的各个部分,以便对它们进行多路复用。零件如下所示:

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

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

§ - 加 1 跳过标题行

这是如何工作的 - 塑造和编写输出

输出的形状如下:

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

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

upSeq/upC + 1 产生一行1,1,1,1,1,1,1,1,2,2,...

和列

MOD( upSeq, upC ) + 1 产生一列1;2;3;4;5;6;7;8;1;...

mux 的输出将是 unPivMatrix 的内容。在 Stefan 的情况下,这会有点特别,因为他使用 * 和空白作为数据。此公式会将空白转换为 0。因此,如果这是一个问题,您可以将 mux 包装到 IF( ISBLANK( mux ), "", mux ) 中,但我没有添加它,因为我想要一个通用的 unpivot,因为我确信 Stefan 早就继续前进了。

这是如何工作的 - 将部分解复用到输出中

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

APPEND( APPEND( table1, table2 ), table3 ).

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

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

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

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

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

APPEND( byBody, APPEND( attr, mux ) )

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

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

【讨论】:

答案是完美的,如果我能让它工作,它可以解决一个大问题。但无法在 Excel 2016 中使用。Excel 无法识别 LET 函数。有什么线索吗? 嘿@varun - 是的,事实上,在我发布这个之后,我发现 LET 和 SEQUENCE 只在 Excel 365 中 - 甚至不是 2019 年!两者都需要完成。虽然有使用 INDEX & ROW 的 SEQUENCE 的解决方法,但 LET 问题仍然存在。 LET 可以通过将公式折叠成不可读的混乱来消除,但真正的问题将是计算时间。 LET 允许您的公式“干燥”。此处发布的 PowerQuery 方法[***.com/questions/20541905/… by Telwyn 可以为您工作。 是否可以应用它来保留未透视数据中的多个列?例如,假设原始列实际上是二维的,月份跨越 4 列,每列在 4*12=48 列上重复。我需要一个包含 4 个值列的非数据透视表,或者一个包含 2 个属性列的非数据透视表 @Scott - 所以,要理解,你的意思是 upHdr 会是多列,逐行重复吗?例如这些列是每个月的压力、湿度、温度、二氧化碳。即 Jan Pressure、Jan Hum、... Feb Pressure、Feb Hum... 行可能代表城市、州。然后输出将有 4 个值 (P,h,T,C) 用于上面图像 2 中的 values 并且 attr 将具有月份。如果是这样,它就像一个 python-pandas 融化,是的,它可以在动态数组公式中解决。 @Scott - 它让我想起了这个 OP:***.com/questions/68109101/… 但列标题中会有多行,对吧?【参考方案3】:

我正在使用这个 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】:

我相信您可以使用如下的模运算。使用 cols 和 rows 图例将您的数据放入此 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

【讨论】:

【参考方案5】:

Power Query 还有另一种方式:

选择您的单元格 菜单Data > From a table or a range

在 Power Query 编辑器中,选择所有列保存第一个,然后 Transform > Unpivot

表未透视。转到Home > Close and load

您的未透视表在这里。如果您的原始表格已更新,请右键单击它并选择 Refresh

【讨论】:

以上是关于取消透视 Excel 矩阵/数据透视表?的主要内容,如果未能解决你的问题,请参考以下文章

excel如何将数据透视表中的汇总项去掉?

Excel 请问资料透视表如何去掉汇总行

快速取消数据透视表的分类汇总行和总计行

在 Mac 上的 Excel 2011 中取消透视表

如何将excel表格中数据透视后的汇总一栏去掉呢?

如何转置 Excel 数据透视表