TL;DR
a is np.nan じゃなくて np.isnan(a) を使おう。
nan
nan (Not A Number) です。nan「数値」じゃなくて、その比較についてはIEEE754で定義されています。これは何と比較しても等しくならない。nan == nan すらFalse です。Python の場合、math.nan と np.nan があります。(他にもあるかも知れないけれど。)
一部で流通している誤解: 「a is np.nan でチェックできるよ」
a が nan の場合、a == np.nan ではチェックできません。なぜならば上述のとおり、IEEE754でそう決められているから。で、どうするかと言うと、いくつかのサイトでは a is np.nan とすればいいよ、とあります。確かに多くの場合これでうまく行くのですが、これはどうやらnumpyやmathの実装上、たまたまうまく行く(同一のnanオブジェクトを参照するようになっている)ことが多い だけのようで、基本的に間違ったチェック方法と断じたいと思います。
というわけで反例。
# 以下は Python 3.91 + numpy 1.19.4 で試しました。math の nan でも基本的に同じです。
import numpy as np
a = np.nan
b = a * 2
print(a) # => nan
print(b) # => nan
print(a is np.nan) # => True (これがよく使われる。これはok)
print(b is np.nan) # => False (こういうことが起こる。)
nan に対して演算をしてしまうことはありますよね? その結果はやはり(当然?) nan です。ここで is で比較をしてしまうと、参照先が異なるようで Flase になってしまいます。だから a is np.nan は(nan かどうかのチェックとしては)誤り。
正解は?
素直に np.isnan() を使うのが良いでしょう。わかりやすいし。定義を逆手に取った裏技(?)として自身との比較 a == a (が False だったら nan) というのもあるのですが、混乱するのでぼくは遠慮しときます。
悪者は誰か? (個人の感想です。あまりツッコまないでください。)
Python です(バッサリ)。is なんてモノを導入したのが諸悪の根源だと。こういう「直観的な機能が曖昧なbe動詞」に「参照先の比較」という大切な働きをさせるから混乱が生じる。さらに悪いことに、is の式は読みようによっては(意味を勘違いしたままでも)英文的にわかりやすいので広まっちゃう。is なんかなくたって、id() があるんだから、それ使えば簡潔・正確・誤用防止といいことづくめなのに…。