python原生的None和pandas, numpy中的numpy.NaN尽管在功能上都是用来标示空缺数据。但它们的行为在很多场景下确有一些相当大的差异。由于不熟悉这些差异,曾经给我的工作带来过不少麻烦。 特此整理了一份详细的实验,比较None和NaN在不同场景下的差异。

实验的结果有些在意料之内,有些则让我大跌眼镜。希望读者看过此文后会None和NaN这对“小妖精”有更深的理解。

为了理解本文的内容,希望本文的读者需要对pandas的Series使用有一定的经验。

首先,导入所需的库

In[2]:

 
 
 
 
 

Python

 
1
2
3
fromnumpy importNaN
frompandas importSeries,DataFrame
importnumpy asnp

数据类型?

None是一个python特殊的数据类型, 但是NaN却是用一个特殊的float

In[3]:

 
 
 
 
 

Python

 
1
type(None)

Out[3]:

 
 
 
 
 

Python

 
1
NoneType

In[4]:

 
 
 
 
 

Python

 
1
type(NaN)

Out[4]:

 
 
 
 
 

Python

 
1
float

能作为dict的key?

In[5]:

 
 
 
 
 

Python

 
1
{None:1}

Out[5]:

 
 
 
 
 

Python

 
1
{None:1}

In[6]:

 
 
 
 
 

Python

 
1
{NaN:1}

Out[6]:

 
 
 
 
 

Python

 
1
{nan:1}

In[7]:

 
 
 
 
 

Python

 
1
{None:1,NaN:2}

Out[7]:

 
 
 
 
 

Python

 
1
{nan:2,None:1}

都可以,而且会被认为是不同的key

Series函数中的表现

Series.map

In[8]:

 
 
 
 
 

Python

 
1
2
s=Series([None,NaN,'a'])
s

Out[8]:

 
 
 
 
 

Python

 
1
2
3
4
0    None
1     NaN
2       a
dtype:object

In[9]:

 
 
 
 
 

Python

 
1
s.map({None:1,'a':'a'})

Out[9]:

 
 
 
 
 

Python

 
1
2
3
4
0    1
1    1
2    a
dtype:object

可以看到None和NaN都会替换成了1

In[10]:

 
 
 
 
 

Python

 
1
s.map({NaN:1,'a':'a'})

Out[10]:

 
 
 
 
 

Python

 
1
2
3
4
0    1
1    1
2    a
dtype:object

同样None和NaN都会替换成了1

In[11]:

 
 
 
 
 

Python

 
1
s.map({NaN:2,'None':1,'a':'a'})

Out[11]:

 
 
 
 
 

Python

 
1
2
3
4
0    2
1    2
2    a
dtype:object

将None替换成1的要求被忽略了

In[12]:

 
 
 
 
 

Python

 
1
s.map({'None':1,NaN:2,'a':'a'})

Out[12]:

 
 
 
 
 

Python

 
1
2
3
4
0    2
1    2
2    a
dtype:object

将NaN替换成1的要求被忽略了

总结: 用Series.map对None进行替换时,会“顺便”把NaN也一起替换掉;NaN也会顺便把None替换掉。

如果None和NaN分别定义了不同的映射数值,那么只有一个会生效。

Series.replace中的表现

In[13]:

 
 
 
 
 

Python

 
1
2
s=Series([None,NaN,'a'])
s

Out[13]:

 
 
 
 
 

Python

 
1
2
3
4
0    None
1     NaN
2       a
dtype:object

In[14]:

 
 
 
 
 

Python

 
1
s.replace([NaN],9)

Out[14]:

 
 
 
 
 

Python

 
1
2
3
4
0    9
1    9
2    a
dtype:object

In[15]:

 
 
 
 
 

Python

 
1
s.replace([None],9)

Out[15]:

 
 
 
 
 

Python

 
1
2
3
4
0    9
1    9
2    a
dtype:object

和Series.map的情况类似,指定了None的替换值后,NaN会被替换掉;反之亦然。

对函数的支持

numpy有不少函数可以自动处理NaN。

In[16]:

 
 
 
 
 

Python

 
1
np.nansum([1,2,NaN])

Out[16]:

 
 
 
 
 

Python

 
1
3.0

但是None不能享受这些函数的便利,如果数据包含的None的话会报错

In[17]:

 
 
 
 
 

Python

 
1
2
3
4
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

 
1
2
importpandas aspd
pd.cut(Series([NaN]),[1,2])

Out[18]:

 
 
 
 
 

Python

 
1
2
3
0    NaN
dtype:category
Categories(1,object):[(1,2]]

In[19]:

 
 
 
 
 

Python

 
1
2
3
4
5
importpandas aspd
try:
    pd.cut(Series([None]),[1,2])
exceptExceptionase:
    print(type(e),e)

unorderable types: int() > NoneType()

对容器数据类型的影响

混入numpy.array的影响

如果数据中含有None,会导致整个array的类型变成object。

In[20]:

 
 
 
 
 

Python

 
1
np.array([1,None]).dtype

Out[20]:

 
 
 
 
 

Python

 
1
dtype('O')

而np.NaN尽管会将原本用int类型就能保存的数据转型成float,但不会带来上面这个问题。

In[21]:

 
 
 
 
 

Python

 
1
np.array([1,NaN]).dtype

Out[21]:

 
 
 
 
 

Python

 
1
dtype('float64')

混入Series的影响

下面的结果估计大家能猜到

In[22]:

 
 
 
 
 

Python

 
1
Series([1,NaN])

Out[22]:

 
 
 
 
 

Python

 
1
2
3
0    1.0
1    NaN
dtype:float64

下面的这个就很意外的吧

In[23]:

 
 
 
 
 

Python

 
1
Series([1,None])

Out[23]:

 
 
 
 
 

Python

 
1
2
3
0    1.0
1    NaN
dtype:float64

pandas将None自动替换成了NaN!

In[24]:

 
 
 
 
 

Python

 
1
Series([1.0,None])

Out[24]:

 
 
 
 
 

Python

 
1
2
3
0    1.0
1    NaN
dtype:float64

却是Object类型的None被替换成了float类型的NaN。 这么设计可能是因为None无法参与numpy的大多数计算, 而pandas的底层又依赖于numpy,因此做了这样的自动转化。

不过如果本来Series就只能用object类型容纳的话, Series不会做这样的转化工作。

In[25]:

 
 
 
 
 

Python

 
1
Series(['a',None])

Out[25]:

 
 
 
 
 

Python

 
1
2
3
0       a
1    None
dtype:object

如果Series里面都是None的话也不会做这样的转化

In[26]:

 
 
 
 
 

Python

 
1
Series([None,None])

Out[26]:

 
 
 
 
 

Python

 
1
2
3
0    None
1    None
dtype:object

其它的数据类型是bool时,也不会做这样的转化。

In[27]:

 
 
 
 
 

Python

 
1
Series([True,False,None])

Out[27]:

 
 
 
 
 

Python

 
1
2
3
4
0     True
1    False
2     None
dtype:object

等值性判断

单值的等值性比较

下面的实验中None和NaN的表现会作为后面的等值性判断的基准(后文称为基准)

In[28]:

 
 
 
 
 

Python

 
1
None==None

Out[28]:

 
 
 
 
 

Python

 
1
True

In[29]:

 
 
 
 
 

Python

 
1
NaN==NaN

Out[29]:

 
 
 
 
 

Python

 
1
False

In[30]:

 
 
 
 
 

Python

 
1
None==NaN

Out[30]:

 
 
 
 
 

Python

 
1
False

在tuple中的情况

这个不奇怪

In[31]:

 
 
 
 
 

Python

 
1
(1,None)==(1,None)

Out[31]:

 
 
 
 
 

Python

 
1
True

这个也不意外

In[32]:

 
 
 
 
 

Python

 
1
(1,None)==(1,NaN)

Out[32]:

 
 
 
 
 

Python

 
1
False

但是下面这个实验NaN的表现和基准不一致

In[33]:

 
 
 
 
 

Python

 
1
(1,NaN)==(1,NaN)

Out[33]:

 
 
 
 
 

Python

 
1
True

在numpy.array中的情况

In[34]:

 
 
 
 
 

Python

 
1
np.array([1,None])==np.array([1,None])

Out[34]:

 
 
 
 
 

Python

 
1
array([True,  True],dtype=bool)

In[35]:

 
 
 
 
 

Python

 
1
np.array([1,NaN])==np.array([1,NaN])

Out[35]:

 
 
 
 
 

Python

 
1
array([True,False],dtype=bool)

In[36]:

 
 
 
 
 

Python

 
1
np.array([1,NaN])==np.array([1,None])

Out[36]:

 
 
 
 
 

Python

 
1
array([True,False],dtype=bool)

和基准的表现一致。

但是大部分情况我们希望上面例子中, 我们希望左右两边的array被判定成一致。这时可以用numpy.testing.assert_equal函数来处理。 注意这个函数的表现同assert, 不会返回True, False, 而是无反应或者raise Exception

In[37]:

 
 
 
 
 

Python

 
1
np.testing.assert_equal(np.array([1,NaN]),np.array([1,NaN]))

它也可以处理两边都是None的情况

In[38]:

 
 
 
 
 

Python

 
1
np.testing.assert_equal(np.array([1,None]),np.array([1,None]))

但是一边是None,一边是NaN时会被认为两边不一致, 导致AssertionError

In[39]:

 
 
 
 
 

Python

 
1
2
3
4
try:
    np.testing.assert_equal(np.array([1,NaN]),np.array([1,None]))
exceptExceptionase:
    print(type(e),e)
 
 
 
 
 

Python

 
1
2
3
4
5
6
<class'assertionerror'="">
Arrays are notequal
 
(mismatch50.0%)
x:array([  1.,  nan])
y:array([1,None],dtype=object)

在Series中的情况

下面两个实验中的表现和基准一致

In[40]:

 
 
 
 
 

Python

 
1
Series([NaN,'a'])==Series([NaN,'a'])

Out[40]:

 
 
 
 
 

Python

 
1
2
3
0    False
1     True
dtype:bool

In[41]:

 
 
 
 
 

Python

 
1
Series([None,'a'])==Series([NaN,'a'])

Out[41]:

 
 
 
 
 

Python

 
1
2
3
0    False
1     True
dtype:bool

但是None和基准的表现不一致。

In[42]:

 
 
 
 
 

Python

 
1
Series([None,'a'])==Series([None,'a'])

Out[42]:

 
 
 
 
 

Python

 
1
2
3
0    False
1     True
dtype:bool

和array类似,Series也有专门的函数equals用于判断两边的Series是否整体看相等

In[43]:

 
 
 
 
 

Python

 
1
Series([None,'a']).equals(Series([NaN,'a']))

Out[43]:

 
 
 
 
 

Python

 
1
True

In[44]:

 
 
 
 
 

Python

 
1
Series([None,'a']).equals(Series([None,'a']))

Out[44]:

 
 
 
 
 

Python

 
1
True

In[45]:

 
 
 
 
 

Python

 
1
Series([NaN,'a']).equals(Series([NaN,'a']))

Out[45]:

 
 
 
 
 

Python

 
1
True

比numpy.testing.assert_equals更智能些, 三种情况下都能恰当的处理

在DataFrame merge中的表现

两边的None会被判为相同

In[46]:

 
 
 
 
 

Python

 
1
2
3
a=DataFrame({'A':[None,'a']})
b=DataFrame({'A':[None,'a']})
a.merge(b,on='A',how='outer')

Out[46]:

  A
0 None
1 a

两边的NaN会被判为相同

In[47]:

 
 
 
 
 

Python

 
1
2
3
a=DataFrame({'A':[NaN,'a']})
b=DataFrame({'A':[NaN,'a']})
a.merge(b,on='A',how='outer')

Out[47]:

  A
0 NaN
1 a

无论两边都是None,都是NaN,还是都有,相关的列都会被正确的匹配。 注意一边是None,一边是NaN的时候。会以左侧的结果为准。

In[48]:

 
 
 
 
 

Python

 
1
2
3
a=DataFrame({'A':[None,'a']})
b=DataFrame({'A':[NaN,'a']})
a.merge(b,on='A',how='outer')

Out[48]:

  A
0 None
1 a

In[49]:

 
 
 
 
 

Python

 
1
2
3
a=DataFrame({'A':[NaN,'a']})
b=DataFrame({'A':[None,'a']})
a.merge(b,on='A',how='outer')

Out[49]:

  A
0 NaN
1 a

注意

这和空值在postgresql等sql数据库中的表现不一样, 在数据库中, join时两边的空值会被判定为不同的数值

在groupby中的表现

In[50]:

 
 
 
 
 

Python

 
1
2
d=DataFrame({'A':[1,1,1,1,2],'B':[None,None,'a','a','b']})
d.groupby(['A','B']).apply(len)

Out[50]:

 
 
 
 
 

Python

 
1
2
3
4
A  B
1  a    2
2  b    1
dtype:int64

可以看到(1, NaN)对应的组直接被忽略了

In[51]:

 
 
 
 
 

Python

 
1
2
d=DataFrame({'A':[1,1,1,1,2],'B':[None,None,'a','a','b']})
d.groupby(['A','B']).apply(len)

Out[51]:

 
 
 
 
 

Python

 
1
2
3
4
A  B
1  a    2
2  b    1
dtype:int64

(1,None)的组也被直接忽略了

In[52]:

 
 
 
 
 

Python

 
1
2
d=DataFrame({'A':[1,1,1,1,2],'B':[None,NaN,'a','a','b']})
d.groupby(['A','B']).apply(len)

Out[52]:

 
 
 
 
 

Python

 
1
2
3
4
A  B
1  a    2
2  b    1
dtype:int64

那么上面这个结果应该没啥意外的

总结

DataFrame.groupby会忽略分组列中含有None或者NaN的记录

支持写入数据库?

往数据库中写入时NaN不可处理,需转换成None,否则会报错。这个这里就不演示了。

相信作为pandas老司机, 至少能想出两种替换方法。

In[53]:

 
 
 
 
 

Python

 
1
2
s=Series([None,NaN,'a'])
s

Out[53]:

 
 
 
 
 

Python

 
1
2
3
4
0    None
1     NaN
2       a
dtype:object

方案1

In[54]:

 
 
 
 
 

Python

 
1
s.replace([NaN],None)

Out[54]:

 
 
 
 
 

Python

 
1
2
3
4
0    None
1    None
2       a
dtype:object

方案2

In[55]:

 
 
 
 
 

Python

 
1
2
s[s.isnull()]=None
s

Out[55]:

 
 
 
 
 

Python

 
1
2
3
4
0    None
1    None
2       a
dtype:object

然而这么就觉得完事大吉的话就图样图森破了, 看下面的例子

In[56]:

 
 
 
 
 

Python

 
1
2
s=Series([NaN,1])
s

Out[56]:

 
 
 
 
 

Python

 
1
2
3
0    NaN
1    1.0
dtype:float64

In[57]:

 
 
 
 
 

Python

 
1
s.replace([NaN],None)

Out[57]:

 
 
 
 
 

Python

 
1
2
3
0    NaN
1    1.0
dtype:float64

In[58]:

 
 
 
 
 

Python

 
1
2
s[s.isnull()]=None
s

Out[58]:

 
 
 
 
 

Python

 
1
2
3
0    NaN
1    1.0
dtype:float64

当其他数据是int或float时,Series又一声不吭的自动把None替换成了NaN。

这时候可以使用第三种方法处理

In[59]:

 
 
 
 
 

Python

 
1
s.where(s.notnull(),None)

Out[59]:

 
 
 
 
 

Python

 
1
2
3
0    None
1       1
dtype:object

where语句会遍历s中所有的元素,逐一检查条件表达式, 如果成立, 从原来的s取元素; 否则用None填充。 这回没有自动替换成NaN

None vs NaN要点总结

  1. 在pandas中, 如果其他的数据都是数值类型, pandas会把None自动替换成NaN, 甚至能将s[s.isnull()]= None,和s.replace(NaN, None)操作的效果无效化。 这时需要用where函数才能进行替换。
  2. None能够直接被导入数据库作为空值处理, 包含NaN的数据导入时会报错。
  3. numpy和pandas的很多函数能处理NaN,但是如果遇到None就会报错。
  4. None和NaN都不能被pandas的groupby函数处理,包含None或者NaN的组都会被忽略。

等值性比较的总结:(True表示被判定为相等)

  None对None NaN对NaN None对NaN
单值 True False False
tuple(整体) True True False
np.array(逐个) True False False
Series(逐个) False False False
assert_equals True True False
Series.equals True True True
merge True True True

由于等值性比较方面,None和NaN在各场景下表现不太一致,相对来说None表现的更稳定。

为了不给自己惹不必要的麻烦和额外的记忆负担。 实践中,建议遵循以下三个原则即可

  • 在用pandas和numpy处理数据阶段将None,NaN统一处理成NaN,以便支持更多的函数。
  • 如果要判断Series,numpy.array整体的等值性,用专门的Series.equals,numpy.array函数去处理,不要自己用==判断 *
  • 如果要将数据导入数据库,将NaN替换成None

转自:http://python.jobbole.com/87266/

python原生的None和pandas, numpy中的numpy.NaN尽管在功能上都是用来标示空缺数据。但它们的行为在很多场景下确有一些相当大的差异。由于不熟悉这些差异,曾经给我的工作带来过不少麻烦。 特此整理了一份详细的实验,比较None和NaN在不同场景下的差异。

实验的结果有些在意料之内,有些则让我大跌眼镜。希望读者看过此文后会None和NaN这对“小妖精”有更深的理解。

为了理解本文的内容,希望本文的读者需要对pandas的Series使用有一定的经验。

首先,导入所需的库

In[2]:

 
 
 
 
 

Python

 
1
2
3
fromnumpy importNaN
frompandas importSeries,DataFrame
importnumpy asnp

数据类型?

None是一个python特殊的数据类型, 但是NaN却是用一个特殊的float

In[3]:

 
 
 
 
 

Python

 
1
type(None)

Out[3]:

 
 
 
 
 

Python

 
1
NoneType

In[4]:

 
 
 
 
 

Python

 
1
type(NaN)

Out[4]:

 
 
 
 
 

Python

 
1
float

能作为dict的key?

In[5]:

 
 
 
 
 

Python

 
1
{None:1}

Out[5]:

 
 
 
 
 

Python

 
1
{None:1}

In[6]:

 
 
 
 
 

Python

 
1
{NaN:1}

Out[6]:

 
 
 
 
 

Python

 
1
{nan:1}

In[7]:

 
 
 
 
 

Python

 
1
{None:1,NaN:2}

Out[7]:

 
 
 
 
 

Python

 
1
{nan:2,None:1}

都可以,而且会被认为是不同的key

Series函数中的表现

Series.map

In[8]:

 
 
 
 
 

Python

 
1
2
s=Series([None,NaN,'a'])
s

Out[8]:

 
 
 
 
 

Python

 
1
2
3
4
0    None
1     NaN
2       a
dtype:object

In[9]:

 
 
 
 
 

Python

 
1
s.map({None:1,'a':'a'})

Out[9]:

 
 
 
 
 

Python

 
1
2
3
4
0    1
1    1
2    a
dtype:object

可以看到None和NaN都会替换成了1

In[10]:

 
 
 
 
 

Python

 
1
s.map({NaN:1,'a':'a'})

Out[10]:

 
 
 
 
 

Python

 
1
2
3
4
0    1
1    1
2    a
dtype:object

同样None和NaN都会替换成了1

In[11]:

 
 
 
 
 

Python

 
1
s.map({NaN:2,'None':1,'a':'a'})

Out[11]:

 
 
 
 
 

Python

 
1
2
3
4
0    2
1    2
2    a
dtype:object

将None替换成1的要求被忽略了

In[12]:

 
 
 
 
 

Python

 
1
s.map({'None':1,NaN:2,'a':'a'})

Out[12]:

 
 
 
 
 

Python

 
1
2
3
4
0    2
1    2
2    a
dtype:object

将NaN替换成1的要求被忽略了

总结: 用Series.map对None进行替换时,会“顺便”把NaN也一起替换掉;NaN也会顺便把None替换掉。

如果None和NaN分别定义了不同的映射数值,那么只有一个会生效。

Series.replace中的表现

In[13]:

 
 
 
 
 

Python

 
1
2
s=Series([None,NaN,'a'])
s

Out[13]:

 
 
 
 
 

Python

 
1
2
3
4
0    None
1     NaN
2       a
dtype:object

In[14]:

 
 
 
 
 

Python

 
1
s.replace([NaN],9)

Out[14]:

 
 
 
 
 

Python

 
1
2
3
4
0    9
1    9
2    a
dtype:object

In[15]:

 
 
 
 
 

Python

 
1
s.replace([None],9)

Out[15]:

 
 
 
 
 

Python

 
1
2
3
4
0    9
1    9
2    a
dtype:object

和Series.map的情况类似,指定了None的替换值后,NaN会被替换掉;反之亦然。

对函数的支持

numpy有不少函数可以自动处理NaN。

In[16]:

 
 
 
 
 

Python

 
1
np.nansum([1,2,NaN])

Out[16]:

 
 
 
 
 

Python

 
1
3.0

但是None不能享受这些函数的便利,如果数据包含的None的话会报错

In[17]:

 
 
 
 
 

Python

 
1
2
3
4
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

 
1
2
importpandas aspd
pd.cut(Series([NaN]),[1,2])

Out[18]:

 
 
 
 
 

Python

 
1
2
3
0    NaN
dtype:category
Categories(1,object):[(1,2]]

In[19]:

 
 
 
 
 

Python

 
1
2
3
4
5
importpandas aspd
try:
    pd.cut(Series([None]),[1,2])
exceptExceptionase:
    print(type(e),e)

unorderable types: int() > NoneType()

对容器数据类型的影响

混入numpy.array的影响

如果数据中含有None,会导致整个array的类型变成object。

In[20]:

 
 
 
 
 

Python

 
1
np.array([1,None]).dtype

Out[20]:

 
 
 
 
 

Python

 
1
dtype('O')

而np.NaN尽管会将原本用int类型就能保存的数据转型成float,但不会带来上面这个问题。

In[21]:

 
 
 
 
 

Python

 
1
np.array([1,NaN]).dtype

Out[21]:

 
 
 
 
 

Python

 
1
dtype('float64')

混入Series的影响

下面的结果估计大家能猜到

In[22]:

 
 
 
 
 

Python

 
1
Series([1,NaN])

Out[22]:

 
 
 
 
 

Python

 
1
2
3
0    1.0
1    NaN
dtype:float64

下面的这个就很意外的吧

In[23]:

 
 
 
 
 

Python

 
1
Series([1,None])

Out[23]:

 
 
 
 
 

Python

 
1
2
3
0    1.0
1    NaN
dtype:float64

pandas将None自动替换成了NaN!

In[24]:

 
 
 
 
 

Python

 
1
Series([1.0,None])

Out[24]:

 
 
 
 
 

Python

 
1
2
3
0    1.0
1    NaN
dtype:float64

却是Object类型的None被替换成了float类型的NaN。 这么设计可能是因为None无法参与numpy的大多数计算, 而pandas的底层又依赖于numpy,因此做了这样的自动转化。

不过如果本来Series就只能用object类型容纳的话, Series不会做这样的转化工作。

In[25]:

 
 
 
 
 

Python

 
1
Series(['a',None])

Out[25]:

 
 
 
 
 

Python

 
1
2
3
0       a
1    None
dtype:object

如果Series里面都是None的话也不会做这样的转化

In[26]:

 
 
 
 
 

Python

 
1
Series([None,None])

Out[26]:

 
 
 
 
 

Python

 
1
2
3
0    None
1    None
dtype:object

其它的数据类型是bool时,也不会做这样的转化。

In[27]:

 
 
 
 
 

Python

 
1
Series([True,False,None])

Out[27]:

 
 
 
 
 

Python

 
1
2
3
4
0     True
1    False
2     None
dtype:object

等值性判断

单值的等值性比较

下面的实验中None和NaN的表现会作为后面的等值性判断的基准(后文称为基准)

In[28]:

 
 
 
 
 

Python

 
1
None==None

Out[28]:

 
 
 
 
 

Python

 
1
True

In[29]:

 
 
 
 
 

Python

 
1
NaN==NaN

Out[29]:

 
 
 
 
 

Python

 
1
False

In[30]:

 
 
 
 
 

Python

 
1
None==NaN

Out[30]:

 
 
 
 
 

Python

 
1
False

在tuple中的情况

这个不奇怪

In[31]:

 
 
 
 
 

Python

 
1
(1,None)==(1,None)

Out[31]:

 
 
 
 
 

Python

 
1
True

这个也不意外

In[32]:

 
 
 
 
 

Python

 
1
(1,None)==(1,NaN)

Out[32]:

 
 
 
 
 

Python

 
1
False

但是下面这个实验NaN的表现和基准不一致

In[33]:

 
 
 
 
 

Python

 
1
(1,NaN)==(1,NaN)

Out[33]:

 
 
 
 
 

Python

 
1
True

在numpy.array中的情况

In[34]:

 
 
 
 
 

Python

 
1
np.array([1,None])==np.array([1,None])

Out[34]:

 
 
 
 
 

Python

 
1
array([True,  True],dtype=bool)

In[35]:

 
 
 
 
 

Python

 
1
np.array([1,NaN])==np.array([1,NaN])

Out[35]:

 
 
 
 
 

Python

 
1
array([True,False],dtype=bool)

In[36]:

 
 
 
 
 

Python

 
1
np.array([1,NaN])==np.array([1,None])

Out[36]:

 
 
 
 
 

Python

 
1
array([True,False],dtype=bool)

和基准的表现一致。

但是大部分情况我们希望上面例子中, 我们希望左右两边的array被判定成一致。这时可以用numpy.testing.assert_equal函数来处理。 注意这个函数的表现同assert, 不会返回True, False, 而是无反应或者raise Exception

In[37]:

 
 
 
 
 

Python

 
1
np.testing.assert_equal(np.array([1,NaN]),np.array([1,NaN]))

它也可以处理两边都是None的情况

In[38]:

 
 
 
 
 

Python

 
1
np.testing.assert_equal(np.array([1,None]),np.array([1,None]))

但是一边是None,一边是NaN时会被认为两边不一致, 导致AssertionError

In[39]:

 
 
 
 
 

Python

 
1
2
3
4
try:
    np.testing.assert_equal(np.array([1,NaN]),np.array([1,None]))
exceptExceptionase:
    print(type(e),e)
 
 
 
 
 

Python

 
1
2
3
4
5
6
<class'assertionerror'="">
Arrays are notequal
 
(mismatch50.0%)
x:array([  1.,  nan])
y:array([1,None],dtype=object)

在Series中的情况

下面两个实验中的表现和基准一致

In[40]:

 
 
 
 
 

Python

 
1
Series([NaN,'a'])==Series([NaN,'a'])

Out[40]:

 
 
 
 
 

Python

 
1
2
3
0    False
1     True
dtype:bool

In[41]:

 
 
 
 
 

Python

 
1
Series([None,'a'])==Series([NaN,'a'])

Out[41]:

 
 
 
 
 

Python

 
1
2
3
0    False
1     True
dtype:bool

但是None和基准的表现不一致。

In[42]:

 
 
 
 
 

Python

 
1
Series([None,'a'])==Series([None,'a'])

Out[42]:

 
 
 
 
 

Python

 
1
2
3
0    False
1     True
dtype:bool

和array类似,Series也有专门的函数equals用于判断两边的Series是否整体看相等

In[43]:

 
 
 
 
 

Python

 
1
Series([None,'a']).equals(Series([NaN,'a']))

Out[43]:

 
 
 
 
 

Python

 
1
True

In[44]:

 
 
 
 
 

Python

 
1
Series([None,'a']).equals(Series([None,'a']))

Out[44]:

 
 
 
 
 

Python

 
1
True

In[45]:

 
 
 
 
 

Python

 
1
Series([NaN,'a']).equals(Series([NaN,'a']))

Out[45]:

 
 
 
 
 

Python

 
1
True

比numpy.testing.assert_equals更智能些, 三种情况下都能恰当的处理

在DataFrame merge中的表现

两边的None会被判为相同

In[46]:

 
 
 
 
 

Python

 
1
2
3
a=DataFrame({'A':[None,'a']})
b=DataFrame({'A':[None,'a']})
a.merge(b,on='A',how='outer')

Out[46]:

  A
0 None
1 a

两边的NaN会被判为相同

In[47]:

 
 
 
 
 

Python

 
1
2
3
a=DataFrame({'A':[NaN,'a']})
b=DataFrame({'A':[NaN,'a']})
a.merge(b,on='A',how='outer')

Out[47]:

  A
0 NaN
1 a

无论两边都是None,都是NaN,还是都有,相关的列都会被正确的匹配。 注意一边是None,一边是NaN的时候。会以左侧的结果为准。

In[48]:

 
 
 
 
 

Python

 
1
2
3
a=DataFrame({'A':[None,'a']})
b=DataFrame({'A':[NaN,'a']})
a.merge(b,on='A',how='outer')

Out[48]:

  A
0 None
1 a

In[49]:

 
 
 
 
 

Python

 
1
2
3
a=DataFrame({'A':[NaN,'a']})
b=DataFrame({'A':[None,'a']})
a.merge(b,on='A',how='outer')

Out[49]:

  A
0 NaN
1 a

注意

这和空值在postgresql等sql数据库中的表现不一样, 在数据库中, join时两边的空值会被判定为不同的数值

在groupby中的表现

In[50]:

 
 
 
 
 

Python

 
1
2
d=DataFrame({'A':[1,1,1,1,2],'B':[None,None,'a','a','b']})
d.groupby(['A','B']).apply(len)

Out[50]:

 
 
 
 
 

Python

 
1
2
3
4
A  B
1  a    2
2  b    1
dtype:int64

可以看到(1, NaN)对应的组直接被忽略了

In[51]:

 
 
 
 
 

Python

 
1
2
d=DataFrame({'A':[1,1,1,1,2],'B':[None,None,'a','a','b']})
d.groupby(['A','B']).apply(len)

Out[51]:

 
 
 
 
 

Python

 
1
2
3
4
A  B
1  a    2
2  b    1
dtype:int64

(1,None)的组也被直接忽略了

In[52]:

 
 
 
 
 

Python

 
1
2
d=DataFrame({'A':[1,1,1,1,2],'B':[None,NaN,'a','a','b']})
d.groupby(['A','B']).apply(len)

Out[52]:

 
 
 
 
 

Python

 
1
2
3
4
A  B
1  a    2
2  b    1
dtype:int64

那么上面这个结果应该没啥意外的

总结

DataFrame.groupby会忽略分组列中含有None或者NaN的记录

支持写入数据库?

往数据库中写入时NaN不可处理,需转换成None,否则会报错。这个这里就不演示了。

相信作为pandas老司机, 至少能想出两种替换方法。

In[53]:

 
 
 
 
 

Python

 
1
2
s=Series([None,NaN,'a'])
s

Out[53]:

 
 
 
 
 

Python

 
1
2
3
4
0    None
1     NaN
2       a
dtype:object

方案1

In[54]:

 
 
 
 
 

Python

 
1
s.replace([NaN],None)

Out[54]:

 
 
 
 
 

Python

 
1
2
3
4
0    None
1    None
2       a
dtype:object

方案2

In[55]:

 
 
 
 
 

Python

 
1
2
s[s.isnull()]=None
s

Out[55]:

 
 
 
 
 

Python

 
1
2
3
4
0    None
1    None
2       a
dtype:object

然而这么就觉得完事大吉的话就图样图森破了, 看下面的例子

In[56]:

 
 
 
 
 

Python

 
1
2
s=Series([NaN,1])
s

Out[56]:

 
 
 
 
 

Python

 
1
2
3
0    NaN
1    1.0
dtype:float64

In[57]:

 
 
 
 
 

Python

 
1
s.replace([NaN],None)

Out[57]:

 
 
 
 
 

Python

 
1
2
3
0    NaN
1    1.0
dtype:float64

In[58]:

 
 
 
 
 

Python

 
1
2
s[s.isnull()]=None
s

Out[58]:

 
 
 
 
 

Python

 
1
2
3
0    NaN
1    1.0
dtype:float64

当其他数据是int或float时,Series又一声不吭的自动把None替换成了NaN。

这时候可以使用第三种方法处理

In[59]:

 
 
 
 
 

Python

 
1
s.where(s.notnull(),None)

Out[59]:

 
 
 
 
 

Python

 
1
2
3
0    None
1       1
dtype:object

where语句会遍历s中所有的元素,逐一检查条件表达式, 如果成立, 从原来的s取元素; 否则用None填充。 这回没有自动替换成NaN

None vs NaN要点总结

  1. 在pandas中, 如果其他的数据都是数值类型, pandas会把None自动替换成NaN, 甚至能将s[s.isnull()]= None,和s.replace(NaN, None)操作的效果无效化。 这时需要用where函数才能进行替换。
  2. None能够直接被导入数据库作为空值处理, 包含NaN的数据导入时会报错。
  3. numpy和pandas的很多函数能处理NaN,但是如果遇到None就会报错。
  4. None和NaN都不能被pandas的groupby函数处理,包含None或者NaN的组都会被忽略。

等值性比较的总结:(True表示被判定为相等)

  None对None NaN对NaN None对NaN
单值 True False False
tuple(整体) True True False
np.array(逐个) True False False
Series(逐个) False False False
assert_equals True True False
Series.equals True True True
merge True True True

由于等值性比较方面,None和NaN在各场景下表现不太一致,相对来说None表现的更稳定。

为了不给自己惹不必要的麻烦和额外的记忆负担。 实践中,建议遵循以下三个原则即可

  • 在用pandas和numpy处理数据阶段将None,NaN统一处理成NaN,以便支持更多的函数。
  • 如果要判断Series,numpy.array整体的等值性,用专门的Series.equals,numpy.array函数去处理,不要自己用==判断 *
  • 如果要将数据导入数据库,将NaN替换成None

python None 和 NaN的更多相关文章

  1. python [吐槽]关于nan类型时遇到的问题

    今天在用写一段求和的代码时候,发现最后返回的是nan的结果,这段循环求和代码依次调用了三个函数,于是依次打印这三个函数的返回值,发现其中一个函数的返回值为nan,原来是因为这段函数里面没有相似的用户, ...

  2. Python 无穷大与NaN

    想创建或测试正无穷.负无穷或NaN(非数字) 的浮点数 Python 并没有特殊的语法来表示这些特殊的浮点值,但是可以使用float() 来创建它们.比如: >>> a = floa ...

  3. Python 数据清洗--处理Nan

    参考:http://blog.sina.com.cn/s/blog_13050351e0102xfis.html https://www.sogou.com/link?url=DOb0bgH2eKh1 ...

  4. R vs Python:构建data.frame、读取csv与统计描述

    一.Python 数据框就是典型的关系型数据库的数据存储形式,每一行是一条记录,每一列是一个属性,最终构成表格的形式,这是数据科学家必须熟悉的最典型的数据结构. 1.构建数据框 import pand ...

  5. Python 浮点数的冷知识

    本周的PyCoder's Weekly 上分享了一篇小文章,它里面提到的冷知识很有意思,我稍作补充,分享给大家. 它提到的部分问题,读者们可以先思考下: 若两个元组相等,即 a==b 且 a is b ...

  6. DoesNotExist at /account/

    DoesNotExist at /account/ User has no account. Request Method: GET Request URL: http://127.0.0.1:800 ...

  7. DoesNotExist at /admin/

    DoesNotExist at /admin/ User has no account. Request Method: GET Request URL: http://127.0.0.1:8000/ ...

  8. Pandas中数据的处理

    有两种丢失数据 ——None ——np.nan(NaN) None是python自带的,其类型为python object.因此,None不能参与到任何计算中 Object类型的运算比int类型的运算 ...

  9. pandas 之 数据清洗-缺失值

    Abstract During the course fo doing data analysis and modeling, a significant amount of time is spen ...

随机推荐

  1. Maven:Generating Project in Batch mode 卡住问题

    Maven命令执行到Generating Project in Batch mode 卡住,原因是网络带宽不足问题!需要下载一个约4.1M的archetype-catalog.xml文件. Maven ...

  2. Spring Bean引用例子

    在Spring,bean可以“访问”对方通过bean配置文件指定相同或不同的引用. 1. Bean在不同的XML文件 如果是在不同XML文件中的bean,可以用一个“ref”标签,“bean”属性引用 ...

  3. R类

    资源文件的使用分为在代码中使用和在其他资源文件中引用该资源文件.在我们编译一个Android应用时,Android会自动生成一个R类,在该类中根据不同的资源类型又生成了相应的内部类,该类包含了系统中使 ...

  4. 几个未公开的 DBCC 命令

    http://blog.csdn.net/CathySun118/article/category/538610 https://ask.hellobi.com/blog/lyhabc/1612 1. ...

  5. gdb -Mysql源代码级调试方法

    http://blog.csdn.net/hitzhang/article/details/5985474 gdb -q --batch --ex "set height 0" - ...

  6. WebService如何抛出干净的异常

    转载:http://www.cnblogs.com/ahdung/p/3953431.html 说明:[干净]指的是客户端在捕获WebService(下称WS)抛出的异常时,得到的ex.Message ...

  7. svn 冲突

    转载:http://blog.sina.com.cn/s/blog_65fd4c1e0100h2cg.html 1.  如何产生冲突 当开发人员A和开发人员B从版本库同时检出文档1.txt,而A和B同 ...

  8. EatCam Webcam Recorder Pro

    EatCam Webcam Recorder Pro Webcam Recorder records webcams to AVI, FLV, WMV files and watch them whe ...

  9. AE 打开工具箱工具的对话框

    The best approach to run a system tool—or any tool or model created, and saved in a toolbox from a b ...

  10. Shell编程入门(第二版)(中)

    注:test測试语句一般不单独使用,一般作为if语句的測试条件,如; if test -d file then .... fi test的变量的简写形式"[]" 演示样例-apac ...