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. [前端]使用JQuery UI Layout Plug-in布局

    引言 使用JQuery UI Layout Plug-in布局框架实现快速布局,用起来还是挺方便的,稍微研究了一下,就能上手,关于该布局框架的材料,网上也挺多的.在项目中也使用到了,不过那是前端的工作 ...

  2. 【资料】wod烟草

    注意: 1. 所有效果持续时间是整个地城 2. 某几样菸草在使用 烟雾的祝福 的时候效果只有LV1 (技能 -25), 表示该物品设计上主要是自己使用而非加给团队. SL = 技能等级 HL = 英雄 ...

  3. Unity接入谷歌支付

    文章理由 前段时间负责Unity接入Google内购功能,一开始研究别人的技术博客时发现,他们的文章都有些年头了,有些细节的地方已经不像n年前那样了,技术永远是需要更新的,而这篇就作为2016年末的最 ...

  4. 使用 pm2-web 监控 pm2 服务运行状态

    pm2-web 是一款 pm2 服务状态监控程序,基于 web . 安装 $ npm install -g pm2-web 运行(默认是在8080端口) $ pm2-web 配置 pm2-web 将会 ...

  5. C++ Tr1中的正則表達式

    要使用正則表達式,首先要有类库支持,C++曾经不像Java或者C#有完整的类库使用,可是在Tr1中早已提供了正则库,仅仅是非常少被人们注意罢了 TR1中包括了一个正则库,来自Boost的 regex, ...

  6. [Linux] Ubuntu下的文件比较工具--meld

    在ubuntu中需要比较文件的差异,于是安装meld apt-get install meld 安装完后,在/usr/bin/下找到meld,然后发送到桌面上, 或者在命令行执行meld命令 打开后选 ...

  7. [Android Pro] android root权限破解分析

    许 多机友新购来的Android机器没有破解过Root权限,无法使用一些需要高权限的软件,以及进行一些高权限的操作,其实破解手机Root权限是比较简 单及安全的,破解Root权限的原理就是在手机的/s ...

  8. 4)linux程序设计入门--时间概念

    )程序设计入门--时间概念 前言:Linux下的时间概念 这一章我们学习Linux的时间表示和计算函数 时间的表示 时间的测量 计时器的使用 .时间表示 在程序当中,我们经常要输出系统当前的时间,比如 ...

  9. 谈谈Copy-on-Write容器

    1.简介 Copy-On-Write简称COW,是一种用于程序设计中的优化策略.其基本思路是,从一开始大家都在共享同一个内容,当某个人想要修改这个内容的时候,才会真正把内容Copy出去形成一个新的内容 ...

  10. Flume目录

    1. Flume的安装配置 2. flume和kafka整合(转) 3. Flume NG 配置详解(转) 4. Flume-NG一些注意事项(转) 5. FLume监控文件夹,将数据发送给Kafka ...