PythonのNaN(非数)判定完全ガイド:math.isnan()と検出方法を徹底解説
目次
NaN(非数)とは
NaN(Not a Number)は、数学的に定義されない演算の結果を表す特殊な浮動小数点値です。IEEE 754標準で定義され、「非数」や「数ではない」という意味を持ちます。
import math
# NaNの作成方法
nan1 = float('nan')
nan2 = math.nan
nan3 = 0.0 / 0.0 # ZeroDivisionErrorが発生する場合あり
print(f"float('nan'): {nan1}")
print(f"math.nan: {nan2}")
print(f"タイプ: {type(nan1)}")
NaNが生成される演算
基本的なNaN生成パターン
import math
# NaNが生成される典型的な演算
nan_operations = [
float('nan'), # 直接作成
math.sqrt(-1), # 負数の平方根
math.log(-1), # 負数の対数
0.0 / 0.0, # ゼロ除算(例外の場合もある)
math.inf - math.inf, # 無限大同士の減算
math.inf / math.inf, # 無限大同士の除算
math.inf * 0, # 無限大とゼロの乗算
]
print("NaNが生成される演算:")
for i, operation in enumerate(nan_operations[:-4]): # エラーになるものは除く
print(f"パターン{i+1}: {operation}")
# エラーハンドリングが必要な演算
try:
sqrt_negative = math.sqrt(-1)
except ValueError as e:
print(f"math.sqrt(-1): {e}")
sqrt_negative = float('nan')
try:
log_negative = math.log(-1)
except ValueError as e:
print(f"math.log(-1): {e}")
log_negative = float('nan')
print(f"エラー回避後のNaN: {sqrt_negative}, {log_negative}")
複素数演算での対処
import cmath
import math
# 実数では不可能な演算を複素数で
negative_sqrt_real = math.sqrt(-1) if -1 >= 0 else float('nan')
negative_sqrt_complex = cmath.sqrt(-1)
print(f"実数での負数平方根: {negative_sqrt_real}")
print(f"複素数での負数平方根: {negative_sqrt_complex}")
# NaN判定
print(f"実数結果はNaN: {math.isnan(negative_sqrt_real)}")
print(f"複素数結果の実部はNaN: {math.isnan(negative_sqrt_complex.real)}")
NaNの判定方法
math.isnan()による基本判定
import math
# 様々な値でのNaN判定
test_values = [
1.0,
0.0,
math.inf,
-math.inf,
float('nan'),
math.nan,
float('inf') - float('inf')
]
print("NaN判定結果:")
for val in test_values:
print(f"{val}: {math.isnan(val)}")
NaNの特殊な比較特性
import math
nan = float('nan')
# NaNの特殊な性質
print(f"nan == nan: {nan == nan}") # False
print(f"nan != nan: {nan != nan}") # True
print(f"nan < 1: {nan < 1}") # False
print(f"nan > 1: {nan > 1}") # False
print(f"nan == 1: {nan == 1}") # False
# この特性を利用したNaN判定
def is_nan_alternative(x):
"""自己比較によるNaN判定"""
return x != x
test_val = float('nan')
print(f"自己比較判定: {is_nan_alternative(test_val)}")
print(f"math.isnan()判定: {math.isnan(test_val)}")
包括的なNaN検出関数
単一値のNaN検出
import math
def comprehensive_nan_check(value):
"""包括的なNaN検出"""
checks = {
'math.isnan()': math.isnan(value),
'自己不等価': value != value,
'str判定': str(value).lower() == 'nan',
'有限性判定': not (math.isfinite(value) or math.isinf(value))
}
return checks
# テスト
test_cases = [42, float('nan'), math.inf, "not a number"]
for case in test_cases:
if isinstance(case, (int, float)):
results = comprehensive_nan_check(case)
print(f"{case}: {results}")
リストやデータ構造でのNaN検出
import math
def find_nan_positions(data):
"""リスト内のNaNの位置を特定"""
nan_positions = []
for i, value in enumerate(data):
if isinstance(value, (int, float)) and math.isnan(value):
nan_positions.append(i)
return nan_positions
def count_nan(data):
"""データ内のNaN数をカウント"""
count = 0
for value in data:
if isinstance(value, (int, float)) and math.isnan(value):
count += 1
return count
# テストデータ
mixed_data = [1, 2.5, float('nan'), 4, math.inf, float('nan'), 7]
print(f"データ: {mixed_data}")
print(f"NaN位置: {find_nan_positions(mixed_data)}")
print(f"NaN数: {count_nan(mixed_data)}")
データクリーニングでのNaN処理
NaNの除去
import math
def remove_nan(data):
"""リストからNaNを除去"""
return [x for x in data if not (isinstance(x, float) and math.isnan(x))]
def replace_nan(data, replacement=0):
"""NaNを指定値で置換"""
result = []
for x in data:
if isinstance(x, float) and math.isnan(x):
result.append(replacement)
else:
result.append(x)
return result
# テスト
original_data = [1, 2, float('nan'), 4, float('nan'), 6]
print(f"元データ: {original_data}")
print(f"NaN除去: {remove_nan(original_data)}")
print(f"NaN置換(0): {replace_nan(original_data, 0)}")
print(f"NaN置換(平均): {replace_nan(original_data, sum(remove_nan(original_data))/len(remove_nan(original_data)))}")
統計計算でのNaN対応
import math
def safe_mean(data):
"""NaNを除外した平均値計算"""
valid_data = [x for x in data if not math.isnan(x)]
return sum(valid_data) / len(valid_data) if valid_data else float('nan')
def safe_max(data):
"""NaNを除外した最大値計算"""
valid_data = [x for x in data if not math.isnan(x)]
return max(valid_data) if valid_data else float('nan')
def safe_min(data):
"""NaNを除外した最小値計算"""
valid_data = [x for x in data if not math.isnan(x)]
return min(valid_data) if valid_data else float('nan')
# テストデータ
test_data = [1, 2, float('nan'), 4, 5, float('nan')]
print(f"データ: {test_data}")
print(f"安全な平均: {safe_mean(test_data)}")
print(f"安全な最大: {safe_max(test_data)}")
print(f"安全な最小: {safe_min(test_data)}")
# 全てNaNの場合
all_nan = [float('nan')] * 3
print(f"全NaNデータ: {all_nan}")
print(f"全NaN平均: {safe_mean(all_nan)}")
ファイル読み込み時のNaN処理
CSV読み込みでのNaN検出
import math
def parse_numeric_field(field_str):
"""文字列フィールドを数値に変換(NaN対応)"""
field_str = field_str.strip().lower()
# 空文字列や特定の文字列をNaNとして扱う
nan_indicators = ['', 'na', 'nan', 'null', 'none', '#n/a']
if field_str in nan_indicators:
return float('nan')
try:
return float(field_str)
except ValueError:
return float('nan')
# CSVライクなデータの処理例
csv_like_data = [
"1.5", "2.0", "", "4.5", "NA", "6.0", "invalid"
]
parsed_data = [parse_numeric_field(field) for field in csv_like_data]
print(f"元データ: {csv_like_data}")
print(f"変換後: {parsed_data}")
print(f"NaN数: {sum(1 for x in parsed_data if math.isnan(x))}")
NaNの伝播と計算への影響
計算でのNaN伝播
import math
def demonstrate_nan_propagation():
"""NaNの伝播を実演"""
nan = float('nan')
# 算術演算でのNaN伝播
operations = [
('加算', nan + 5),
('減算', 10 - nan),
('乗算', nan * 3),
('除算', nan / 2),
('べき乗', nan ** 2)
]
print("NaN伝播の例:")
for op_name, result in operations:
print(f"{op_name}: {result} (isnan: {math.isnan(result)})")
demonstrate_nan_propagation()
集計関数への影響
import math
def aggregation_with_nan():
"""集計関数でのNaN影響を確認"""
data_with_nan = [1, 2, float('nan'), 4, 5]
# 標準の集計関数
try:
total = sum(data_with_nan)
average = total / len(data_with_nan)
maximum = max(data_with_nan)
minimum = min(data_with_nan)
print(f"データ: {data_with_nan}")
print(f"合計: {total} (isnan: {math.isnan(total)})")
print(f"平均: {average} (isnan: {math.isnan(average)})")
print(f"最大: {maximum} (isnan: {math.isnan(maximum)})")
print(f"最小: {minimum} (isnan: {math.isnan(minimum)})")
except Exception as e:
print(f"エラー: {e}")
aggregation_with_nan()
デバッグとログ出力
NaNの詳細情報表示
import math
def debug_nan_info(value, var_name="value"):
"""NaNの詳細情報を表示"""
if not isinstance(value, (int, float)):
print(f"{var_name}: 数値型ではありません ({type(value)})")
return
info = {
'値': value,
'タイプ': type(value).__name__,
'isnan': math.isnan(value),
'isinf': math.isinf(value),
'isfinite': math.isfinite(value),
'文字列表現': str(value),
'自己等価性': value == value
}
print(f"=== {var_name} の詳細情報 ===")
for key, val in info.items():
print(f"{key}: {val}")
print()
# 様々な値でテスト
test_values = [
(42, "正常な整数"),
(3.14, "正常な浮動小数点"),
(float('nan'), "NaN"),
(math.inf, "正の無限大"),
(-math.inf, "負の無限大")
]
for val, desc in test_values:
debug_nan_info(val, desc)
パフォーマンス比較
NaN判定方法の速度比較
import math
import time
def benchmark_nan_detection():
"""NaN判定方法の性能比較"""
test_value = float('nan')
iterations = 1000000
# math.isnan()
start = time.time()
for _ in range(iterations):
result = math.isnan(test_value)
isnan_time = time.time() - start
# 自己比較
start = time.time()
for _ in range(iterations):
result = test_value != test_value
self_compare_time = time.time() - start
# 文字列比較
start = time.time()
for _ in range(iterations):
result = str(test_value).lower() == 'nan'
str_compare_time = time.time() - start
print("NaN判定方法の性能比較:")
print(f"math.isnan(): {isnan_time:.6f}秒")
print(f"自己比較: {self_compare_time:.6f}秒")
print(f"文字列比較: {str_compare_time:.6f}秒")
benchmark_nan_detection()
まとめ
PythonにおけるNaN(非数)は、不正な数学演算の結果を表現するIEEE 754標準の特殊値です。math.isnan()が最も確実で高速な判定方法であり、NaN特有の「自分自身と等しくない」性質も判定に利用できます。データ処理において、NaNは計算結果に伝播するため、適切な検出と処理(除去、置換、エラーハンドリング)が重要です。科学計算やデータ分析では、NaNを含むデータの前処理が結果の正確性に直結するため、これらの手法を適切に使い分けることが重要です。
■「らくらくPython塾」が切り開く「呪文コーディング」とは?
■プロンプトだけでオリジナルアプリを開発・公開してみた!!
■AI時代の第一歩!「AI駆動開発コース」はじめました!
テックジム東京本校で先行開始。
■テックジム東京本校
「武田塾」のプログラミング版といえば「テックジム」。
講義動画なし、教科書なし。「進捗管理とコーチング」で効率学習。
より早く、より安く、しかも対面型のプログラミングスクールです。
<短期講習>5日で5万円の「Pythonミニキャンプ」開催中。
<月1開催>放送作家による映像ディレクター養成講座
<オンライン無料>ゼロから始めるPython爆速講座

