この記事でわかること

  • 備考や商品名に特定の言葉を含む行だけを取り出す方法(str.contains)
  • 空欄のセルを「含まない」として扱う書き方(na=False)
  • 括弧や点のような記号を、文字のまま探す書き方(regex=False)
  • 大文字と小文字、全角と半角、数字のセルでつまずく点

Excelの問い合わせの明細から「返品」と書かれた行だけを見たいとき、検索で1件ずつ拾ってコピーしていくと、件数が増えるほど手間がかかり、拾い忘れも起きます。この記事では、pandasで明細を読み込んで、備考の列に決めた言葉がある行だけを選び、別のファイルに保存するところまでを、実際に動かした結果を添えて紹介します。

拠り所は、pandas 3.1.0の公式の資料のSeries.str.containsと、文字のデータの扱い方をまとめたユーザーガイドです。2026年11月4日に原文を読みました。商品名や問い合わせの内容は、どれも試すために用意した架空のものです。

先に結論: str.containsで真偽の列を作り、それで行を選ぶ

列にstr.contains("返品", regex=False, na=False)を使うと、言葉を含む行がTrueになる真偽の列ができます。 それを表の[ ]に渡すと、Trueの行だけが残ります。記号を文字のまま探すregex=Falseと、空欄を含まない扱いにするna=Falseは、毎回付けておくと安心です。

やりたいこと 書き方 試した明細での結果
言葉を含む行を選ぶ str.contains("返品", regex=False, na=False) 10行から3行
大文字と小文字を区別しない case=Falseを足す 「usb」で1行から3行
言葉を含まない行を選ぶ 結果の前に~を付ける 7行
空欄のセルを含まない扱いにする na=False 備考が空欄の1行は選ばれない

str.containsのしくみ

pandasの資料では、str.containsを、型や正規表現が文字列の中に含まれるかを調べ、結果を真偽(TrueとFalse)の列で返すものとしています。ユーザーガイドの説明では、似た働きのmatchは文字列の先頭から、fullmatchは文字列の全体が一致するかを見るのに対し、containsは文字列のどの位置にあっても一致とみなします。

引数のcaseは大文字と小文字を区別するかで、初めの設定はTrue(区別する)です。regexは探す語を正規表現として読むかで、初めの設定はTrueです。Falseにすると、渡した語をそのままの文字として探します。資料の例でも、正規表現の「.0」は「どれか1文字の後に0」と読まれるため、「40」にも一致すると注意しています。

空欄や数字のセルの扱い

pandas 3.0からは、文字の列を読み込むと、文字用の型(str)になります。ユーザーガイドによると、この型の空の値はNaNです。str.containsの資料では、空の値の結果をnaの引数で決め、何も渡さないときは列の型によって変わるとしています。str型ならFalse、いろいろな値が混ざったobject型ならNaNになります。

文字と数字のセルが混ざった列は、object型として読み込まれます。数字のセルは文字列ではないため、結果にNaNが入り、その結果で行を選ぼうとするとエラーで止まります。na=Falseを付けると、空欄も数字も「含まない」として扱われます。数字のセルも文字として調べたいときは、先にastype(str)で文字に直します。

手順: 問い合わせの明細から「返品」を含む行を書き出す

次のコードは、架空の問い合わせの明細 toiawase.xlsx を読み込み、備考の列に「返品」を含む行を toiawase_henpin.xlsx に書き出します。元の toiawase.xlsx は書きかえません。ファイルの名前と探す言葉は、自分の明細のものに書きかえてください。

import pandas as pd

toiawase = pd.read_excel("toiawase.xlsx")  # 読むだけで、上書きはしない

word = "返品"
# 記号も文字のまま探し、空欄のセルは「含まない」として扱う
found = toiawase["備考"].str.contains(word, regex=False, na=False)
hits = toiawase[found]

print("読み込んだ行:", len(toiawase))
print(f"「{word}」を含む行:", int(found.sum()))
print(hits[["受付日", "商品名", "備考"]].to_string(index=False))
hits.to_excel("toiawase_henpin.xlsx", index=False)

foundは、行ごとにTrueかFalseが入った列です。sumでTrueの数を数えられるので、書き出す前に件数を画面に出し、見つけた行の中身と見比べます。xlsxの読み書きには、pandasのほかにopenpyxlが要ります。

試した結果

受付日・商品名・型番・備考の4列で、データの行が10行の架空の明細で動かしました。備考には「返品したい」「交換の相談」「返品ではなく修理を希望」などを入れ、1行は空欄にしました。型番の列には、「C-150」のような文字と、2041のような数字を混ぜました。

  • 「読み込んだ行: 10」「「返品」を含む行: 3」と表示されました
  • 見つかったのは、USBケーブル(1.5m)・USB-C充電器・キーボードの3行でした
  • キーボードの備考は「返品ではなく修理を希望」で、返品の依頼ではありませんが、「返品」の2文字を含むため選ばれました
  • 備考が空欄のマウスの行は、選ばれませんでした
  • toiawase_henpin.xlsx は4列・3行で、受付日の古い順に並んでいました
  • 元の toiawase.xlsx は、実行の前と後でハッシュ値が一致しました

動かしたのは手元のMacで、pandasの版は3.1.0です(Pythonは3.12.15、openpyxlは3.1.5)。書き出したファイルはExcelのアプリでは開かず、openpyxlで読み直して中身を見ました。

自分で絞り込んでよいか、相談するかの判断

目安は、「言葉が入っているかどうかで、行の扱いを決めてよいか」です。返品の問い合わせを先に読みたい、ある商品の行だけを集計したい、のように人が後で中身を読む使い方なら、上のコードで絞り込んでかまいません。

言葉を含むかどうかだけで、返金や在庫の処理を自動で進めるような使い方は、先に相談します。試した結果のように「返品ではなく」のような否定の文も選ばれますし、書く人によって「返却」「戻し」のような別の言い方もあります。抜き出した行は必ず目で確かめ、どの言葉で探すかを明細を作っている人とそろえておきます。

つまずきやすいところ

  • 否定の文の行まで選ばれる: 「返品」で探すと、「返品ではなく修理を希望」の行も入りました。str.containsは言葉の意味ではなく文字の並びを見ます。件数だけで判断せず、選ばれた行を読みます
  • 括弧の付いた語が、別の行にも一致する: regexを付けずに商品名を「(1.5m)」で探すと、括弧の無い「ケーブル1.5m」の行にも一致して2行になり、正規表現の括弧についての警告(UserWarning)も出ました。regex=Falseにすると、括弧付きの1行だけになりました
  • 全角の「USB」が見つからない: case=Falseで「usb」を探すと、USBケーブル・usbハブ・USB-C充電器の3行になりましたが、全角で書かれた「USBメモリ」は入りませんでした。全角の文字を半角に直すなら、PythonでExcelの表の文字の前後の空白と全角・半角をそろえる手順を先に使います
  • 数字のセルが混ざった列でエラーになる: 型番の列をna=Falseなしで調べると、数字のセルの結果がNaNになり、行を選ぶところで、空の値を含む真偽の列では行を選べないというエラー(ValueError)が出ました。na=Falseを付けるか、astype(str)で文字に直してから調べます
  • 日付の列には使えない: 受付日の列にstr.containsを使うと、.strは文字列の値にしか使えないというエラー(AttributeError)が出ました。日付で行を選ぶときは、文字ではなく日付として比べます
  • 等しい値や一覧の値で選びたい: 「返品」と完全に同じ値の行や、決めた商品の一覧に入る行を選ぶときは、PythonでExcelの表から条件に合う行を抽出する手順のやり方が向いています
  • 行ではなく、セルの中の番号だけがほしい: 行を選ぶのではなく、セルの文字列から型番や番号の部分だけを取り出すなら、PythonでExcelの文字列から番号やコードを抽出する手順を見てください

まとめ

  • 言葉を含む行は、str.containsで真偽の列を作り、表の[ ]に渡して選びます
  • regex=Falseで記号を文字のまま探し、na=Falseで空欄や数字のセルを「含まない」として扱います
  • 大文字と小文字はcase=Falseでそろえられますが、全角と半角は先にそろえます
  • 文字の並びだけを見るので、選ばれた行の中身と件数を確かめてから書き出します