2

我想转换一个 excel 文件的结构,以便我可以将它导入到需要以给定方式构建的系统中。

这是excel文件的一个小提取。有分类变量(例如Line of business)和四个虚拟变量(指示在给定流程中使用哪些数据类别)(例如Customer )的组合。

| Process name | Line of business | Customer | Potential customer | Employee | Vendor |
|--------------|------------------|----------|--------------------|----------|--------|
| Ad campaign  | Marketing        | x        | x                  |          | x      |
| Payroll      | HR               |          |                    | x        | x      |


我想要的是更改结构,以便为虚拟变量的每个变体创建一个新行,并使用一个应用/转置相关数据类别名称的数据类别列。所需的输出如下所示:

| Process name | Line of business | Data category      |
|--------------|------------------|--------------------|
| Ad campaign  | Marketing        | Customer           |
| Ad campaign  | Marketing        | Potential customer |
| Ad campaign  | Marketing        | Vendor             |
| Payroll      | HR               | Employee           |
| Payroll      | HR               | Vendor             |


我尝试过的是制作一个 COUNTIF 语句来计算每行的“x”数。然后,我使用了一个 vba 脚本,该脚本为每个数据类别的变体创建了一个带有进程名称的新行。这是代码,脚本中的字母指的是 excel 中的列,所以 A 是进程名称列,G 是COUNTIF列,它创建了我需要的 n 行。

Sub KopyKat()
   Dim N As Long, i As Long, K As Long
   Dim v As String, kk As Long, m As Long
   N = Cells(Rows.Count, "G").End(xlUp).Row
   K = 1

   For i = 2 To N
      kk = Cells(i, "G").Value
      v = Cells(i, "A").Value
      For m = 1 To kk
         Cells(K + 1, "H") = v
         K = K + 1
      Next m
   Next i
End Sub

所以它从这个开始:

| Process name | Line of business | Customer | Potential customer | Employee | Vendor | COUNTIF |
|--------------|------------------|----------|--------------------|----------|--------|---------|
| Ad campaign  | Marketing        | x        | x                  |          | x      | 3       |
| Payroll      | HR               |          |                    | x        | x      | 2       |

对此:

| Process name | Line of business | Customer | Potential customer | Employee | Vendor | COUNTIF | Process name_2 |
|--------------|------------------|----------|--------------------|----------|--------|---------|----------------|
| Ad campaign  | Marketing        | x        | x                  |          | x      | 3       | Ad campaign    |
| Payroll      | HR               |          |                    | x        | x      | 2       | Ad campaign    |
|              |                  |          |                    |          |        |         | Ad campaign    |
|              |                  |          |                    |          |        |         | Payroll        |
|              |                  |          |                    |          |        |         | Payroll        |

这就是我有限的vba知识带我去的地方。我想更改代码以获得所需的输出。
提前致谢!

4

3 回答 3

3

只需使用 Power Query 完成(在 Excel 2010+ 中可用)所有步骤都可以从 UI 完成,但 M 代码如下

编辑: 添加了重命名属性列的步骤

  • 从表/范围中获取数据
  • 选择前两列,然后Unpivot Other Columns
  • 过滤列以仅显示 x
  • 删除
  • 重命名属性列 --> Data Category

M-代码

let
    Source = Excel.CurrentWorkbook(){[Name="Table1"]}[Content],
    #"Changed Type" = Table.TransformColumnTypes(Source,{{"Process name", type text}, {"Line of business", type text}, {"Customer", type text}, {"Potential customer", type text}, {"Employee", type text}, {"Vendor", type text}}),
    #"Unpivoted Other Columns" = Table.UnpivotOtherColumns(#"Changed Type", {"Process name", "Line of business"}, "Attribute", "Value"),
    #"Filtered Rows" = Table.SelectRows(#"Unpivoted Other Columns", each ([Value] = "x")),
    #"Removed Columns" = Table.RemoveColumns(#"Filtered Rows",{"Value"}),
    #"Renamed Columns" = Table.RenameColumns(#"Removed Columns",{{"Attribute", "Data Category"}})
in
    #"Renamed Columns"

在此处输入图像描述 在此处输入图像描述

于 2019-12-14T02:00:41.367 回答
2

首先,我推荐 Power Query 用于数据这种数据操作。

其次,如果你必须这样做,我认为将你的数据放在一个实际的 Excel 表格中对于组织你的数据和编写你的代码要好得多。

无论如何,您可以在下面找到我的解决方案。根据您有多少输入列,您可以调整内部循环。

注意:逐个循环单元格不是最有效的方法。如果数据量很大,将值读取到数组中,然后在内存中更改它们将使计算速度提高 100 倍以上。如果您可以提供有关数据大小的更多信息,我可以相应地更新我的答案。

Option Explicit

Sub KopyKat()
    Dim totalRow As Long
    totalRow = Cells(Rows.Count, "A").End(xlUp).Row

    'Result and data input sheets are specified here.
    Dim wsInput As Worksheet:     Set wsInput = Worksheets("Sheet5")
    Dim wsOutput As Worksheet:     Set wsOutput = Worksheets("Sheet6")

    Dim i As Long 'Row
    Dim j As Long 'Column
    Dim counter As Long:    counter = 0

   For i = 2 To totalRow
      For j = 3 To 6 'Column numbers are hardcoded for the sake of the example

      'Assumption is that value "x" is the only way to specify
        If wsInput.Cells(i, j).Value = "x" Then
            With wsOutput
                .Cells(counter + 2, 1) = wsInput.Cells(i, 1).Value 'Process Name
                .Cells(counter + 2, 2) = wsInput.Cells(i, 2).Value 'Line of Business
                .Cells(counter + 2, 3) = wsInput.Cells(1, j).Value 'Data Category
            End With
          counter = counter + 1
        End If

      Next j
   Next i
End Sub
于 2019-12-14T00:44:17.250 回答
1

检查此代码并根据您的需要对其进行自定义:

基于此数据布局:

在此处输入图像描述

Sub Transpose()

    Dim evalSheet As Worksheet
    Dim evalRange As Range
    Dim headerRange As Range
    Dim evalCell As Range
    Dim destCell As Range

    Dim sheetName As String
    Dim sourceRangeAddress As String
    Dim headerRangeAddress As String
    Dim destinationCellAddress As String

    Dim rowCounter As Long

    ' Customize to fit your needs
    sheetName = "Sheet1"
    sourceRangeAddress = "A2:F3"
    headerRangeAddress = "A1:F1"
    destinationCellAddress = "I2"

    Set evalSheet = ThisWorkbook.Worksheets(sheetName )

    ' Get the range
    Set evalRange = evalSheet.Range(sourceRangeAddress)

    Set headerRange = evalSheet.Range(headerRangeAddress)

    Set destCell = evalSheet.Range(destinationCellAddress)

    ' Loop through each cell in the first column
    For Each evalCell In evalRange.Columns(1).Cells

        ' Evaluate the four columns (columnOffset means how many columns to the right)
        If Trim(evalCell.Offset(columnOffset:=2).Value) = "x" Then
            destCell.Offset(rowOffset:=rowCounter, columnOffset:=0).Value = Trim(evalCell.Offset(columnOffset:=0).Value)
            destCell.Offset(rowOffset:=rowCounter, columnOffset:=1).Value = Trim(evalCell.Offset(columnOffset:=1).Value)
            ' Header range cells (3) means the third cell in the range - different than offset
            destCell.Offset(rowOffset:=rowCounter, columnOffset:=2).Value = Trim(headerRange.Cells(3).Value)

            rowCounter = rowCounter + 1

        End If

        If Trim(evalCell.Offset(columnOffset:=3).Value) = "x" Then
            destCell.Offset(rowOffset:=rowCounter, columnOffset:=0).Value = Trim(evalCell.Offset(columnOffset:=0).Value)
            destCell.Offset(rowOffset:=rowCounter, columnOffset:=1).Value = Trim(evalCell.Offset(columnOffset:=1).Value)
            destCell.Offset(rowOffset:=rowCounter, columnOffset:=2).Value = Trim(headerRange.Cells(4).Value)

            rowCounter = rowCounter + 1
        End If
        If Trim(evalCell.Offset(columnOffset:=4).Value) = "x" Then
            destCell.Offset(rowOffset:=rowCounter, columnOffset:=0).Value = Trim(evalCell.Offset(columnOffset:=0).Value)
            destCell.Offset(rowOffset:=rowCounter, columnOffset:=1).Value = Trim(evalCell.Offset(columnOffset:=1).Value)
            destCell.Offset(rowOffset:=rowCounter, columnOffset:=2).Value = Trim(headerRange.Cells(5).Value)

            rowCounter = rowCounter + 1
        End If
        If Trim(evalCell.Offset(columnOffset:=5).Value) = "x" Then
            destCell.Offset(rowOffset:=rowCounter, columnOffset:=0).Value = Trim(evalCell.Offset(columnOffset:=0).Value)
            destCell.Offset(rowOffset:=rowCounter, columnOffset:=1).Value = Trim(evalCell.Offset(columnOffset:=1).Value)
            destCell.Offset(rowOffset:=rowCounter, columnOffset:=2).Value = Trim(headerRange.Cells(6).Value)

            rowCounter = rowCounter + 1
        End If
    Next evalCell

End Sub

如果有帮助记得标记答案

于 2019-12-14T00:21:57.110 回答