日付変換でNaTが出たら、元から空欄だった行と、入力があるのに変換できなかった行を分けます。NaTを消すだけでは、月と日が逆になった誤変換までは見つかりません。 元の文字列を残し、受け入れる形式を決めてから、変換結果と件数を確認しましょう。

対象は、CSVの日付列を月別集計や並べ替えに使いたいPython初学者です。変数、DataFrameの列選択、真偽値で行を選ぶ操作を使います。機械学習や統計の知識は必要ありません。ここでは架空の貸出記録8行を使い、外部サービスへ業務データを送らずに練習します。

公式資料確認日:2026年10月5日。コードはpandas 2.2.3で検算し、現行の公式ドキュメント3.0.6も確認しています。教材の形式判定は編集部が決めた受け入れ規則です。すべての業務データに適用できる共通の規則ではありません。

NaTの原因を、元の値と受け入れ形式から切り分ける

日付を変換する前に、列の意味と形式を決める

最初に決めたいのは、「何月何日として読めるか」よりも「何の日付か」です。申込日、利用予定日、実際の返却日を混ぜると、変換が成功していても集計の意味が変わります。本稿では、架空の備品貸出記録にある「利用予定日」だけを対象にします。受付日時や返却日を代わりに入れることはしません。

次に入力規則を確認します。教材で受け入れるのは、年を4桁、月と日を2桁で書いた「2026-10-05」と「2026/10/05」の二種類です。元システムでこの二種類を使うと分かっている、という設定にします。区切り文字を置き換えても意味が変わらないのは、年・月・日の順序が先に確定しているからです。

「05/10/2026」は、日・月・年なら10月5日、月・日・年なら5月10日です。どちらも存在する日なので、パソコンが日付へ変換できたことだけでは正しい読み方を選べません。担当者や出力仕様に確認するまでは、教材では変換対象から外し、形式未確認として残します。

見た目が日付でも、文字列、日時型、Excel由来の数値では入口が異なります。列に数字しかないからといって一律に同じ単位で変換しないでください。コードを書く前に数件を表示し、元ファイルの仕様、列名、入力例を確認します。読み込み自体が初めてなら、社会人向けのCSVデータ分析入門から進められます。

この確認を省くと、エラーのないコードほど問題を見逃しやすくなります。例えば5月の記録へ入るべき一件が10月へ移っても、全体件数は変わりません。件数の検算は大切ですが、日付の意味を確かめる工程とは別です。何を確認できて、何は確認できないかを最初に分けておきます。

8行の教材で、空欄と変換できない値を見分ける

次の表を教材として使います。R04は空文字、R05は欠損値です。表ではどちらも空欄に見えやすいものの、元のデータ状態は異なります。R06は前後に半角スペースがあります。教材では前後の空白を除いてよいと決め、その処理だけを行います。

架空の利用予定日8行。引用符内の空白も元の入力の一部
記録ID元の値先に見る点
R012026-10-05年・月・日の形式
R022026/10/06区切りのみ異なる
R032026-02-30存在しない日
R04空文字未入力
R05None元から欠損
R06『 2026-10-07 』前後の空白
R0705/10/2026月日の順序が未確認
R082024-02-29うるう年の有効な日

正しく変換したい行はR01、R02、R06、R08の4件です。空欄は2件、形式未確認は1件、受け入れた形式なのに日付として無効なものが1件あります。この内訳を先に紙へ書いておくと、コードが期待どおりに分類しているか、出力を見て確認できます。

R08を含めたのは、「2月29日なら間違い」と判断しない練習のためです。年も含めて判定する必要があります。一方、R03の2月30日は区切り文字も桁数も正しいため、文字の形だけを見た判定では通ってしまいます。文字列の形式と、日付の有効性は二段階で確かめます。

元の列はdate_rawとして保存し、整えた文字列を別の変数で扱います。後からR06が変換できた理由を説明するとき、元に空白があったことが分かるようにするためです。値を上書きしてしまうと、元システムに空白があったのか、自分の処理で変わったのかを追いにくくなります。

import pandas as pd

df = pd.DataFrame({
    "record_id": [f"R{i:02d}" for i in range(1, 9)],
    "date_raw": ["2026-10-05", "2026/10/06", "2026-02-30", "", None,
                 " 2026-10-07 ", "05/10/2026", "2024-02-29"],
})
s = df["date_raw"].astype("string").str.strip()
empty = s.isna() | s.eq("").fillna(False)
accepted = s.str.fullmatch(
    r"(?:[0-9]{4}-[0-9]{2}-[0-9]{2}|[0-9]{4}/[0-9]{2}/[0-9]{2})", na=False
)
normalized = s.where(accepted).str.replace("/", "-", regex=False)
df["date_parsed"] = pd.to_datetime(normalized, format="%Y-%m-%d", errors="coerce")
print(pd.__version__)

正規表現は、教材で許可した二種類の形に文字列全体が一致するかを調べます。pandas公式のstr.fullmatchが、その全体一致のための機能です。この段階では月が13でも形が合えば通るため、後のto_datetimeと組み合わせます。

errors=“coerce”で止まらなくなっても、失敗した行は残る

to_datetimeの公式リファレンスでは、errors=“raise”は無効な解析に対して例外を出し、errors=“coerce”は解析できない値をNaTへ変換すると説明されています。NaTは日時の欠損を表す値です。coerceを付けたこと自体で、入力間違いが修正されるわけではありません。

例えばR03は、2月30日を別の日に直してもらえるわけではなく、date_parsedがNaTになります。コードが最後まで動いたので成功と考えて月別集計へ進むと、どの行に確認が必要だったか分からないままになります。実行の成功と、データの確認完了を分けて扱いましょう。

教材のdate_parsedには、空欄のR04・R05、形式未確認のR07、無効な日付のR03の計4件でNaTが入ります。NaTの数だけを見ても、この4件を同じ理由とは判断できません。変換前に取ったemptyとacceptedを使うことで、理由ごとの一覧を作れます。

df["status"] = "変換済み"
df.loc[empty, "status"] = "元から空欄"
df.loc[~empty & ~accepted, "status"] = "形式未確認"
df.loc[~empty & accepted & df["date_parsed"].isna(), "status"] = "無効な日付"

print(df[["record_id", "date_raw", "date_parsed", "status"]].to_string(index=False))
counts = df["status"].value_counts().to_dict()
assert counts == {"変換済み": 4, "元から空欄": 2, "形式未確認": 1, "無効な日付": 1}
assert df["date_parsed"].isna().sum() == 4
assert len(df) == 8 and df["record_id"].is_unique

このassertは教材の期待結果を確かめるためのものです。自分のCSVへ移すときは、4件や8件という数字をそのまま使いません。どの形式を受け入れるか、どの状態が何件あるはずかを元資料で決めてから置き換えます。実務で数が不明なら、まず一覧を作って確認する作業から始めます。

ここでdropnaを使ってNaTの行を消してしまうと、8件あった記録が4件になります。日時が確定した行だけを使う分析なら、その選別が必要なこともあります。ただし、「入力8件のうち確定した4件を分析、残り4件は保留」と説明し、保留行を別に残してください。削除によって問題を見えなくすることを避けます。

月日が逆でも変換できるため、NaTゼロだけでは検算にならない

R07の「05/10/2026」を、月・日・年の形式で変換すると5月10日、日・月・年の形式で変換すると10月5日になります。どちらもNaTではありません。この一件だけでも、「エラーがない」「NaTがない」という判定が、日付の意味の正しさを確かめていないと分かります。

text = "05/10/2026"
as_month_first = pd.to_datetime(text, format="%m/%d/%Y", errors="raise")
as_day_first = pd.to_datetime(text, format="%d/%m/%Y", errors="raise")
assert as_month_first == pd.Timestamp("2026-05-10")
assert as_day_first == pd.Timestamp("2026-10-05")
assert as_month_first != as_day_first

このコードは正解を選ぶためではなく、二つの読み方が成立することを示す教材です。現実のR07に相当する行では、元の入力画面や出力元の仕様へ戻ります。担当者から「このファイルは日・月・年」と確認できた場合に、その根拠と対象の範囲を記録して別の変換規則を適用します。

公式資料ではdayfirstやyearfirstは厳密な形式制約ではないと注意されています。また、format=“mixed”による個別推定にも注意が必要です。受け入れる表記が分かっている教材では、曖昧な推定に任せずformatを指定しています。使えるオプションを増やすより、入力仕様を明確にすることが先です。

ただしformatを指定しても、入力時点で別の日を正しい形式で記録した誤りは見つかりません。10月5日が正しいところに10月6日と書かれていれば、日付としては有効です。重要な日付は元の申込票など、独立した資料との照合が必要になります。

元の文字列 8行を保存変換済み4件元から空欄2件形式未確認1件無効な日付1件4 + 2 + 1 + 1 = 8 を照合変換済みでも、月日の意味は別に確認
図は横にスクロールできます。編集部作成。NaTを一種類のエラーとして扱わず、元の状態から理由を分けます。

空欄・無効日・形式未確認で、次にする作業を変える

4種類の状態を付けた後は、状態に合わせて確認先を変えます。空欄なら、入力待ちなのか、予定日未定を表す正当な空欄なのかを調べます。無効な日付なら元記録の転記を確認します。形式未確認なら、月日の順序など入力の約束を確かめます。同じ「日付がありません」という問い合わせより論点が明確になります。

変換結果に応じた対応。教材では勝手な補完をしない
状態教材の件数次の確認
変換済み4件元資料の日付・対象期間と照合
元から空欄2件未定か、入力漏れかを確認
形式未確認1件月日の順序を出力元へ確認
無効な日付1件元記録へ戻り、正しい日を確認

「存在しない日なら月末に丸める」「空欄なら今日の日付を入れる」といった処理は、教材では行いません。それらは日付の解析ではなく、新しい業務ルールによる値の補完です。利用予定日を今日に置き換えると、利用者が指定していない予定を作ってしまいます。

前の行と同じ日付を入れる処理も、見た目が整うからという理由では選びません。表の見出しだけに日付があり、下の行へ同じ日付を引き継ぐ仕様だと確認できる場合とは事情が異なります。補完が必要な場合は、元の列、補完した列、適用した条件を残して区別します。

日付に限らず、空欄の意味を先に決める練習はCSVの欠損・重複の確認でも扱っています。ここではその考え方を、日時へ変換した後の状態管理につなげています。NaTの件数を減らすことより、各行の説明ができることを目標にしてください。

変換結果を検算し、月別集計へ渡せる記録を作る

行数と日付の期待値を別々に確かめる

変換前後の行数が8件で一致していることを確認したら、代表的な日付も照合します。本稿では、区切り文字を変えたR02、前後の空白を除いたR06、うるう日のR08を選びます。いずれも変換済みの件数だけでは、期待した日になったか分からない行です。

by_id = df.set_index("record_id")
expected = {
    "R01": "2026-10-05", "R02": "2026-10-06",
    "R06": "2026-10-07", "R08": "2024-02-29",
}
for record_id, day in expected.items():
    assert by_id.loc[record_id, "date_parsed"] == pd.Timestamp(day)
assert pd.isna(by_id.loc["R03", "date_parsed"])
assert by_id.loc["R07", "status"] == "形式未確認"
assert sum(counts.values()) == len(df)

この確認に加え、元のdate_rawが保存されていることも見ます。R06には前後の空白が残り、R07は元の文字列のままです。変換結果だけを保存する運用では、後から規則を修正するときに元の情報へ戻れません。再実行できる材料をそろえることも、小さな検算の一部です。

実務で全件を目視できない場合でも、先にどの種類の行を確認するか決めます。初日と末日、月をまたぐ箇所、区切り形式が違う箇所など、誤りが結果へ影響しそうな境界を選びます。ただし、抽出した数件の一致から全件正しいと断定しないでください。確認した範囲を記録します。

元の記録IDが一意であるかも大切です。同じIDが二件ある表では、確認結果をどちらへ返すべきか曖昧になります。重複が仕様なのかを確認し、一件を特定できる組み合わせを決めます。日付を変換する処理と、記録の重複を解決する処理を混ぜないようにします。

月別集計では「変換できた」と「対象期間内」を分ける

R08の2024年2月29日は有効な日付です。しかし「2026年10月の利用予定」を集計するなら対象外です。日付への変換に成功したことと、今回の集計へ含めることは別の条件になります。ここを分けると、変換済み4件のうち今回の期間に入る3件を説明できます。

valid = df["status"].eq("変換済み")
in_period = (
    df["date_parsed"].ge(pd.Timestamp("2026-10-01"))
    & df["date_parsed"].lt(pd.Timestamp("2026-11-01"))
)
october = df.loc[valid & in_period].copy()
assert october["record_id"].tolist() == ["R01", "R02", "R06"]
assert len(october) == 3
assert (valid & ~in_period).sum() == 1
assert (~valid).sum() == 4
assert len(october) + (valid & ~in_period).sum() + (~valid).sum() == len(df)

教材の報告は「全8件、2026年10月と確認できた3件、変換済みで対象期間外1件、日付未確定4件」と書けます。未確定の4件がすべて10月ではないと分かったわけではありません。R07が日・月・年の形式だと判明した場合は10月分へ増える可能性があります。

このため、3件を「10月の全利用件数」と呼ぶのは早い段階です。「確認済みの10月分」と表現すれば、未確定の存在が伝わります。日付の不明な行を除外した後の数字が、どの範囲を表すかを表の注記にも残してください。

金額や数量も集計する場合は、日付が不明な行に値がいくらあるかを別に見ます。日付だけ欠けているのに、金額もゼロと扱う必要はありません。groupbyで合計が合わないときの検算では、集計キーの欠損と値の欠損を分ける方法を扱っています。

一行を変えて試し、確認コードが何を見つけるか確かめる

教材のコピーでR03を「2026-02-28」へ直したと仮定し、最初から実行し直してみます。変換済みは5件、無効な日付は0件になります。ただし10月分の3件は変わりません。入力の修正と、対象期間の件数の変化が同じではないことを確認できます。

次にR07を、出力元へ確認せず「2026-10-05」へ書き換えたと仮定します。コード上は変換済みが増え、10月分も4件になります。しかし、根拠のない修正という問題はコードからは分かりません。入力値を変える際に確認先や理由を残す工程が必要だと分かります。

R08を「2025-02-29」へ変えれば、同じ月日でも無効な日付へ分類されます。形式の検査だけでは通り、日時変換でNaTになります。二段階のチェックがそれぞれ違う誤りを見つけることを確認する練習です。元の教材を保存してから、コピーで一つずつ変更してください。

行を二重に追加する実験もできます。この場合、変換規則はすべて正しくても、元の8件という前提とIDの一意性が崩れます。日付変換のテストに合格しただけでは、記録の重複は解消しません。merge後の行数の確認も、結合したデータを使うときの別の点検になります。

時刻やタイムゾーンを含む列へ進む場合は、今回とは別の教材を用意しましょう。UTCへそろえることと、日本の現地時刻として読むことは同じではありません。日付だけの教材へ安易にutc=Trueを付け足し、意味の異なる入力を一緒に処理しないことが大切です。

確認メモと学習課題を残し、同じ処理をやり直せるようにする

記録には、元ファイル、対象列の意味、受け入れ形式、空白の扱い、未確定件数、実行環境を書きます。「日付をきれいにした」という一言だけでは、翌月の担当者が同じ処理を再現できません。元データを作った人へ問い合わせる際も、どの行の何が不明かを特定できる記録が役立ちます。

教材の確認メモ。実データでは保存先や担当者の管理規則に合わせる
項目記入例
対象架空の貸出記録R01〜R08、利用予定日
受け入れ形式YYYY-MM-DDとYYYY/MM/DD。前後の空白を除去
確認結果変換済み4、空欄2、形式未確認1、無効1
10月分確認済み3件。未確定4件は集計保留
次の作業R03の原票、R07の月日の順序を確認

問い合わせ文なら、「R07の05/10/2026は、5月10日と10月5日のどちらとして出力されたものですか。ファイル全体で同じ形式ですか」と書けます。一行の正解だけでなく、同じ形式の他の行へ適用できる規則かどうかも確かめます。返答がない間は、未確定として管理します。

翌月に同じ処理を使う場合は、受け入れる形式が変わっていないかを最初に見ます。前月は二種類だったのに、今月だけ時刻付きの値が増えたなら、形式未確認の件数が増えるはずです。そこで規則を広げる前に、出力設定が変わったのか、別の種類の記録が混ざったのかを確認します。毎月の保留件数は、入力の変化に気づく手がかりになります。

修正後に再実行する際は、修正前の確認表も残します。R03が無効な日付から変換済みへ変わったなら、いつ、何を根拠に直したかを追えます。元の表を手作業で上書きし、修正後の結果しか残さないと、同じ不備が翌月も出たときに、以前の対応を再利用できません。教材でも「元データ」「修正案」「再実行結果」を別にして試せます。

保存したCSVを別の人が読み込む場面にも注意します。CSVは、Pythonの日時型そのものを保持する保存形式ではありません。日付を文字として書き出した後、相手の環境でどう読み込むかを決める必要があります。提出物には表示形式の説明を添え、コードと確認記録も必要に応じて一緒に保存します。表計算ソフトで開き直した見た目だけで、元の形式が保たれたと判断しないようにします。

未確定行だけの一覧を作るなら、記録ID、元の値、状態、問い合わせ事項を列にします。利用者名など、日付の原因調査に不要な情報まで共有する必要はありません。確認が済んだら、返答を根拠にした変換規則と、特定の一行だけの訂正を区別します。この区別があれば、別のデータにも適用してよい修正なのか判断できます。

表と確認メモを二人で見比べる学習場面
入力規則を別の人へ説明して確かめる場面のイメージ(AI生成)。実在の受講生ではありません。

ここまで自分で説明できるなら、別の8行を作って独学を続けられます。区切り形式を増やす前に、対応する理由と期待結果を用意してください。文法やDataFrameの操作から質問したい場合は、Python Winnerの学習範囲を確認する記事も判断材料になります。

2026年10月5日にPython Winnerの公式案内で、Pythonの基礎やデータ分析に関するコースの掲載を確認しました。基礎から個別に質問したい人は検討しやすい一方、日時データの専門的な設計だけを求める経験者は別の学習先も比較してください。この記事のコードを添削してもらえるかは、コースと質問対応の範囲を申込前に確認する必要があります。

相談時は、受講総額、必要な学習時間、使用するpandasの版、持ち込み課題への対応を確かめましょう。空欄の業務上の意味を決める作業は、講座への申込みだけでは解決しません。元システムの担当者へ確認する課題と、Pythonを学ぶ課題を分けて持っていくと相談しやすくなります。

PR:Pythonの学習範囲を相談したい方は 【PythonWinner】 の公式案内をご確認ください。

よくある質問と、次に日付列を扱うときの順番

よくある質問

Q. NaTを空文字に置き換えれば解決しますか?
A.

表示は変わりますが、日付が分からない理由は解決しません。元から空欄なのか、形式が未確認なのか、存在しない日付なのかを分け、元の文字列を残してください。

Q. errors='coerce'は使わないほうがよいですか?
A.

原因を一覧にする用途では使えます。ただし変換後のNaTを確認する工程とセットにします。全行が有効な日付である前提を厳しく確認したい処理では、raiseを使って停止させる設計も考えられます。

Q. dayfirst=Trueなら月日の取り違えを防げますか?
A.

公式では厳密な形式制約ではないと説明されています。入力仕様を確認し、分かっている形式を指定してください。元の入力自体が誤っている場合は、formatを指定しても元資料との照合が必要です。

Q. Excelの日付番号にも同じコードを使えますか?
A.

この記事のコードは年・月・日の文字列が対象です。数値の日付は元システムの起点と単位を確認し、別の変換規則を用意します。数字を見ただけで日数や秒数を推測しないでください。

Q. NaTが0件なら月別集計へ進んでよいですか?
A.

月日の順序、対象期間、記録IDの重複、元資料との一致も確認します。NaTが0件なのは日時として変換できたという一つの結果で、入力の意味まで正しいと確定したわけではありません。

次の日付変換では、列の意味と入力形式を確認し、元の値を保存するところから始めます。空欄と形式未確認を先に分け、変換後の無効日を調べ、代表的な日付と全体の件数を照合してください。最後に対象期間で絞り、保留した行の数と理由を添えます。

この順番で進めると、エラーを消すための修正と、意味を確かめた修正を区別できます。小さな8行でも、どの日付を集計へ渡してよいか説明できることが、次のデータ分析へ進む土台になります。