(Power BI) 列名を修正しながら複数のCSVをまとめる : Mから始めよう #12

　11回目では、複数のエクセルファイルをまとめる方法を説明しました。今回はCSVファイルのまとめ方で、なおかつファイルによって列名の書き方が違っている場合でもうまくいくようにします。

　列名が違っているものが混じってる時、１つ１つ直してからまとめる作業をやっていませんか？違いにパターンがある場合、変換テーブルを作成して、列名を自動で変更しながらまとめあげます。

# パラメータの設定
　フォルダーの位置をパラメータに設定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/41fee6a1-cae9-ee5b-a03a-0f15a3b264ca.png)

# 列名変更ルールの作成
　変更前、変更後のテーブルを作成します。
「データの入力」で作成すれば簡単に作成できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/541a915b-3e2e-4155-116f-f4ac7f61f152.png)
　Power Queryのコードで書く場合は、以下のように書きます。

```javascript
let
    Source = Table.FromRecords(
        {
            [BEFORE="Segment", AFTER="区分名"],
            [BEFORE="Country", AFTER="国名"],
            [BEFORE="Product", AFTER="製品名"],
            [BEFORE="Discount Band", AFTER="割引幅"],
            [BEFORE="Units Sold", AFTER="販売数"],
            [BEFORE="Manufacturing Price", AFTER="原価"],
            [BEFORE="Sale Price", AFTER="単価"],
            [BEFORE="Sales Price", AFTER="単価"],
            [BEFORE="Gross Sales", AFTER="総売上高"],
            [BEFORE="Discounts", AFTER="割引"],
            [BEFORE=" Sales", AFTER="売上高"],
            [BEFORE="Sales", AFTER="売上高"],
            [BEFORE="COGS", AFTER="売上原価"],
            [BEFORE="Profit", AFTER="利益"]
        },
        type table[
            BEFORE = Text.Type,
            AFTER = Text.Type
        ]
    )
in
    Source
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/2049b573-df25-84ae-d4a9-3fc907662777.png)
　今回は、列名をすべて日本語に変換するほか、"Sale Price"は"Sales Price"となっている場合があり、"Sales"は、頭に空白が入ってることがあるため、いずれもまとめる際に１つの列に入るようにします。


# ソースコード
　全体のコードは、以下のようになります。

```text
let
    Source = Folder.Files(FolderPath),
    // CSVデーターソースにアクセスし、１行目をヘッダーに設定
    Step1 = Table.TransformColumns(
        Source,
        {
            "Content",
            each
                Table.PromoteHeaders(
                    Csv.Document(_, [Encoding=932])  // Shift_JIS
                )
        }

    ),
    // マッピングテーブルを参照して列名を変換
    Step2 = Table.TransformColumns(
        Step1,
        {
            "Content",
            each
                Table.RenameColumns(
                    _,
                    Table.ToRows(Mapping),
                    MissingField.Ignore
            )
        }
    ),
    // データテーブルを結合
    Step3 = Table.Combine(
        Step2[Content]
    )
in
    Step3
```


# CSVファイルを読み込む
　Sourceにフォルダー内のファイルを読み込みます。
　フォルダーの中に、まとめるCSVファイル以外のものが混じっている場合は、１ステップ追加して、Name列かExtension列でフィルターをかけます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/43a4f45f-2c54-9ff2-bb07-5fffbc311962.png)
　読み込んだ状態では、上記のように、ファイルの中身はBinaryになっているので、Step1でこれをCSVと認識させ、１行目を列名にします。
　今回のファイルは、漢字コードがShift_JISになっていたため、**Encoding=932**を付けています。UTF-8の場合は、**Csv.Document(_)**で読み込めます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/2df31ef3-fc6a-417f-e5d9-d597a94bd1ce.png)

# 列名の変換
## Table.RenameColumnsを使う
　Table.RenameColumnsを使用して、列名を変更します。
　最初に、Table.TransformColumnsで、Content列にあるテーブルを１つ１つ処理(each)するようにします。
　そのなかで、Table.RenameColumnsを使いますが、第２引数には、列名が新・旧のリスト形式になっている必要がありますが、最初作った変換テーブルは、テーブル形式です。これを、Table.ToRowsを使ってリスト形式にしています。第３引数のMissingField.Ignoreは、当てはまる列名がなくてもエラーにならないようにするオプションです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/0218e473-e5bb-fc02-ada4-57611c64bec3.png)

## Table.TransformColumnNamesとList.Accumulateを使う
　**List.Accumulate**を使って列名を変換するやり方がYoutubeにありました。
<iframe width="560" height="315" src="https://www.youtube.com/embed/wKglApDFMog" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

　そのやり方でStep2を書き換えると以下のようになります。
　このやり方は、Table.RenameColumnsを使うより複雑です。

```text
    // マッピングテーブルを参照して列名を変換
    // How to Merge Excel Files with Different Headers in Power Query | List.Accumulate
    // https://www.youtube.com/watch?v=wKglApDFMog
    Step2 = Table.TransformColumns(
        Step1,
        {
            "Content",
            (_Table) => Table.TransformColumnNames(
                _Table, 
                (_ColumnName)=> List.Accumulate(
                    Table.ToRecords(Mapping),
                    _ColumnName, 
                    (state, current) => 
                        Text.Replace(
                            state,
                            current[BEFORE], 
                            current[AFTER]
                        )
                )
            )
        }
    ),
```

　最初に、Table.TransformColumnsでContent列のテーブルをTable.TransformColumnNamesに渡します。
　次に、Table.TransformColumnNamesは、現在の列名の変換先をList.Accumulateで探します。
　List.Accumulateは、現在の列名を変換テーブルのBEFOREを順番に当たって、見つかったらAFTERの値を返します。見つからない場合は、現在の列名が返ります。つまり、変更されません。

　**accumulate**とは蓄積する、累算するという意味で、state、currentを使って処理が積み重なっていきます。最初にシード値として現在の列名が与えられ、変換テーブルのBEFOREに一致する値があった場合、AFTERに置き換えられます。
　注意が必要なのは、Text.Replaceで行われるのは、部分一致だということです。例えば、変換テーブルに **[BEFORE="Sale", AFTER="売"]** のようなレコードが入っていた場合、正しく動作しません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/1a1a0928-1446-5a05-3bcb-b599a3b56e5b.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/114df7ac-8481-822a-511e-c284547822cc.png)
　そこで、以下のように列名の比較部分を書き換えて、完全一致で変換するようにします。

```text
    Step2 = Table.TransformColumns(
        Step1,
        {
            "Content",
            (_Table) => Table.TransformColumnNames(
                _Table, 
                (_ColumnName)=> List.Accumulate(
                    Table.ToRecords(Mapping),
                    _ColumnName, 
                    (state, current) => 
                        if state = current[BEFORE] then
                            current[AFTER]
                        else
                            state
                )
            )
        }
    ),
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/9481a78b-f3ee-6352-8435-2d628b8f229e.png)

# 表を結合する

## CSVの中身だけを結合する
 一番簡単な方法で、Table.Combineを使って列を指定してやれば、一気に結合することができます。最初のコードは、このやり方です。

## ファイル名も残して結合する１
　各テーブルの列名を取り出してリストを作成し、Table.ExpandColumnで展開します。

```text
    // データテーブルを結合
    Step3 = Table.ExpandTableColumn(
        Step2,
        "Content",
        List.Union(
            List.Transform(
                Step2[Content],
                each Table.ColumnNames(_)
            )
        )
    )
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/a3a3eb96-b5f4-8027-35f0-8222664ac43a.png)
　現在の表はそのままで、表の頭にテーブルのデータが展開されます。

## ファイル名も残して結合する２
　CurbalのYoutubeで紹介されている、CSVテーブルの中にファイル名の項目を追加する方法です。
<iframe width="560" height="315" src="https://www.youtube.com/embed/hJF2dPfIeaM" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

```text
    // データテーブルにファイル名の列を追加
    Step3 = Table.AddColumn(
        Step2,
        "NewContent",
        each
            Table.AddColumn(
                [Content],
                "File",
                (x) => [Name]
            )

    ),
    // データテーブルを結合
    Step4 = Table.Combine(
        Step3[NewContent]
    )
```

　Content にファイル名の列を追加した、新しい列 NewContent を作成しています。これを、Table.Combineで結合すれば、ファイル名の入ったテーブルが出来上がります。ファイル名の列は、後ろの方につきます。
　位置は、最初のテーブルの最後に追加されますが、他のテーブルで項目が増えた場合、その項目が後ろに追加されるため、必ずしも位置が最後とはなりません。
　また、この式の説明は [#11](https://qiita.com/spumoni/items/6c9d249514b0a26e2f60#ファイル名シート名を残してシートの項目をまとめる２-tableaddcolumnでファイル名を入れ込む) で行っていますので、そちらを参照してください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/864075/5ab6fbf7-a74c-7bac-2f31-3f28a638db94.png)