为什么pandas使用numpy中的“NaN”,而不是它自己的null值?

xpcnnkqh  于 2023-09-29  发布在  其他
关注(0)|答案(3)|浏览(95)

这是一个比较宽泛的主题,但我会尝试将其缩减到一些具体的问题。
在开始回答关于SO的问题时,我发现自己在制作玩具数据时有时会遇到这样一个愚蠢的错误:

In[0]:

import pandas as pd

df = pd.DataFrame({"values":[1,2,3,4,5,6,7,8,9]})
df[df < 5] = np.nan

Out[0]:
NameError: name 'np' is not defined

我非常习惯用pandas自动导入numpy,这在真实的代码中通常不会发生。然而,这确实让我想知道为什么pandas没有自己的值/对象来表示空值。
我最近才意识到,对于类似的情况,你可以使用Python None

import pandas as pd

df = pd.DataFrame({"values":[1,2,3,4,5,6,7,8,9]})
df[df < 5] = None

它按预期工作并且不产生错误。但我觉得我看到的SO上的约定是使用np.nan,人们在讨论空值时通常会提到np.nan(这也许是为什么我没有意识到可以使用None,但也许这是我自己的特质)。
简单地看一下这个,我现在已经看到pandas自1.0.0以来确实有一个pandas.NA值,但是 * 我从来没有见过有人在帖子中使用它 *:

In[0]:

import pandas as pd
import numpy as np

df = pd.DataFrame({'values':np.random.rand(20,)})
df['above'] = df['values']
df['below'] = df['values']
df['above'][df['values']>0.7] = np.nan
df['below'][df['values']<0.3] = pd.NA

df['names'] = ['a','b','c','a','b','c','a','b','c','a']*2
df.loc[df['names']=='a','names'] = pd.NA
df.loc[df['names']=='b','names'] = np.nan
df.loc[df['names']=='c','names'] = None
df

Out[0]:
      values     above     below names
0   0.323531  0.323531  0.323531  <NA>
1   0.690383  0.690383  0.690383   NaN
2   0.692371  0.692371  0.692371  None
3   0.259712  0.259712       NaN  <NA>
4   0.473505  0.473505  0.473505   NaN
5   0.907751       NaN  0.907751  None
6   0.642596  0.642596  0.642596  <NA>
7   0.229420  0.229420       NaN   NaN
8   0.576324  0.576324  0.576324  None
9   0.823715       NaN  0.823715  <NA>
10  0.210176  0.210176       NaN  <NA>
11  0.629563  0.629563  0.629563   NaN
12  0.481969  0.481969  0.481969  None
13  0.400318  0.400318  0.400318  <NA>
14  0.582735  0.582735  0.582735   NaN
15  0.743162       NaN  0.743162  None
16  0.134903  0.134903       NaN  <NA>
17  0.386366  0.386366  0.386366   NaN
18  0.313160  0.313160  0.313160  None
19  0.695956  0.695956  0.695956  <NA>

因此,对于数值来说,这些不同的空值之间的区别似乎并不重要,但它们对于字符串(也许对于其他数据类型?)).

我的问题基于以上

  • 使用np.nan(而不是None)来表示pandas中的空值是否是常规的?
  • 为什么pandas在其生命周期的大部分时间里都没有自己的null值(直到去年)?添加的动机是什么?
  • 如果在一个Series或列中可以有多种类型的缺失值,它们之间有什么区别吗?为什么它们的表示方式不相同(如数字数据)?

我完全预料到我可能对事物以及pandasnumpy之间的区别有错误的解释,所以请纠正我。

tzxcd3kk

tzxcd3kk1#

pandas的一个主要依赖项是numpy,换句话说,pandas构建在numpy之上。由于pandas继承并使用了许多numpy方法,因此保持一致性是有意义的,即缺失的数值数据用np.NaN表示。
(This选择建立在numpy的基础上也会对其他事情产生影响。例如,date and time operations构建在np.timedelta64np.datetime64数据类型之上,而不是标准的datetime模块。
您可能不知道的一件事是,numpy一直与pandas一起存在

import pandas as pd
pd.np?
pd.np.nan

虽然您可能认为这种行为 * 可能 * 会更好,因为您没有导入numpy,但这是不鼓励的,并且在不久的将来将被弃用,以支持直接导入numpy
未来警告:pandas.np模块已被弃用,并将在未来版本中从pandas中删除。直接导入numpy

在pandas中使用np.nan(而不是None)来表示空值是否是一种常规做法?

如果数据是数字,那么是的,你应该使用np.NaNNone要求dtype为Object,而对于pandas,您希望数字数据存储在数字dtype中。pandas在创建或导入时通常会强制转换为正确的空类型,以便使用正确的dtype

pd.Series([1, None])
#0    1.0
#1    NaN        <- None became NaN so it can have dtype: float64
#dtype: float64

为什么pandas在其生命周期的大部分时间里都没有自己的null值(直到去年)?添加的动机是什么?

pandas没有自己的null值,因为它使用了np.NaN,这适用于大多数情况。然而,pandas很容易丢失数据,entire section of the documentation专门用于此。NaN是一个浮点数,不适合整数容器,这意味着任何缺少数据的数字系列都将被向上转换为float。这可能是become problematic because of floating point math,有些整数不能用浮点数完美地表示。因此,任何连接或merges都可能失败。

# Gets upcast to float
pd.Series([1,2,np.NaN])
#0    1.0
#1    2.0
#2    NaN
#dtype: float64

# Can safely do merges/joins/math because things are still Int
pd.Series([1,2,np.NaN]).astype('Int64')
#0       1
#1       2
#2    <NA>
#dtype: Int64
b4wnujal

b4wnujal2#

  • 首先,您可以通过只返回一个值的filter-function来统一nan值,例如None
  • 我猜原因是为了使它在对numpy计算等数据进行数据挖掘时具有独特性。所以,pandas nan的意思是不同的。也许,它在你的特殊情况下没有意义,但它在其他情况下会有意义。
eh57zj3b

eh57zj3b3#

问得好!我的直觉是,这与NumPy函数是在C中实现的这一事实有关,这使得它如此之快。Python的None可能不会给予你同样的效率(或者可能被翻译成np.nan),而Pandas的pd.NA可能会被翻译成NumPy的np.nan,因为Pandas需要NumPy。不过,还没有找到支持我说法的资源。

相关问题