ホーム / お役立ち記事 / pandas の read_excel で #N/A が空になる理由|エラー値の数え方

Knowledge

pandas の​ read_excel で​ #N/A が​空に​なる​理由|​エラー値の​数え方

pandas の read_excel で Excel を読むと、#N/A などのエラー値のセルは空(NaN)になり、keep_default_na=False を付けても戻りません。read_excel が読み込みの途中で、エラー値のセルを空の値に置き換えるためです。弊社の事例では、品名が #N/A の行を「0行」と数え、実際は約2万4千行ありました。原因の切り分け、見分け方、openpyxl で数えて XML と照らす直し方、落とし穴、確かめ方を、弊社の実測と公開の文書でまとめます。

著者: 村山 悠太(ゴムマリ) 最終更新: 2026年11月

pandas の read_excel で Excel を読むと、#N/A や #REF! などのエラー値のセルは、空(NaN)として読まれます。keep_default_na=False を付けても、エラー値のセルは空のままです。

原因は、read_excel が .xlsx を openpyxl で読むときの pandas の処理の中で、エラー値のセルが空の値に置き換わることです。弊社は pandas 3.0.5 のソースと実測で確かめました。

弊社の事例では、品名が #N/A の行を pandas で数えて「0行」と出しました。読み方を替えて数え直すと、実際は約2万4千行ありました。

直し方は、エラー値を数える列を read_excel で読まず、openpyxl で開いてセルの型がエラー値のセルを数えることです。数が合っているかは、シートの XML から数えた数と照らします。

この記事は、Excel の台帳やマスタを Python で集計・照合する仕組みを任されている業務の担当者・情報システムの担当者に向けて書きます。

原因の切り分け、見分け方、直し方、落とし穴、確かめ方を、弊社の実測と、pandas の文書とソース、ECMA-376(Office のファイルの形式の国際規格)でまとめます。

原因: エラー値の​セルは、​文字の​扱いより​前に​空に​置き換わる

.xlsx のシートの XML では、エラー値のセルは型の印 t="e" を持ち、値の要素(v)にエラー値の文字が入ります。ECMA-376 の Part 1 は、型 e を「エラーを含むセル」と定めています(18.18.11 節)。

<c r="B6" t="e"><f>VLOOKUP(A6,マスタ!A:B,2,FALSE)</f><v>#N/A</v></c>

数式のセルは、数式と、最後に計算した結果のエラー値の両方を持ちます。弊社が XML を手で書いたファイルでは、値だけのエラー値のセルは、数式の無い t="e" のセルでした。

pandas の文書は、read_excel の engine を指定しないとき、.xlsx などは openpyxl で読むと書いています。pandas 3.0.5 のソースは、そのとき openpyxl を data_only=True で開きます。

同じソースは、セルの型がエラー値なら np.nan を返します(pandas/io/excel/_openpyxl.py の _convert_cell)。np.nan は、pandas が空として扱う値です。

この置き換えは、セルの値を取り出す段で起きます。na_values や keep_default_na が文字を空にするのは、その後の段です。そのため、この2つの引数を変えても、エラー値のセルは戻りません。

pandas の文書は、keep_default_na が False で na_values を指定しないとき「no strings will be parsed as NaN」と書いています。

エラー値のセルについての記述は、read_excel の頁にありません。この2点は、2026年11月1日に見た 3.0 系の文書(3.0.6)と最新の文書(3.1.0)で同じでした。

エラー値を np.nan にするソースの部分も、pandas 3.0.5 と 3.1.0 で同じ文字でした。

文字の​ “#N/A” は、​別の​理由で​空に​なる

文字として入った “#N/A” は、型 s の普通の文字のセルです。こちらは、pandas が既定で空と読む文字の一覧に “#N/A” が入っているために、空になります。

pandas の文書の na_values の説明は、既定で空と読む文字として、“#N/A”・“N/A”・“NA”・“NULL” など19個を挙げています(空の文字を含む)。

セル keep_default_na=False のとき
エラー値の #N/A(型 e) 空のまま
文字の “#N/A”(型 s) 文字のまま残る

見た目は同じ #N/A でも、空になる理由が違います。そのため、効く直し方も違います。

弊社の​実測: 100行の​ファイルで、​#N/A の​15セルを​0と​数えた

弊社は 2026年11月1日に、pandas 3.0.5・openpyxl 3.1.5(Python 3.14.6)と合成のファイルで試しました。品名の列には、次のセルを入れました。

品名の列 セルの数
VLOOKUP の結果の #N/A 12
値で貼り付けた #N/A 3
数式の結果の #REF! 2
文字の “#N/A” 2
空のセル 4
普通の品名 77

ファイルは、ECMA-376 の形(t="e" と値の要素)で XML を書いた物と、それを LibreOffice 26.2.4.2 で保存し直した物の2つです。どちらも同じ結果でした。Excel では開いていません。

読み方 品名の列の数え方
read_excel の既定 空 23・“#N/A” と一致 0
keep_default_na=False 空 17・“#N/A” と一致 2・空の文字 4
na_filter=False keep_default_na=False と同じ
dtype=str・dtype=object 既定と同じ
openpyxl(data_only=True) 型 e の #N/A 15・#REF! 2、型 s の “#N/A” 2
XML を直に読む t="e" の #N/A 15・#REF! 2

既定の読み方の空23は、エラー値17・文字の “#N/A” 2・空のセル4の和です。空になった後では、どれが何かを見分けられません。

keep_default_na=False を付けると、“#N/A” と一致する数は0から2になります。直ったように見えますが、2は文字の “#N/A” で、エラー値の15セルは空のままです。

pandas 3.0.5 では、どの読み方でも警告を出しませんでした。3.1.0 では、engine を指定しない read_excel に、既定の engine が将来 calamine へ変わる予告の警告が出ます。

既定の読み方は、国の列の “NA”(ナミビアの国の符号として入れた3セル)と、備考の列の “N/A” 5セル・“NULL” 1セルも空にしました。

弊社の​事例: 0行と​数えた​品名の​ #N/A が、​約2万4千行あった

2026年10月、お客様から受け取った単価のマスタ(Excel)を検品したとき、品名が #N/A の行を pandas で数え、「0行」と出しました。

同じころ、照合に使う正解の一覧も pandas で読んで作っていました。一覧の全行を照らすと、品名の #N/A が、正解の側だけ空になっていました。

正解の一覧は XML を直に読む方式に、検品の数は openpyxl で数える方式に替えました。数え直すと、品名が #N/A の行は約2万4千行ありました。

0という数はもっともらしく見え、数え方の誤りは、全行の照合まで表に出ませんでした。

見分け方​: ​空が、​エラー値か、​文字か、​空の​セルか

read_excel の既定と keep_default_na=False の2通りで読み、同じセルがどう読まれるかを比べます。

既定と keep_default_na=False(表では False)での見え方 疑う所
既定で空、False でも空 エラー値のセル(型 e)
既定で空、False では “#N/A” などの文字 既定で空と読む文字の一覧
既定で空、False では空の文字 空のセルか、計算結果の無い数式

弊社が試した読み方(engine が openpyxl=pandas 3.0.5 の既定)では、keep_default_na=False で空(NaN)が残ったのは、エラー値のセルだけでした。

空のセルと計算結果の無い数式は、空の文字(“”)になりました。

ただし、エラー値の種類(#N/A か #REF! か)は、pandas では分かりません。種類ごとに数えるには、次の直し方に進みます。

直し方​: openpyxl で​エラー値の​セルを​数え、​XML の​数と​照らす

この記事のために書いたコードの手順は次のとおりです。

  1. エラー値を数える・そのまま出す列は、read_excel で読まない
  2. openpyxl を data_only=True で開き、セルの型(data_type)が e のセルを、列とエラー値の種類ごとに数える
  3. 同じ数を、シートの XML の t="e" のセルから数え、2つが一致することを確かめる
  4. 表として使うときは、エラー値を “#N/A” などの文字のまま残した表を作る

openpyxl の文書は、data_only は、数式のセルが数式を持つか、Excel が最後にシートを読んだときに保存された値を持つかを決める、と書いています。

data_only を付けないと、数式のセルは数式の文字で返ります。弊社の実測(XML を書いた方のファイル)では、型 e と読めたのは値だけの3セルで、数式の14セルは数式の文字でした。

import collections
import re
import sys
import zipfile
import xml.etree.ElementTree as ET

import pandas as pd
from openpyxl import load_workbook
from openpyxl.utils import get_column_letter

NS = {"m": "http://schemas.openxmlformats.org/spreadsheetml/2006/main",
      "r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
      "p": "http://schemas.openxmlformats.org/package/2006/relationships"}


def read_keep_errors(path, sheet, header_row=1):
    """エラー値のセルを "#N/A" などの文字のまま残して読む(空のセルは None)。"""
    wb = load_workbook(path, read_only=True, data_only=True)
    rows = [[c.value for c in row] for row in wb[sheet].iter_rows(min_row=header_row)]
    wb.close()
    width = max(len(r) for r in rows)
    rows = [r + [None] * (width - len(r)) for r in rows]
    return pd.DataFrame(rows[1:], columns=rows[0], dtype=object)


def count_errors_openpyxl(path, sheet):
    """列の文字とエラー値の種類ごとに、エラー値のセル(型 e)を数える。"""
    wb = load_workbook(path, read_only=True, data_only=True)
    n = collections.Counter()
    for row in wb[sheet].iter_rows():
        for c in row:
            if getattr(c, "data_type", None) == "e":
                n[(get_column_letter(c.column), c.value)] += 1
    wb.close()
    return n


def count_errors_xml(path, sheet):
    """同じ数を、シートの XML の t="e" のセルから数える(openpyxl を使わない)。"""
    z = zipfile.ZipFile(path)
    wb = ET.fromstring(z.read("xl/workbook.xml"))
    rid = next(s.get(f"{{{NS['r']}}}id") for s in wb.iter(f"{{{NS['m']}}}sheet") if s.get("name") == sheet)
    rels = ET.fromstring(z.read("xl/_rels/workbook.xml.rels"))
    target = next(r.get("Target") for r in rels.iter(f"{{{NS['p']}}}Relationship") if r.get("Id") == rid)
    part = target.lstrip("/") if target.startswith("/") else "xl/" + target
    n = collections.Counter()
    with z.open(part) as f:
        for _, el in ET.iterparse(f):
            if el.tag == f"{{{NS['m']}}}c":
                if el.get("t") == "e":
                    v = el.find("m:v", NS)
                    n[(re.match(r"[A-Z]+", el.get("r")).group(), v.text if v is not None else None)] += 1
                el.clear()
    return n


if __name__ == "__main__":
    path, sheet = sys.argv[1], sys.argv[2]
    a, b = count_errors_openpyxl(path, sheet), count_errors_xml(path, sheet)
    df = pd.read_excel(path, sheet_name=sheet)
    print("列 エラー値 openpyxl XML")
    for key in sorted(set(a) | set(b)):
        print(*key, a[key], b[key])
    print("pandas の既定で空と読んだセル(列ごと):", df.isna().sum().to_dict())
    sys.exit(0 if a == b else 1)

使い方は python excel_errors.py マスタ.xlsx シート名 です。列とエラー値の種類ごとに openpyxl と XML の数を並べ、食い違えば終了コード1を返します。

事例で替えた先は、正解の一覧が XML を直に読む方式、検品の数が openpyxl で数える方式でした。このコードはこの記事のために書いたもので、2つを並べて照らす形にまとめています。

弊社は 2026年11月1日に、上の実測の2つのファイルと、openpyxl で書いたファイルで、このコードを確かめました。

確かめたのは、2つの数え方が #N/A 15・#REF! 2 で一致すること、read_keep_errors がエラー値を “#N/A” などの文字で残し、空のセルを None にすることです。

data_only=False で開くように壊した版では、openpyxl の数が #N/A 3・#REF! 0 になり、終了コード1を返しました。照らし合わせが、読み方の誤りを拾います。

read_keep_errors の表では、エラー値の “#N/A” と文字の “#N/A” は同じ文字になります。2つを分けて数えるときは、型 e だけを数える count_errors_openpyxl を使います。

落とし穴: 事例で​踏んだ​ものと、​試して​分かった​もの

事例で踏んだのは1つ目と2つ目です。3つ目からは、この記事のための 2026年11月1日の実測で確かめたものです。

  • 0行を確かめずに出したread_excel で読んだ表で “#N/A” と一致する行を数えると、エラー値の行は0と出ます。0は、別の読み方で数えた数と照らしてから出します。
  • keep_default_na=False で直ったように見える“#N/A” と一致する数が0から増えても、増えたのが文字の “#N/A” だけのことがあります。実測では0から2になり、エラー値の15セルは空のままでした。
  • 計算結果の無い数式は、openpyxl でも None になるopenpyxl で数式を書いたファイルは、計算結果を持ちません。=NA() を2セル入れたファイルでは、openpyxl の data_only=True も None と読みました。
  • dtype=str を付けても戻らない実測では、dtype=str と dtype=object は、既定の読み方と同じ空23でした。
  • “NA” や “NULL” の文字も空になる既定で空と読む文字の一覧は、国の符号の “NA” も空にします。実測では、国の列の3セルが空になりました。

3つ目の、計算結果を持たないファイルができる理由は、Python で値を書いた Excel の計算結果が古いままの理由にまとめています。

確かめ方​: 2つの​読み方の​数が、​列と​種類ごとに​一致する

短い時間で確かめるなら、ファイルを上のコードに通します。openpyxl と XML の数が、列とエラー値の種類ごとに一致すれば、終了コードは0です。

0件と報告する前には、XML の t="e" のセルが本当に0かを、このコードの出力で見ます。

ただし、2つの数え方はどちらも、ファイルに残った計算結果を読みます。計算結果の無い数式のセルは、どちらも数えません。

弊社の事例で誤りが見つかったのは、一覧の全行を照らしたときでした。数の照合に加えて行ごとに照らすと、どの行が抜けたかまで分かります。

自力で​どこまで​やれるか

読むファイルが少なく、エラー値を数える列が決まっているなら、自力で足ります。上のコードは、pandas と openpyxl と Python の標準のライブラリだけで動きます。

自力の壁は次の3つです。

  • ファイルと列が多く、どの列のエラー値を空と読んでよいかを、列ごとに決める必要がある
  • エラー値の行ごとに、照合先にコードが無いのか、数式の参照が壊れているのかを切り分ける必要がある
  • 毎月の集計や照合で、数え方の誤りを全行の照合で止める工程を、続けて回す必要がある

外注する​場合の​判断基準

外注の分かれ目は、Excel から数えた数を、別の読み方と全行で照らす工程を、自社で持ち続けられるかです。外注するなら、見積もりを比べる前に次の3点を確かめてください。

  • エラー値のセルと空のセルを、分けて数えるか
  • 読み方の違う2つの方法で数え、数が合うかを確かめるか
  • 0件という結果を、確かめずに報告する作りになっていないか

ゴムマリでは、2つ以上の台帳・帳票を全件突き合わせ、差分と要確認の箇所だけを返す仕事を、データ突合・照合チェックとして承ります。

お渡しするのは、全件の突合結果(一致・不一致・要確認の別)と、どの項目がどう食い違うかを確認すべき理由つきで示した差分の一覧です。

料金は20万円から、期間は1週間です。進め方は相談からご連絡ください。

次の​一歩

手元の Excel を1本選び、エラー値がありそうな列を、read_excel の既定と keep_default_na=False の2通りで読んでください。

keep_default_na=False でも空が残る列には、弊社が試した読み方(engine が openpyxl)では、エラー値のセルがあります。engine を calamine にすると、エラー値は空の文字になり、この見分け方は使えません。

そのうえで、上のコードで種類ごとに数え、XML の数と合うかを見ます。

帳票を自動で作るときに、数字が正しいことを仕組みで確かめる方法は、既存Excel様式を自動生成するときの品質保証にまとめています。

関連記事: Python で値を書いた Excel の計算結果が古いままの理由/既存Excel様式を自動生成するときの品質保証

この記事を書いた人

代表 村山悠太のポートレート

村山 悠太(ゴムマリ代表)

ワークスアプリケーションズで大手企業向けERPの営業と導入を担当し、外資系SaaS企業と国内大手放送局で販路と新規事業の立ち上げ、AI学習用データを扱うAIスタートアップ2社でマーケティングと営業を担った。2024年10月から生成AIの実装に入り、いまは大手企業の現場で、AIエージェントを要件定義から運用まで手がけている。

代表の経歴を見る

FAQ

よく​ある​質問

keep_default_na=False を付ければ、#N/A は残りますか?

文字として入った "#N/A" は残りますが、エラー値のセルは残りません。弊社が pandas 3.0.5 で試すと、エラー値のセルは keep_default_na=False・na_filter=False・dtype=str のどれを付けても空でした。エラー値は、openpyxl を data_only=True で開いて読みます。

na_values から "#N/A" を外せば、エラー値のセルは残りますか?

残りません。弊社が見た pandas 3.0.5 のソースでは、エラー値のセルは、読み込みの途中で空の値に置き換わります。na_values と keep_default_na の文書は、文字を空として読むかを決める引数として書かれています。外して効くのは、文字の "#N/A" です。

エラー値の行を数えるだけなら、pandas の isna で足りますか?

足りません。既定の読み方の isna は、エラー値・空のセル・"NA" などの文字をまとめて数えます。弊社の実測では、品名の列の空23は、エラー値17・文字の "#N/A" 2・空のセル4の和でした。エラー値の種類(#N/A か #REF! か)も分かりません。

openpyxl で読んでも、エラー値のはずのセルが None になります。

ファイルが数式の計算結果を持っていない恐れがあります。openpyxl で数式を書いたファイルは、計算結果を持ちません。弊社が試したファイルでは、計算結果の無い =NA() のセルを、openpyxl の data_only=True は None、pandas は空と読みました。

Related

関連

ご相談は​こちらから

相談は無料です。

相談する