Power Query 実務テンプレ | データ取込・更新系:CSVのヘッダー行が途中にあるデータ取込

Excel
スポンサーリンク

ゴールのイメージを先にそろえる

今回のテーマは「CSVのヘッダー行が途中にある(しかも場合によっては何回も出てくる)データを、Power Queryでキレイに取り込む実務テンプレ」です。

よくあるパターンは大きく二つです。

1つ目は「最初の数行がタイトルや説明で、本当のヘッダーは3行目以降にある」パターン。
2つ目は「複数CSVをフォルダから結合した結果、ファイルごとにヘッダー行が途中に何度も出てくる」パターンです。

この二つを、プログラミング初心者でも扱えるレベルまでかみ砕いて、Mコード付きでテンプレ化していきます。


パターン1:ヘッダーが最初の行ではなく途中にある

例題データのイメージ

まずは、こんなCSVをイメージしてください。

売上レポート 2025年1月分
出力日: 2025/02/01

日付,商品名,数量,金額
2025/01/01,りんご,10,1200
2025/01/02,みかん,5,500
...

最初の2〜3行は「タイトル」「出力日」などの情報で、本当に欲しいヘッダーは「日付,商品名,数量,金額」の行です。
この場合、「上の不要行を飛ばしてから、ヘッダー昇格する」というのが基本パターンになります。

手動操作での考え方(Power Query画面)

Power Queryの画面上でやるときの流れは、ざっくりこうです。

上位の行を削除 → 本当のヘッダー行を一番上に持ってくる → 「最初の行をヘッダーとして使用」

これをMコードで書くと、Table.SkipTable.PromoteHeaders の組み合わせになります。

Mコードでのテンプレ(固定行数をスキップ)

「ヘッダーは必ず4行目にある」と分かっているケースのテンプレです。

let
    Source = Csv.Document(
        File.Contents("C:\Data\incoming\sales.csv"),
        [
            Delimiter = ",",
            Encoding = 65001,
            QuoteStyle = QuoteStyle.Csv
        ]
    ),

    // 上から3行をスキップ(4行目が先頭になる)
    SkippedTop = Table.Skip(Source, 3),

    // 先頭行をヘッダーに昇格
    Promoted = Table.PromoteHeaders(
        SkippedTop,
        [PromoteAllScalars = true]
    )
in
    Promoted

ここで重要なのは「どこまでを“ゴミ行”とみなすかを、行数で決めている」という点です。
タイトル行の構造が毎回同じなら、これだけで十分実務テンプレとして使えます。

行数が変わる場合の考え方(ヘッダー行を探す)

もう少し現実的なパターンとして、「タイトル行の行数が日によって微妙に違うけれど、ヘッダーの文字列は毎回同じ」というケースがあります。

たとえば、ヘッダー行が必ず「日付,商品名,数量,金額」という文字列で始まるなら、その行を探してそこまでスキップする、というロジックにできます。

イメージはこうです。

  1. いったん全部テーブルとして読み込む
  2. 「日付」「商品名」など、ヘッダー候補の行を探す
  3. その行番号より上を全部スキップする
  4. 残ったテーブルの先頭行をヘッダーに昇格する

Mコードの一例です。

let
    Source = Csv.Document(
        File.Contents("C:\Data\incoming\sales.csv"),
        [
            Delimiter = ",",
            Encoding = 65001,
            QuoteStyle = QuoteStyle.Csv
        ]
    ),

    // 各行をリストとして扱うために、インデックスを付与
    Indexed = Table.AddIndexColumn(Source, "Index", 0, 1, Int64.Type),

    // ヘッダー行を探す(ここでは1列目が「日付」の行をヘッダーとみなす)
    HeaderRow = Table.SelectRows(
        Indexed,
        each [Column1] = "日付"
    ),

    // 見つかったヘッダー行のインデックスを取得
    HeaderIndex = HeaderRow{0}[Index],

    // ヘッダー行より上をスキップ
    SkippedTop = Table.Skip(Indexed, HeaderIndex),

    // もう一度インデックス列を削除して元の形に戻す
    RemovedIndex = Table.RemoveColumns(SkippedTop, {"Index"}),

    // 先頭行をヘッダーに昇格
    Promoted = Table.PromoteHeaders(
        RemovedIndex,
        [PromoteAllScalars = true]
    )
in
    Promoted

ここでの重要ポイントは、「ヘッダー行を“文字列で探す”」という発想です。
これにより、「タイトル行の行数が変動する」という現場あるあるにも対応できます。


パターン2:途中に何度もヘッダー行が出てくる(フォルダ結合など)

例題データのイメージ

次は、フォルダ内の複数CSVを結合したときによく起きるパターンです。

たとえば、1つ1つのCSVがこうなっているとします。

日付,商品名,数量,金額
2025/01/01,りんご,10,1200
2025/01/02,みかん,5,500

これが3ファイルあって、フォルダから一括取り込みすると、結合結果はこんな感じになります。

日付,商品名,数量,金額
2025/01/01,りんご,10,1200
2025/01/02,みかん,5,500
日付,商品名,数量,金額
2025/01/03,バナナ,3,450
...

つまり、「途中にヘッダー行が何度も出てくる」状態です。
このままだと集計時に邪魔なので、「途中のヘッダー行だけを削除する」必要があります。

一番シンプルな考え方:ヘッダーと同じ行を削除する

一番分かりやすいロジックは、「1行目のヘッダーと同じ内容の行を、データ部分から削除する」です。

手順を言葉で書くとこうなります。

最初の行をヘッダーに昇格する → データ部分の中で「日付」などヘッダーと同じ値を持つ行を削除する

Mコードの例です。

let
    // フォルダから結合した後のテーブルを想定
    Source = ...,

    // 先頭行をヘッダーに昇格
    Promoted = Table.PromoteHeaders(
        Source,
        [PromoteAllScalars = true]
    ),

    // 1列目が「日付」の行はヘッダー行とみなして削除
    RemovedHeaderRows = Table.SelectRows(
        Promoted,
        each [日付] <> "日付"
    )
in
    RemovedHeaderRows

ここでのポイントは、「ヘッダーと同じ文字列を持つ行をフィルタで落とす」というシンプルさです。
1列目の列名が「日付」で固定されているなら、これだけでかなり実務的に使えます。

もう少し厳密に判定したい場合(複数列で比較)

もし「日付」列にたまたま「日付」という文字列が入る可能性がある、という場合は、複数列を組み合わせて判定する方が安全です。

たとえば、「日付」「商品名」「数量」「金額」の4列すべてがヘッダーと同じなら、その行はヘッダー行とみなす、というロジックにできます。

let
    Promoted = ...,

    RemovedHeaderRows =
        Table.SelectRows(
            Promoted,
            each not (
                [日付] = "日付" and
                [商品名] = "商品名" and
                [数量] = "数量" and
                [金額] = "金額"
            )
        )
in
    RemovedHeaderRows

ここでの重要ポイントは、「ヘッダー行を“条件で表現する”」という考え方です。
条件さえ書ければ、途中に何回出てきても、すべてまとめて削除できます。


重要ポイントの深掘り

Table.Skip と「上位の行の削除」の関係

Power QueryのUIで「行の削除 → 上位の行の削除」をすると、裏側では Table.Skip が使われます。

Table.Skip(テーブル, 行数) は、「先頭から指定した行数だけ飛ばして残りを返す」関数です。

固定行数を飛ばすだけなら、UI操作で十分ですが、
「ヘッダー行の位置が変わる」「条件で行を探したい」といった場合は、
Mコードで Table.Skip を組み合わせる方が柔軟になります。

Table.PromoteHeaders のタイミング

Table.PromoteHeaders は「先頭行をヘッダーに昇格する」関数です。

大事なのは、「本当にヘッダーにしたい行が先頭に来てから実行する」ことです。

ヘッダーより上にゴミ行が残っている状態で Table.PromoteHeaders を実行すると、
ゴミ行が列名になってしまい、その後の処理がすべてやりにくくなります。

実務テンプレとしては、

不要行を削除 or スキップ → ヘッダー行を先頭にそろえる → Table.PromoteHeaders

という順番を、頭の中の「型」として持っておくと安定します。

「途中ヘッダー削除」は最後の方でやるのが楽

フォルダ結合などで途中にヘッダー行が混ざる場合、
削除処理は「ヘッダー昇格のあと、型変換の前」くらいに入れるのが扱いやすいです。

理由はシンプルで、「列名が確定していた方が条件を書きやすい」からです。
列名が「Column1」「Column2」のままだと、どれが日付なのか分かりにくくなります。


実務テンプレとしてのまとめイメージ

ここまでの話を、実務テンプレの流れとしてまとめると、こんな感じになります。

ヘッダーが途中にある場合は、「ヘッダー行を探してそこまでスキップ → ヘッダー昇格」。
途中に何度もヘッダーが出てくる場合は、「ヘッダー昇格 → ヘッダーと同じ行を条件で削除」。

どちらも、「ヘッダー行をどう見つけるか」「どう表現するか」が肝です。
一度、自分のCSVのヘッダー文字列を眺めて、「どんな条件なら一意に判定できるか」を言葉で書き出してみると、Mコードに落とし込むのが一気に楽になります。

タイトルとURLをコピーしました