重塑 table Excel PowerQuery

Reshaping table Excel PowerQuery

我在 Excel 中有一个很大的 table,它是数据收集工具的输出,大致如下所示:

  DateA    |  ValueA  |   DateB    |  ValueB  | ... |    DateZ   | ValueZ
---------------------------------------------------------------------------
2019-01-01 |    3     | 2019-01-01 |    6     | ... | 2019-01-04 |   7
2019-01-02 |    1     | 2019-01-04 |    2     | ... | 2019-01-05 |   3

我想处理它,所以它会像这样:

  Date     |  Value  | Type
-----------------------------
2019-01-01 |   3     |   A
2019-01-02 |   1     |   A
2019-01-01 |   6     |   B
2019-01-04 |   2     |   B
            ...
2019-01-04 |   7     |   Z
2019-01-05 |   3     |   Z

因为这是我们的 sql 数据库使用的格式。 如何以最简单的方式做到这一点,最好是使用 PowerQuery?我想避免使用 vba 循环进行暴力复制和粘贴。 列数是固定的,但如果可以选择稍后再添加一列就好了,但是行数每天都会围绕某个值(例如 20、21、20、22、19、20)变化-天

列更难处理,所以我首先将每一列转换为一个新行作为列表。

ColumnsToRows =
    Table.FromColumns(
        {
         Table.ToColumns(Source),
         Table.ColumnNames(Source)
        },
        {"ColumnValues","ColumnName"}
    )

这应该会给您一个 table,如下所示,其中每个列表都包含相应列中的值。例如,顶部列表是 {1/1/2019,1/2/2019}。 (从列部分是添加 ColumnName 列。)

| ColumnValues | ColumnName |
|--------------|------------|
| [List]       | DateA      |
| [List]       | ValueA     |
| [List]       | DateB      |
| [List]       | ValueB     |
| [List]       | DateZ      |
| [List]       | ValueZ     |

然后我们可以根据每个列表中的数据类型对其进行过滤。要获取日期行,您可以这样写:

DataRows =
    Table.SelectRows(
        ColumnsToRows,
        each Value.Type(List.First([ColumnValues])) = type date
    )

这让你得到以下过滤 table:

| ColumnValues | ColumnName |
|--------------|------------|
| [List]       | DateA      |
| [List]       | DateB      |
| [List]       | DateZ      |

如果用 Table.ExpandListColumn(DataRows, "ColumnValues") 展开第一列,则得到

| ColumnValues | ColumnName |
|--------------|------------|
| 1/1/2019     | DateA      |
| 1/2/2019     | DateA      |
| 1/1/2019     | DateB      |
| 1/4/2019     | DateB      |
| 1/4/2019     | DateZ      |
| 1/5/2019     | DateZ      |

逻辑类似于过滤和扩展值行。

ValueRows =
    Table.ExpandListColumn(
        Table.SelectRows(
            ColumnsToRows,
            each Value.Type(List.First([ColumnValues])) = type number
        ),
        "ColumnValues"
    )

这让你看起来很相似 table:

| ColumnValues | ColumnName |
|--------------|------------|
| 3            | ValueA     |
| 1            | ValueA     |
| 6            | ValueB     |
| 2            | ValueB     |
| 7            | ValueZ     |
| 3            | ValueZ     |

现在我们只需要将我们想要的列组合成一个 table:

Combine Columns =
    Table.FromColumns(
        {
         DateRows[ColumnValues],
         ValueRows[ColumnValues],
         ValueRows[ColumnName]
        },
        {"Date", "Value", "Type"}
    )

然后提取列名称中 Value 之后的文本。

ExtractType =
    Table.TransformColumns(
        CombineColumnns,
        {{"Type", each Text.AfterDelimiter(_, "Value"), type text}}
    )

最后的 table 应该与指定的一样:

| Date     | Value | Type |
|----------|-------|------|
| 1/1/2019 | 3     | A    |
| 1/2/2019 | 1     | A    |
| 1/1/2019 | 6     | B    |
| 1/4/2019 | 2     | B    |
| 1/4/2019 | 7     | Z    |
| 1/5/2019 | 3     | Z    |

全部在单个查询中,M 代码如下所示:

let
    Source = <Source Goes Here>,
    ColumnsToRows = Table.FromColumns({Table.ToColumns(Source), Table.ColumnNames(Source)}, {"ColumnValues","ColumnName"}),
    DateRows = Table.ExpandListColumn(Table.SelectRows(ColumnsToRows, each Value.Type(List.First([ColumnValues])) = type date), "ColumnValues"),
    ValueRows = Table.ExpandListColumn(Table.SelectRows(ColumnsToRows, each Value.Type(List.First([ColumnValues])) = type number), "ColumnValues"),
    CombineColumnns = Table.FromColumns({DateRows[ColumnValues], ValueRows[ColumnValues], ValueRows[ColumnName]},{"Date", "Value", "Type"}),
    ExtractType = Table.TransformColumns(CombineColumnns, {{"Type", each Text.AfterDelimiter(_, "Value"), type text}})
in
    ExtractType