文字をそのまま探したいなら、まずstr.contains(検索語, regex=False, na=False)を使います。 ただし「含まない行」へ反転するときは、欠損値まで非一致として拾っていないかを別に確かめてください。エラーが消えることと、必要な行だけを抽出できることは別です。
対象は、CSVやExcelをpandasへ読み込み、文書名・商品名・メモなどで行を絞りたい人です。架空の文書一覧9行を使い、ピリオドを含む文字、空欄、数字の混入、大文字と小文字を切り分けます。自分の表を一括修正する前に、小さな一覧で検索の意味を説明できるようにしましょう。
一次資料確認日:2026年10月17日。str.containsのAPI、文字列型の移行ガイド、isnaの仕様、Pythonのre.escapeを参照しました。pandas公式資料の表示は3.0.6、掲載コードの実行環境はPython 3.12.14・pandas 2.2.3です。版をまたいで既定の欠損処理が同じとは想定せず、教材では型と引数を明示します。
str.containsの欠損処理と検索語の解釈を切り分ける
最初に、検索する列・語句・残したい行を決める
str.containsは、Seriesの各値に検索語が含まれるかを調べます。DataFrame全体へまとめて使うのではなく、まず文書名などの一列を選びます。検索結果は行ごとの判定であり、元の表の値を書き換える操作ではありません。
例えば「設計v1.0という文字が文書名に入る資料を探す」という依頼なら、検索語はv1.0です。「設計の第1版らしい資料を探す」という依頼では、表記揺れをどこまで含めるかが未定です。似た言い方でも、抽出条件を同じコードで済ませられるとは限りません。
先に、採用したいIDを数件書きます。本稿ではA01の「設計v1.0」は対象、A02の「設計v1x0」は対象外です。この二行を区別できることを、コードの実行前の期待値にします。検索結果を見てから都合よくルールを変更しないための準備です。
列名も確認してください。文書名が複数列ある表で、表示名とファイル名を取り違えると、コードが正常でも依頼とは違う結果になります。CSVの列選択で止まっている場合は、usecolsと列名を調べる方法を先に使えます。
検索語の前後の空白を無条件に削るかも、依頼に含めて決めます。文書名そのものに空白がある場合、その空白を含む語句を探したいこともあります。後述のコードは空だけの検索を拒みますが、入力された語句を勝手に整形して別の検索へ変えません。
実務では、抽出対象の列名、検索語、普通の文字として探すか、大文字を区別するか、不明な行の置き場所を一組の条件として残すと便利です。「このCSVから検索しました」より、同僚が同じ結果を再現するための情報になります。
na=Falseは、欠損を抽出条件の中でFalseにする
検索する列に欠損があると、文字が含まれるか判定できない行が生まれます。その判定に何を置くかを指定するのがnaです。na=Falseを付けると、今回の検索結果では、その行をFalseとして扱います。元の欠損セルへ文字やゼロを書き込む処理ではありません。
ここで区別したいのは「語句が入っていない」と「値がなくて調べられない」です。担当者が入力を済ませた文書名に検索語がないなら非一致と説明できます。文書名が未入力なら、その文書に何が含まれるかは、検索結果だけでは分かりません。
含む行だけが必要な一時的な検索なら、欠損をFalseにして対象から外す方法で目的を満たせることもあります。その場合も、欠損行が何件あったかを記録しておくと、抽出件数が減った理由を後から調べられます。エラー回避の引数に、データの意味まで任せないことがポイントです。
欠損と空文字も同じではありません。公式のisnaは、空文字列を欠損と同一には扱いません。見た目が空のセルでも、Noneなのか、長さゼロの文字列なのか、空白だけが入っているのかで、処理する条件が変わります。本稿は業務上の確認待ちとして、これらを整理します。
例えば「文書名が入力済みの資料だけを仕分ける」という仕事なら、空文字も確認待ちへ寄せる運用にできます。これはpandasの欠損仕様を変更するという意味ではなく、成果物に必要な情報がそろっているかを決める、編集部の教材上のルールです。
欠損を補う必要がある場面では、どの根拠で補ったかを別に残します。検索を通す目的だけで未入力を適当な文書名に置き換えると、後から本来の情報と見分けられません。欠損と重複を整理する前工程は、CSVの欠損・重複を検算する練習が関連します。
regex=Falseは、ピリオドやプラスを普通の文字として探す
regexの既定値はTrueです。その場合、検索語は正規表現として解釈されます。正規表現のピリオドは、文字としての点を探す指定と同じではありません。v1.0を探したつもりでv1x0も拾うなら、欠損とは別に検索語の解釈を調べます。
文字通りのバージョン名やファイル名を探すなら、regex=Falseを明示すると意図を表しやすくなります。本稿の二つの設計書では、FalseのときはA01だけ、TrueのときはA01とA02が抽出されます。後者も処理上の失敗ではなく、異なるルールによる検索です。
プラス記号や丸括弧などにも、正規表現で特別な意味を持つものがあります。C++の教材名を普通の文字として探したいなら、記号の意味を一つずつ推測する前に、Falseを選ぶ理由を確認してください。正規表現を使わない検索へ、余計なエスケープを何重にも重ねる必要はありません。
一方、複数の表記をパターンとして扱うなら、正規表現が役立つ場合があります。最初からFalseを正解、Trueを間違いと分類するのではなく、今回の依頼が文字そのものを探す仕事か、パターンで探す仕事かを決めます。引数名だけを覚えるより、結果の説明がしやすくなります。
検索語を利用者が入力する仕組みでは、検索方式を画面や説明文でも伝えます。普通の文字検索と案内しながら正規表現として処理すると、利用者は記号の意味を知らないまま異なる結果を受け取ります。今回のコードは、固定した架空の語句で方式の違いを確認する教材です。
設定を変えた後は、検索語が見える行だけでなく、似た行が除外されたことも照合します。一致件数が一件になっただけでは、その一件がA01だとは分かりません。数とIDを一緒に見ると、たまたま同じ件数になった誤抽出を避けやすくなります。
| 指定・条件 | 扱う問題 | この教材での選び方 |
|---|---|---|
| na=False | 欠損行の検索判定 | 含む行の検索から外す。ただし不明行のIDも別に残す |
| regex=False | 検索語の記号 | v1.0やC++を文字通りに探す |
| case=False | 大文字と小文字 | PYTHONとpythonを同じ語として探すときだけ使う |
| 入力済みかの条件 | 欠損・空文字・非文字列 | 文書名として確認できない行は非一致へ混ぜない |
object・string・strで、欠損判定の既定を同じと考えない
現行の公式APIでは、欠損時の既定の判定は列の型によって異なると説明されています。object型、欠損を扱うStringDtype、pandas 3.0で導入されたstr型の説明を、古い記事の一つの例へまとめないでください。別の環境でエラーが再現しない場合は、最初に版と型を見ます。
本稿は実行環境がpandas 2.2.3であるため、3.0.6の既定動作を実測したとは書きません。コードではastype("string")と引数指定を用い、検索の前提を明示しています。新しい版で試すときも、同じ架空データと期待IDを使い、結果を確かめてください。
エラーメッセージを検索する際は、列全体が数字になっていないかも調べます。すべて数値の列へ文字列用のアクセサを使う問題は、検索結果に欠損がある問題と別です。文書名を探すつもりで管理番号の列を指定したなら、型を変えて通すより、対象列を直すほうが依頼に合います。
文字列へ変換する場合も、変換前の値は残します。数値の101を文字列へ変えれば、文字として101を探すことはできます。しかし今回の列は文書名という前提なので、元から数字だったA08は入力の確認が必要です。変換できたことを、その値が適切だった証拠にはしません。
反対に、商品コードの仕様が数値と文字列の混在を許しているなら、今回とは異なる受入ルールを作れます。どの列も非文字列を除く、という一般論にはしていません。型の変更と、業務上その値を使ってよいかの判断を別に記録してください。
同僚へ実行結果を渡すときは、pandasのバージョン、元の列の型、変換の有無を添えます。Pythonのエラー画面だけでは、検索に使った列や前処理の違いが伝わりません。元データを共有できない場合でも、同じ型の混在を架空の数行で再現することはできます。
「含まない」を反転すると、欠損まで入る理由を確かめる
否定条件は、~で判定を反転して作れます。ところが、先にna=Falseで欠損をFalseへ置いた結果を反転すると、その行はTrueになります。「検索できなかった」が「含まない」に変わるため、未入力の文書も抽出対象へ入ります。
これは~が欠損の意味を理解していないという話ではありません。反転する段階では、欠損由来だったという区別をFalseの中に残していないためです。後から結果だけを眺めても、文字を調べてFalseだった行と、値がなくFalseへ置かれた行を区別できません。
本稿では、先に入力済みの文字列かどうかを判定し、その範囲の中で一致と非一致を分けます。入力済みでない行は確認待ちです。文書名が分かっている行だけを他部署へ渡したいなら、この三つの区別が受け渡しの説明になります。
例えば「旧版という文字を含まない資料を送る」という依頼では、文書名が未入力の資料を新しい版だと推測しないほうがよいでしょう。検索語がないことだけで、資料の版や公開可否まで分かるわけでもありません。まず検索可能な範囲を限定し、必要な別条件を後で追加します。
処理の都合で二択にする必要があるときも、不明値をどちらへ寄せたかを仕様として書きます。システム上の二択と、人間の判断に必要な情報は同じではありません。確認待ちのIDを別の一覧へ残すだけでも、何をまだ確認していないかが明確になります。
三つへ分けた後には、同じ行が二つの群に入っていないか、全件がどれかに入っているかも確かめます。検索の正しさだけでなく、仕分けの途中で行が消えていないかを見るためです。以下の実行例では、その条件もIDの集合で検算します。
架空の文書名9行で、一致・非一致・確認待ちを検算する
元の型を残したまま、三つの行群を作る
教材には、設計書二つ、未入力、空文字、C++とCの入門資料、大文字と小文字のPython資料、数字だけの値を入れます。実在の社内文書や顧客情報は使いません。検索語を変更しても、どのIDが動いたか追えるように、一覧の順番を固定します。
コードのknownは、今回の文書名として判定できる行を表します。元の値が文字列で、空白だけではないことを条件にしました。検索用の変換後の列とは別に元の列を見るので、数値101が変換によって紛れ込むことを防げます。
foundは語句が含まれるかの判定です。knownと組み合わせて一致と非一致を作り、その反対を確認待ちにします。行を削除したり、文書名を書き換えたりせず、各群に含まれるIDだけを返す形にしました。ファイルの上書きや外部への送信もありません。
import pandas as pd
df = pd.DataFrame({
"ID": ["A01", "A02", "A03", "A04", "A05", "A06", "A07", "A08", "A09"],
"文書名": ["設計v1.0", "設計v1x0", None, "", "C++入門", "C入門",
"PYTHON 基礎", 101, "python入門"],
})
def split_rows(keyword, case=True):
if not isinstance(keyword, str) or not keyword.strip():
raise ValueError("空でない文字列の検索語を指定してください")
raw = df["文書名"]
text = raw.astype("string")
known = raw.map(lambda v: isinstance(v, str)) & text.fillna("").str.strip().ne("")
found = text.str.contains(keyword, regex=False, case=case, na=False)
masks = {"一致": known & found, "非一致": known & ~found, "確認待ち": ~known}
return {name: df.loc[mask, "ID"].tolist() for name, mask in masks.items()}
result = split_rows("v1.0")
assert result == {"一致": ["A01"],
"非一致": ["A02", "A05", "A06", "A07", "A09"],
"確認待ち": ["A03", "A04", "A08"]}
groups = [set(ids) for ids in result.values()]
assert not (groups[0] & groups[1] or groups[0] & groups[2] or groups[1] & groups[2])
assert set.union(*groups) == set(df["ID"])
print(result)
ここでのfillna("")は、入力済みかを判定する一時的な式の中だけで使っています。元の文書名へ空文字を保存したわけではありません。後からA03の欠損とA04の空文字を別々に調べられるよう、元の表を残しています。
IDが重複する実データでは、集合にするだけで行数の欠落を検算できないことがあります。本稿はA01からA09が一意である教材です。実務へ移す際は、IDの一意性を確認するか、元の行番号などの別の識別方法も組み合わせてください。
一致1行・非一致5行・確認待ち3行の理由を読む
検索語v1.0を普通の文字として探すので、一致するのはA01です。A02には似た並びがありますが、ピリオドではなくxが入っています。この違いを守ることが、最初に書いた検索依頼に合う結果です。
非一致の五行は、文書名が入力された文字列であり、その中に検索語がありません。C++の資料やPythonの資料を、値が欠けている行と同じ理由で除外したわけではありません。この群の説明は「入力済みの名前を検索し、該当語がなかった」です。
確認待ちの三行には別々の理由があります。A03は欠損、A04は空文字、A08は元の値が数値です。いずれも今回の文書名としては判断を止めますが、修正する作業は同じではありません。確認待ちの件数だけでなく、元の値を参照できるIDを残すことに意味があります。
| 行・値 | 分類 | 次に判断すること |
|---|---|---|
| A01 設計v1.0 | 一致 | 指定した語句を含む。文書の中身や版の正しさは別に確認 |
| A02 設計v1x0 | 非一致 | 似ていてもピリオドではない。語句の条件を維持 |
| A05・A06・A07・A09 | 非一致 | 名前は入力済みだが、指定語句を含まない |
| A03 欠損 | 確認待ち | 原資料から文書名を確認 |
| A04 空文字 | 確認待ち | 未入力なのか、空欄に意味があるのか確認 |
| A08 数値101 | 確認待ち | 文書名の列に入った数字の意味を確認 |
一行・五行・三行を足すと九行です。この一致だけで内容が正しいと結論づけず、各群のIDも期待値と比べます。もし一致がA02だけになっていたら、件数は一行でも、依頼に合う抽出にはなりません。
検索して得た結果を集計へ使う場合は、確認待ちを分母から外したことも説明します。「文書全体の何割」と「文書名を確認できた範囲の何割」は別の数字です。集計で欠損を扱う考え方は、groupbyで欠損キーと合計を照合する方法にもつながります。
大文字・複数語・空の検索語を、別々の例で試す
pythonを大文字・小文字を区別して探すと、今回の一致はA09だけです。case=Falseへ変えると、A07の大文字も含みます。件数が増えた理由をデータの増加と混同せず、検索条件を変更した結果として記録します。
複数の普通の語句を「どれか一つ含む」で探すなら、各語句をre.escapeで処理した後、正規表現のORとして結合する方法があります。次の例は、v1.0とC++の記号を検索パターンの命令として扱わないための書き方です。
import re
assert split_rows("C++")["一致"] == ["A05"]
assert split_rows("python")["一致"] == ["A09"]
assert split_rows("python", case=False)["一致"] == ["A07", "A09"]
words = ["v1.0", "C++"]
pattern = "|".join(re.escape(word) for word in words)
mask = df["文書名"].astype("string").str.contains(pattern, regex=True, na=False)
assert df.loc[mask, "ID"].tolist() == ["A01", "A05"]
ここではORの結合に正規表現を使うため、regex=Trueです。Falseのままでは、縦棒も含めた一続きの文字を探すことになり、意図が変わります。検索語を普通の文字として扱う部分と、複数語を組み合わせる仕組みを分けて考えてください。
空の検索語は、何を抽出したいのかが決まりません。この教材の関数は、空文字、空白だけ、None、数値の検索語を受け付けない設計です。検索語が未入力なら全件を返す仕様も作れますが、そうする場合は利用者へ明示し、誤って全件を渡さない確認を加えます。
複数語の一覧にも、空の項目や重複がないかを先に見ます。今回は固定した二語ですが、入力フォームから受け取る場合は、空の一覧をどう扱うか、ANDかORか、同じ行を何度数えるかまで決める必要があります。短い検索式だけで業務の条件が全部決まるわけではありません。
抽出後のIDと検索条件を、受け渡しの記録にする
検索を実ファイルへ移すときは、元の一覧、一致、非一致、確認待ちを区別して保管します。元データの列を検索結果で上書きしない形なら、語句の変更や入力の修正を後から追えます。共有先へ必要な範囲だけを渡す判断も、検索処理とは別に行ってください。
記録の例は「対象列は文書名、検索語はv1.0、文字通り、大文字は区別、文書名の欠損・空文字・元の非文字列は確認待ち」です。続けて、一致のID、確認待ちのID、元の一覧の件数を書きます。引数の羅列より、読み手が結果の意味を理解しやすくなります。
抽出した一覧をCSVに保存する場合は、保存時に行番号が増えたり、IDの形が変わったりしていないかも検算します。to_csvと保存後のID確認では、読み直して項目と値を照合する方法を扱っています。検索が合っていても、保存後の表が同じとは限りません。
別の月の一覧と差を比べるなら、先に同じ検索条件を使ったかを確かめます。片方だけ大文字を無視した結果では、資料が追加されたのか、条件が変わったのかを区別しにくくなります。compareでIDと並びを揃える練習も、比較前の準備に使えます。
今回実行したのは、架空の九行に対する文字検索と分類、複数語のエスケープ、大文字の扱い、入力チェックです。実際の社内ファイルを処理した実績や、どのデータでも誤りが出ないという結果ではありません。自分のデータへ置き換えた後も、最初の数行の期待IDから照合を始めます。
独学で説明できる範囲と、質問したい範囲をまとめる
練習の終わりには、A03がなぜ非一致ではないのかを自分の言葉で説明してみてください。単に「NaNだから」と覚えるのではなく、「名前がないので検索していない。今回の受け渡しでは確認待ちとして残す」と言えれば、別の表でも必要な条件を考えやすくなります。
次の課題として、文書名の前後の空白、全角の英数字、改行を含む名前を一行ずつ追加できます。最初から全部を正規化せず、その表記を同じ名前として扱ってよいかを決め、変更前と変更後の期待IDを書いてください。文字列を整形できることと、同一の文書と判断できることは異なります。
質問するときは、版、元の型、検索語、必要なID、実際のIDを添えます。社内のファイルをそのまま相談先へ送らず、本稿のような架空の最小例へ置き換える方法があります。記号を含む二行と欠損一行だけでも、欠損の処理と正規表現の問題を話し分けられます。
Python Winnerの講座内容と受講前の確認点では、独学と質問支援を比べるための情報を整理しています。公式Pythonマスターには文字列処理・正規表現・CSVなどの学習範囲があり、2026年10月17日の公式画面では税込297,000円、3ヶ月、30分のマンツーマン18回を確認しました。
今回のpandas教材の添削が講座に含まれるか、使用するライブラリや支援の範囲は受講前に確認してください。料金と期間だけで選ばず、検索結果の誤りを説明する練習まで相談できるかを、自分が作った小さな課題と一緒に聞くと判断しやすくなります。受講後の転職や収入を約束するものではありません。
よくある質問
Q. na=Falseを付ければ、欠損値そのものが空欄に書き換わりますか?
いいえ。この指定はstr.containsの検索判定で欠損時にFalseを使うものです。元の列の欠損を書き換える処理とは分けて考えてください。
Q. regex=Falseとna=Falseはどちらか一つでよいですか?
扱う問題が違います。前者は検索語を普通の文字として扱う指定、後者は検索判定の欠損の扱いです。目的に合わせて両方を指定する場合があります。
Q. 含まない行を検索すると、空欄の行まで出るのはなぜですか?
na=Falseで欠損をFalseにした結果を反転すると、その行はTrueになります。入力済みかを別に判定し、不明な行を確認待ちへ分ける方法があります。
Q. 数字の列は、全部文字列に変えて検索してよいですか?
列の役割によります。今回の文書名では元の非文字列を確認待ちにしました。商品コードなどで数値を許す仕様なら、別のルールを決め、元の値を残して照合してください。
Q. この教材では、どの範囲を実行しましたか?
Python 3.12.14・pandas 2.2.3で、架空の九行の三分割、IDの重複なし・全件保持、記号、大文字、複数語、無効な検索語のチェックを実行しました。実ファイルやpandas 3.0.6の実機検証を行ったという意味ではありません。