Power Query 実務テンプレ | クレンジング(前処理)系:特定文字列を含む行削除

Excel VBA Power Query M Formula Language
スポンサーリンク

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

今回のテーマは「Power Query で “特定の文字列を含む行だけをきれいに削除する” 実務テンプレ」です。

やりたいことはシンプルです。
「備考に『テスト』と入っている行は全部いらない」
「商品名に『サンプル』を含む行は分析対象から除外したい」
「顧客名に『社内用』が入っている行は本番集計から外したい」

こういう“いらない行の条件”を、文字列ベースでサクッと削れるようにします。


まず「どこに、どんな文字列が紛れているか」をイメージする

ありがちな「特定文字列を含む行」の例

例えば、こんなテーブルを想像してください。

商品コード商品名備考
A001通常商品A“”
A002通常商品B“テストデータ”
A003サンプル商品C“”
A004通常商品D(社内用)“社内検証用”
A005通常商品E“”

ここで、次のような要望があるとします。

「備考に『テスト』を含む行は削除したい」
「商品名に『サンプル』を含む行も削除したい」
「『社内用』『社内検証用』など、社内向けの行も全部いらない」

つまり、「特定の文字列を含む行を、条件でまとめて落としたい」というニーズです。


基本の型:1列の「特定文字列を含む行」を削除する

備考列に「テスト」を含む行を削除する

まずは一番シンプルなパターンからいきます。
「備考」列に「テスト」という文字列を含む行を削除するテンプレです。

let
    Source = ・・・前のステップ・・・,

    Filtered =
        Table.SelectRows(
            Source,
            each
                let
                    v = [備考],
                    t =
                        if v = null then
                            ""
                        else
                            Text.From(v)
                in
                    not Text.Contains(t, "テスト")
        )
in
    Filtered
Power Query

やっていることを言葉で整理します。

備考列の値を v として取り出す。
NULL の場合は空文字 "" にしておく(Text.Contains でエラーにしないため)。
Text.Contains(t, “テスト”) で、「テスト」を含むかどうかを判定する。
含む行は除外したいので、not Text.Contains(...) を条件にしている。

結果として、「備考に『テスト』を含む行」はすべて削除されます。


応用:大文字・小文字を無視して判定したい場合

Text.Contains の「比較方法」を指定する

英字を扱うときに、「test」「TEST」「Test」を全部同じ扱いにしたいことがあります。
その場合は、Text.Contains の第3引数に Comparer.OrdinalIgnoreCase を渡します。

Text.Contains(t, "test", Comparer.OrdinalIgnoreCase)
Power Query

これを使えば、「test」「TEST」「TeSt」など、大小文字の違いを無視して判定できます。
日本語だけなら気にしなくていいですが、「英字+日本語」が混ざる現場では覚えておくと便利です。


複数列をまとめて「特定文字列を含む行」を削除する

商品名か備考のどちらかに「サンプル」を含む行を削除したい

今度は、「商品名」か「備考」のどちらかに『サンプル』が入っていたら、その行を削除したい、というパターンです。

let
    Source = ・・・前のステップ・・・,

    Filtered =
        Table.SelectRows(
            Source,
            each
                let
                    name =
                        if [商品名] = null then
                            ""
                        else
                            Text.From([商品名]),

                    note =
                        if [備考] = null then
                            ""
                        else
                            Text.From([備考]),

                    hasSample =
                        Text.Contains(name, "サンプル")
                        or Text.Contains(note, "サンプル")
                in
                    not hasSample
        )
in
    Filtered
Power Query

ポイントはこうです。

商品名と備考の両方を文字列として取り出す。
どちらか一方でも「サンプル」を含んでいたら、その行は削除対象。
なので、hasSample を OR で作り、not hasSample をフィルタ条件にしている。

これで、「商品名にサンプル」「備考にサンプル」「両方にサンプル」——どのパターンでも、その行は落ちます。


複数の「NGワード」をまとめて管理したい

「テスト」「サンプル」「社内用」などを一括で扱う

実務では、「NGワード」が1つではなく、複数あることが多いです。

「テスト」「サンプル」「社内用」「検証用」など、
“本番集計から除外したいキーワード”をまとめて管理したい場合は、
リストにしておいて「どれか一つでも含んでいたら削除」というロジックにするとスッキリします。

let
    Source = ・・・前のステップ・・・,

    // NGワードのリスト
    NgWords = {"テスト", "サンプル", "社内用", "検証用"},

    Filtered =
        Table.SelectRows(
            Source,
            each
                let
                    // 対象とする列をまとめて文字列にする(ここでは商品名+備考)
                    name =
                        if [商品名] = null then
                            ""
                        else
                            Text.From([商品名]),

                    note =
                        if [備考] = null then
                            ""
                        else
                            Text.From([備考]),

                    targetText = name & " " & note,

                    // NGワードのどれかを含むかどうか
                    hasNg =
                        List.AnyTrue(
                            List.Transform(
                                NgWords,
                                (w) => Text.Contains(targetText, w)
                            )
                        )
                in
                    not hasNg
        )
in
    Filtered
Power Query

ここが重要ポイントです。

NgWords に「削除対象のキーワード」を全部並べておく。
商品名と備考をくっつけて、検索対象の文字列 targetText を作る。
List.Transform で「各 NGワード w について Text.Contains(targetText, w)」を評価する。
List.AnyTrue で「どれか一つでも true なら hasNg = true」と判定する。
hasNg が true(NGワードを含む行)は削除したいので、not hasNg を条件にする。

この型を一度作っておけば、NgWords にキーワードを足すだけで、
「除外条件」を柔軟に増やしていけます。


もう一歩進んだ実務テンプレ:関数化して再利用する

「特定文字列を含むかどうか」を判定する関数を作る

毎回同じロジックを書くのが面倒なら、
「NGワードリストと対象文字列を渡すと、含むかどうかを返す関数」を作っておくと便利です。

// クエリ名:fn_HasNgWord
let
    HasNgWord = (text as nullable text, ngList as list) as logical =>
        let
            t =
                if text = null then
                    ""
                else
                    Text.From(text),

            flags =
                List.Transform(
                    ngList,
                    (w) => Text.Contains(t, w)
                ),

            result = List.AnyTrue(flags)
        in
            result
in
    HasNgWord
Power Query

これを使えば、クエリ側はかなりスッキリ書けます。

let
    Source = ・・・前のステップ・・・,

    NgWords = {"テスト", "サンプル", "社内用", "検証用"},

    Filtered =
        Table.SelectRows(
            Source,
            each
                let
                    targetText =
                        Text.From([商品名]) & " " & Text.From([備考])
                in
                    not fn_HasNgWord(targetText, NgWords)
        )
in
    Filtered
Power Query

「NGワードを含む行を落としている」という意図が、コードから一目で分かるようになります。


重要ポイントの深掘り

「含む」か「完全一致」かを意識する

今回のテーマは「特定文字列を含む行削除」なので Text.Contains を使っていますが、
「完全一致のときだけ削除したい」なら、Text.Contains ではなく単純な比較にします。

例えば、「備考がちょうど『テスト』の行だけ削除したい」なら、

each [備考] <> "テスト"
Power Query

で十分です。
「含む」と「一致」は、意図が全然違うので、ここは意識的に使い分けてください。

前後の空白・全角半角・改行もセットで考える

実務では、次のような“微妙な違い”がよく混ざります。

“テスト”
” テスト “(前後に空白)
“テスト ”(全角スペース付き)
“テスト\r\n”(改行付き)

これらを全部「テストを含む」とみなしたいなら、
先に「トリム」「改行削除」「全角→半角」などのクレンジングを入れておくと、判定が安定します。

あなたがすでに作ってきた前処理テンプレ(トリム、改行削除、不正文字除去など)と、
「特定文字列を含む行削除」を組み合わせると、かなり強い“前処理パイプライン”になります。


実務テンプレとしてのまとめ

「特定文字列を含む行削除」の本質は、次の二つです。

Text.Contains(+必要なら NGワードリスト)で「削除対象かどうか」を論理値で判定する。
その判定を Table.SelectRows の条件にして、「残したい行だけ」をテーブルとして残す。

この型さえ持っておけば、
テストデータ・サンプルデータ・社内用データ・ダミー行など、
「本番集計から外したい行」を、文字列ベースで機械的に落とせるようになります。

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