Python 中 NaN 和 None 的詳細比較
python原生的None和pandas, numpy中的numpy.NaN儘管在功能上都是用來標示空缺資料。但它們的行為在很多場景下確有一些相當大的差異。由於不熟悉這些差異,曾經給我的工作帶來過不少麻煩。 特此整理了一份詳細的實驗,比較None和NaN在不同場景下的差異。
實驗的結果有些在意料之內,有些則讓我大跌眼鏡。希望讀者看過此文後會None和NaN這對“小妖精”有更深的理解。
為了理解本文的內容,希望本文的讀者需要對pandas的Series使用有一定的經驗。
首先,匯入所需的庫
In[2]:
Python123 | fromnumpy importNaNfrompandas importSeries,DataFrameimportnumpy asnp |
資料型別?
None是一個python特殊的資料型別, 但是NaN卻是用一個特殊的float
In[3]:
Python1 | type(None) |
Out[3]:
Python1 | NoneType |
In[4]:
Python1 | type(NaN) |
Out[4]:
Python1 | float |
能作為dict的key?
In[5]:
Python1 | {None:1} |
Out[5]:
Python1 | {None:1} |
In[6]:
Python1 | {NaN:1} |
Out[6]:
Python1 | {nan:1} |
In[7]:
Python1 | {None:1,NaN:2} |
Out[7]:
Python1 | {nan:2,None:1} |
都可以,而且會被認為是不同的key
Series函式中的表現
Series.map
In[8]:
Python12 | s=Series([None,NaN,'a'])s |
Out[8]:
Python1234 | 0None1NaN2adtype:object |
In[9]:
Python1 | s.map({None:1,'a':'a'}) |
Out[9]:
Python1234 | 01112adtype:object |
可以看到None和NaN都會替換成了1
In[10]:
Python1 | s.map({NaN:1,'a':'a'}) |
Out[10]:
Python1234 | 01112adtype:object |
同樣None和NaN都會替換成了1
In[11]:
Python1 | s.map({NaN:2,'None':1,'a':'a'}) |
Out[11]:
Python1234 | 02122adtype:object |
將None替換成1的要求被忽略了
In[12]:
Python1 | s.map({'None':1,NaN:2,'a':'a'}) |
Out[12]:
Python1234 | 02122adtype:object |
將NaN替換成1的要求被忽略了
總結: 用Series.map對None進行替換時,會“順便”把NaN也一起替換掉;NaN也會順便把None替換掉。
如果None和NaN分別定義了不同的對映數值,那麼只有一個會生效。
Series.replace中的表現
In[13]:
Python12 | s=Series([None,NaN,'a'])s |
Out[13]:
Python1234 | 0None1NaN2adtype:object |
In[14]:
Python1 | s.replace([NaN],9) |
Out[14]:
Python1234 | 09192adtype:object |
In[15]:
Python1 | s.replace([None],9) |
Out[15]:
Python1234 | 09192adtype:object |
和Series.map的情況類似,指定了None的替換值後,NaN會被替換掉;反之亦然。
對函式的支援
numpy有不少函式可以自動處理NaN。
In[16]:
Python1 | np.nansum([1,2,NaN]) |
Out[16]:
Python1 | 3.0 |
但是None不能享受這些函式的便利,如果資料包含的None的話會報錯
In[17]:
Python1234 | try:np.nansum([1,2,None])exceptExceptionase:print(type(e),e) |
unsupported operand type(s) for +: ‘int’ and ‘NoneType’
pandas中也有不少函式支援NaN卻不支援None。(畢竟pandas的底層是numpy)
In[18]:
Python