博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
提取series中的数值_Python中None和numpy.nan的区别
阅读量:6501 次
发布时间:2019-06-24

本文共 6692 字,大约阅读时间需要 22 分钟。

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

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

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

首先,导入所需的库

from numpy import NaNfrom pandas import Series, DataFrameimport numpy as np

数据类型?

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

type(None)NoneTypetype(NaN)float

能作为dict的key?

{
None:1}{
None: 1}{
NaN:1}{
nan: 1}{
None:1, NaN:2}{
nan: 2, None: 1}

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

Series函数中的表现

Series.map

s = Series([None, NaN, 'a'])s0    None1     NaN2       adtype: objects.map({
None:1,'a':'a'})0 11 12 adtype: object

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

s.map({
NaN:1,'a':'a'})0 11 12 adtype: object

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

s.map({
NaN:2,'None':1,'a':'a'})0 21 22 adtype: object

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

s.map({
'None':1,NaN:2,'a':'a'})0 21 22 adtype: object

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

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

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

Series.replace中的表现

s = Series([None, NaN, 'a'])s0    None1     NaN2       adtype: objects.replace([NaN],9)0    91    92    adtype: objects.replace([None],9)0    91    92    adtype: object

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

对函数的支持

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

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

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

try:    np.nansum([1,2,None])except Exception as e:    print(type(e),e)
unsupported operand type(s) for +: 'int' and 'NoneType'

pandas中也有不少函数支持NaN却不支持None。(毕竟pandas的底层是numpy)

import pandas as pdpd.cut(Series([NaN]),[1,2])0    NaNdtype: categoryCategories (1, object): [(1, 2]]import pandas as pdtry:    pd.cut(Series([None]),[1,2])except Exception as e:    print(type(e),e)
unorderable types: int() > NoneType()

对容器数据类型的影响

混入numpy.array的影响

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

np.array([1, None]).dtypedtype('O')

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

np.array([1, NaN]).dtypedtype('float64')

混入Series的影响

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

Series([1, NaN])0    1.01    NaNdtype: float64

下面的这个就很意外的吧

Series([1, None])0    1.01    NaNdtype: float64

pandas将None自动替换成了NaN!

Series([1.0, None])0    1.01    NaNdtype: float64

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

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

Series(['a', None])0       a1    Nonedtype: object

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

Series([None,None])0    None1    Nonedtype: object

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

Series([True, False, None])0     True1    False2     Nonedtype: object

等值性判断

单值的等值性比较

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

None == NoneTrueNaN == NaNFalseNone == NaNFalse

在tuple中的情况

这个不奇怪

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

这个也不意外

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

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

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

在numpy.array中的情况

np.array([1,None]) == np.array([1,None])array([ True,  True], dtype=bool)np.array([1,NaN]) == np.array([1,NaN])array([ True, False], dtype=bool)np.array([1,NaN]) == np.array([1,None])array([ True, False], dtype=bool)

和基准的表现一致。 

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

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

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

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

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

try:    np.testing.assert_equal(np.array([1,NaN]), np.array([1,None]))except Exception as e:    print(type(e),e)
Arrays are not equal(mismatch 50.0%) x: array([ 1., nan]) y: array([1, None], dtype=object)

在Series中的情况

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

Series([NaN,'a']) == Series([NaN,'a'])0    False1     Truedtype: boolSeries([None,'a']) == Series([NaN,'a'])0    False1     Truedtype: bool

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

Series([None,'a']) == Series([None,'a'])0    False1     Truedtype: bool

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

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

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

在DataFrame merge中的表现

两边的None会被判为相同

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

A 0 None 1 a

两边的NaN会被判为相同

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

A 0 NaN 1 a

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

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

A 0 None 1 a

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

A 0 NaN 1 a

注意

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

在groupby中的表现

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

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

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

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

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

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

总结

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

支持写入数据库?

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

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

s = Series([None,NaN,'a'])s0    None1     NaN2       adtype: object

方案1

s.replace([NaN],None)0    None1    None2       adtype: object

方案2

s[s.isnull()]=Nones0    None1    None2       adtype: object

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

s = Series([NaN,1])s0    NaN1    1.0dtype: float64s.replace([NaN], None)0    NaN1    1.0dtype: float64s[s.isnull()] = Nones0    NaN1    1.0dtype: float64

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

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

s.where(s.notnull(), None)0    None1       1dtype: 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://pllyo.baihongyu.com/

你可能感兴趣的文章
JDBC_MySQL_jdbc连接mysql_MySQL
查看>>
mysql cte的好处_Mysql 8 重要新特性 - CTE 通用表表达式
查看>>
zcu106 固化_xilinx zcu106 vcu demo
查看>>
java ftpclient 代码_java后台代码ftpclient下载文件
查看>>
java数据库生成model_继承BaseModelGenerator 生成Model时添加数据库表字段 生成代码示例...
查看>>
java面向对象的概念_java面向对象(上)-- 面向对象的概念
查看>>
java内部类访问外部类变量 final_Java内部类引用外部类中的局部变量为什么必须是final问题解析...
查看>>
java 栈帧与类的关系_深入理解Java虚拟机之类运行时栈帧结构
查看>>
php中删除评论怎么做的,详解PHP如何实现评论回复删除功能
查看>>
macports 安装php,「macports」MacOS 中 MacPorts 安装和使用 - 金橙教程网
查看>>
php 审计 for linux,for linux是什么意思
查看>>
matlab里面连接器是什么,Oops - an error has occurred
查看>>
matlab建立桌面图标,在ubuntu16.04上创建matlab的快捷方式(实现方法)
查看>>
smarty使用php代码,笑谈配置,使用Smarty技术_php
查看>>
oracle数据实际值限制,c# – Oracle数据库TNS密钥“数据源”的值长度超过了’128’的限制...
查看>>
silk v3 decoder php,解码转换QQ微信的SILK v3编码音频为MP3或其他格式
查看>>
linux不能访问80端口,lunux开放80端口(本地访问不了linux文件可能是这个原因)...
查看>>
android单位转换小程序,微信小程序中rpx与rem单位转换
查看>>
ps切图教程 android,PS前端切图完整教程
查看>>
html显示服务器状态,显示服务器时间并一直显示(html代码)
查看>>