【问题标题】:Convert row with columns of data into column with multiple rows in Excel 2007在 Excel 2007 中将具有数据列的行转换为具有多行的列
【发布时间】:2019-02-22 16:25:25
【问题描述】:

我有一行数据如下:

            header1      header2      header3      header4      header5
row key     datavalue1   datavalue2   datavalue3   datavalue4   datavalue5....

所以基本上,我有一个非规范化数据集,其中数据值可能逐行为空,也可能不为空。我需要将它们标准化。

12345678    NULL         10           3            NULL         14

会变成:

12345678   header2   10
12345678   header3   3
12345678   header5   14

我可以通过使用特殊粘贴转换来做到这一点,但我有数千行,我需要确保为每个行获得正确的行键。此外,每一行都有一堆与之关联的描述,我需要将它们与每个数据值一起复制。

转换每一行列的最简单方法是什么,以便我拥有多行单列,其中包含所有非空数据值以及关联的数据值引用?我需要能够旋转数据集。

【问题讨论】:

  • 就个人而言,我会使用 VBA 代码来解析列表。对于熟悉并熟悉编写 VBA 代码的人来说,这很容易说,但对于刚接触 VBA 的人来说,这似乎非常具有挑战性。您对 VBA 感觉如何?您想查看 VBA 解决方案吗?
  • 添加了 VBA 标签以便向该领域的一些专家提出问题

标签: excel vba pivot-table database-normalization


【解决方案1】:

如果您有五个“标题”列,请输入这些公式

H1: =OFFSET($A$1,INT((ROW()-1)/5)+1,0)
I1: =OFFSET($A$1,0,IF(MOD(ROW(),5)=0,5,MOD(ROW(),5)))
J1: =INDEX($A$1:$F$9,MATCH(H1,$A$1:$A$9,FALSE),MATCH(I1,$A$1:$F$1,FALSE))

复制 H1:J??并在顶部粘贴特殊值。对列 J 进行排序并删除任何为零的内容。如果您在数据中有合法的零,那么您首先需要将空白单元格替换为一些您可以稍后删除的唯一字符串。

如果您有更多列,请将上述所有公式中的“5”替换为您拥有的任何数字。

【讨论】:

  • 谢谢。您的公式是发布的最简单的解决方案(也是我模糊理解的解决方案!)所以我计划在深入 VBA 之前先使用这种方法。
  • 谢谢你!!!!!!这些公式非常有效。我必须制作一些模组并弄清楚到底发生了什么,但它们让我不必即时学习 VBA!
  • 有没有办法修改J1中的公式来引用MATCH中的多列?我发现同一个人可以在不同时间段内拥有多个叶子的情况。我想匹配 2 列,以便确保提取正确的数据;否则,我会重复计算第一次休假类型。
  • 这是一个绝妙的解决方案。感激不尽。
【解决方案2】:

在我看来,您尝试做的部分工作是“取消透视”数据透视表。当我不得不做类似的任务时,我发现这个技巧是一个巨大的帮助:http://spreadsheetpage.com/index.php/tip/creating_a_database_table_from_a_summary_table/

请注意,在 Excel 2007 中,您可以使用按键 Alt+D、P 进入旧的 Excel 2003 数据透视表向导。

【讨论】:

    【解决方案3】:

    Excel 具有转置功能,可以满足您的需求。它非常隐蔽且有点笨拙,但可能比深入研究 VBA 更容易。以下是 Excel 2007 帮助的摘录:

    块引用 切换(转置)列和行 全部显示全部隐藏 如果数据以列或行的形式输入,但您希望将数据重新排列为行或列,则可以快速将数据从一个转换到另一个。

    例如,按列组织的区域销售数据在数据转置后显示为行,如下图所示。

    1.在工作表上,执行以下操作: 要将数据从列重新排列到行,请选择包含数据的列中的单元格。 要将数据从行重新排列到列,请选择包含数据的行中的单元格。 2.在主页选项卡的剪贴板组中,单击复制。

    键盘快捷键复制选中的数据,也可以按CTRL+C。

    注意您只能使用复制命令重新排列数据。要成功完成此过程,请不要使用剪切命令。

    3.在工作表上,选择要重新排列复制数据的目标行或列的第一个单元格。 注意复制区域(复制区域:当您要将数据粘贴到另一个位置时复制的单元格。复制单元格后,它们周围会出现一个移动边框,表示它们已被复制。)和粘贴区域(粘贴区域:使用 Office 剪贴板剪切或复制的数据的目标位置。)不能重叠。确保您在粘贴区域中选择了一个单元格,该单元格位于您从中复制数据的区域之外。

    4.在主页选项卡的剪贴板组中,单击粘贴下方的箭头,然后单击转置。 5.数据转置成功后,可以删除复制区的数据。 提示 如果您转置的单元格包含公式,则会转置公式,并且会自动调整对转置单元格中数据的单元格引用。为确保公式继续正确引用非转置单元格中的数据,请在转置之前在公式中使用绝对引用。

    有关详细信息,请参阅在相对引用、绝对引用和混合引用之间切换。

    块引用

    【讨论】:

      【解决方案4】:

      让我们看看 VBA 中可能的解决方案。我认为这真的会有所帮助。关于我的代码,您应该了解以下几点。

      • 您需要将此代码放在 VBA 的代码模块中(与宏所在的位置相同)
      • 看看我给这些表起什么名字:原始和规范化。您需要更改工作表名称或代码
      • 我正在检查字符串字段为NULL 的值。如果单元格为空,您需要检查 If IsEmpty(rngCurrent.Value) Then

      '

      Sub NormalizeSheet()
      Dim wsOriginal As Worksheet
      Dim wsNormalized As Worksheet
      Dim strKey As String
      Dim clnHeader As Collection
      Dim lngColumnCounter As Long
      Dim lngRowCounterOriginal As Long
      Dim lngRowCounterNormalized As Long
      Dim rngCurrent As Range
      Dim varColumn As Variant
      
      Set wsOriginal = ThisWorkbook.Worksheets("Original")     'This is the name of your original worksheet'
      Set wsNormalized = ThisWorkbook.Worksheets("Normalized") 'This is the name of the new worksheet'
      Set clnHeader = New Collection
      
      wsNormalized.Cells.ClearContents        'This deletes the contents of the destination worksheet'
      
      lngColumnCounter = 2
      lngRowCounterOriginal = 1
      Set rngCurrent = wsOriginal.Cells(lngRowCounterOriginal, lngColumnCounter)
      
      ' We'll loop through just the headers to get a collection of header names'
      Do Until IsEmpty(rngCurrent.Value)
          clnHeader.Add rngCurrent.Value, CStr(lngColumnCounter)
          lngColumnCounter = lngColumnCounter + 1
          Set rngCurrent = wsOriginal.Cells(lngRowCounterOriginal, lngColumnCounter)
      Loop
      
      'Here we'll reset our Row Counter and loop through the entire data set'
      lngRowCounterOriginal = 2
      lngRowCounterNormalized = 1
      lngColumnCounter = 1
      
      Do While Not IsEmpty(wsOriginal.Cells(lngRowCounterOriginal, lngColumnCounter))
      
          Set rngCurrent = wsOriginal.Cells(lngRowCounterOriginal, lngColumnCounter)
          strKey = rngCurrent.Value ' Get the key value from the current cell'
          lngColumnCounter = 2
      
          'This next loop parses the denormalized values for each row'
          Do While Not IsEmpty(wsOriginal.Cells(lngRowCounterOriginal, lngColumnCounter))
              Set rngCurrent = wsOriginal.Cells(lngRowCounterOriginal, lngColumnCounter)
      
              'We're going to check to see if the current value'
              'is equal to NULL. If it is, we won't add it to'
              'the Normalized Table.'
              If rngCurrent.Value = "NULL" Then
                  'Skip it'
              Else
                  'Add this item to the normalized sheet'
                  wsNormalized.Range("A" & lngRowCounterNormalized).Value = strKey
                  wsNormalized.Range("B" & lngRowCounterNormalized).Value = clnHeader(CStr(lngColumnCounter))
                  wsNormalized.Range("C" & lngRowCounterNormalized).Value = rngCurrent.Value
                  lngRowCounterNormalized = lngRowCounterNormalized + 1
              End If
      
              lngColumnCounter = lngColumnCounter + 1
          Loop
          lngRowCounterOriginal = lngRowCounterOriginal + 1
          lngColumnCounter = 1    'We reset the column counter here because we're on a new row'
      Loop
      
      
      
      End Sub
      

      【讨论】:

        【解决方案5】:

        我将创建一个循环遍历每一行并将数据输出到另一个页面的 VBA 宏。这将允许您在数据输出后在新页面中创建数据透视表。

        不确定您对 VBA 的熟悉程度,但这可以通过将数据加载到数组(或对象集合,如果您真的想正确执行)并将其写回来轻松完成。

        这是一个好的 VBA 文档的链接。

        http://social.msdn.microsoft.com/Forums/en/isvvba/thread/d712dbdd-c876-4fe2-86d2-7d6323b4262c

        编辑

        请注意,这并不是一个完整的解决方案,而是一个真正的通用框架,可以帮助您朝着正确的方向前进。

        作为一个通用示例,它做了很多您需要做的事情(不是最好的方法,但对于初学者来说可能是最简单的方法),这样的事情应该可以帮助您入门,尽管很难说没有看到更多你的工作表。

        Sub RowsToColumns ()
          Application.ScreenUpdating = False
          Dim srcWrkSheet As Worksheet
          Dim destWrkSheet As Worksheet
          Dim excelData as pExcelData
          Dim srcRowNumber As Long
          Dim srcRolNumber As Long
          Dim destRowNumber As Long
          Dim destColNumber As Long
        
          SET srcWrkSheet = Sheets("YourSourceWorkSheetName")
          SET destWrkSheet = Sheets("YourDestinationWorkSheetName")
        
          srcRowNumber = 1
          srcColNumber = 1
          destRowNumber = 1
          destColNumber = 1
        
          'Loop until blank row is encountered in column 1
          Do
            destWrkSheet.Cells(destRowNumber ,1).Value = "Header 1 " & srcWrkSheet.Cells(srcRowNumber,srcColNumber )
            destWrkSheet.Cells(destRowNumber ,1).Value = "Header 2 " & srcWrkSheet.Cells(srcRowNumber ,srcColNumber)
        
            srcRowNumber = srcRowNumber + 1
            srcColNumber = srcColNumber + 1
            destRowNumber = destRowNumber  + 1
          Loop Until srcWrkSheet .Cells(rowNumber, 1).value = ""
        
        End Sub
        

        【讨论】:

        • 呃。我真的希望避免使用 VBA。我对 VBA 的了解为零。不过会阅读链接。
        • 如果您打开录制宏功能并手动执行一次任务并发布代码,我会尝试快速修剪它或至少为您指明正确的方向。
        • “我真的希望避免使用 VBA。” - 我有这种感觉很久了,但事实是,学​​习 VBA 非常适合做你想做的事。 Excel 函数通常需要大量修改才能正确使用。
        • @Irwin,我认为您的代码有很多问题:您需要将行号和列号初始化为 1。您确实应该对行和列都使用嵌套循环。您需要为目标工作表上的行设置一个单独的计数器,否则每个键只能写入一个值。使用 VBA 的好主意,不过......只要确保它是好的 VBA 使用 :)
        • @bmccormack,你是绝对正确的。我在编辑器中编写了该代码作为工作框架,但我应该更加小心。我现在正在纠正它。
        猜你喜欢
        • 1970-01-01
        • 2011-04-11
        • 2012-12-27
        • 1970-01-01
        • 1970-01-01
        • 2021-11-17
        • 2021-05-12
        • 1970-01-01
        • 2012-01-28
        相关资源
        最近更新 更多