【问题标题】:Why does pandas use "NaN" from numpy, instead of its own null value?为什么 pandas 使用 numpy 中的“NaN”,而不是它自己的 null 值?
【发布时间】:2020-10-10 20:02:19
【问题描述】:

这是一个有点宽泛的话题,但我会尽量减少一些具体的问题。

在开始回答关于 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>

所以对于数值来说,这些不同的空值之间的区别似乎并不重要,但是对于字符串(也许对于其他数据类型?),它们的表示方式不同。

我的问题基于上述

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

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

【问题讨论】:

  • import numpy as np 可以在整个pandas 代码中找到。他们不鼓励使用pd.np,但鼓励您自己进行导入。它不会再占用任何“内存”。
  • 如果列/系列是数字(整数),分配这些“NA”中的任何一个都会生成float 并插入np.nan。如果 object dtype(与 strings 列一样),则插入实际的 np.nanNonepd.NA
  • np.nan 是一个“IEEE 754 浮点”值,因此可以有效地用于数值运算(快速编译的整个数组 numpy 代码)。所以它可以通过任何别名使用,在数字 dtype Series 中很有意义。这不适用于 object dtype Series,因此可以在那里使用任何方便的对象。
  • @hpaulj 输入也不错,结合 ALollz 的回答很全面

标签: python pandas numpy null missing-data


【解决方案1】:

一个主要的dependency of pandas is numpy,换句话说,pandas 是建立在 numpy 之上的。因为 pandas 继承并使用了许多 numpy 方法,所以保持一致是有意义的,也就是说,缺失的数字数据用np.NaN 表示。

(基于 numpy 构建的这种选择也会对其他事物产生影响。例如,date and time operations 是基于 np.timedelta64np.datetime64 dtypes,而不是标准的 datetime 模块。)


您可能不知道的一件事是numpy 一直与pandas 在一起

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

虽然您可能认为这种行为可能会更好,因为您不导入 numpy,但不鼓励这样做,并且在不久的将来会弃用,而支持直接导入 numpy

FutureWarning:pandas.np 模块已弃用并将被删除 在未来版本中来自 pandas。改为直接导入numpy


在 pandas 中使用np.nan(而不是None)来表示空值是惯例吗?

如果数据是数字,那么是的,您应该使用np.NaNNone 要求 dtype 为 Object 并且对于 pandas,您希望将数字数据存储在数字 dtype 中。 pandas 通常会在创建或导入时强制转换为正确的 null 类型,以便它可以使用正确的 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

【讨论】:

  • 很好的答案,谢谢!我不知道pd.np,你是对的?
  • 关于“某些整数不能用浮点数完美表示”- 64 位浮点数可以表示精确到 2**53 (9e+15) 的整数,而 2**63 则为int64 (9e+18),因此在绝大多数情况下,您不会遇到问题。
【解决方案2】:
  • 首先,您可以通过只返回一个值的filter-function 统一nan 值,例如None
  • 我猜原因是为了在对来自numpy 计算等的数据进行数据挖掘时使其独一无二。所以,pandasnan 意味着不同的东西。也许,在您的特殊情况下它在这里没有意义,但在其他情况下它会有意义。

【讨论】:

  • 感谢您的意见!是的,你的第一点是对的,我相信他们都被.isna().isnull() 抓住了
  • 这可能是一种方法,但我会以不同的方式将过滤器定义为 filter1 = df[key==NaN] filter2 = df[key==None] filter3 = df[key=='&lt;Na&gt;'] 然后,您可以简单地使用 df.loc 并返回如上所述的唯一值。
【解决方案3】:

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

【讨论】:

    猜你喜欢
    • 2013-12-17
    • 2015-10-22
    • 1970-01-01
    • 1970-01-01
    • 2015-08-28
    • 2016-02-08
    • 1970-01-01
    • 2014-08-25
    • 2021-12-07
    相关资源
    最近更新 更多