我正在使用不同数据类型的numpy数组。我想知道,在任何特定数组中,哪些元素是NaN。通常,这是np.isnan
的用途。
但是,np.isnan
对数据类型object
(或任何字符串数据类型)的数组不友好:
>>> str_arr = np.array(["A", "B", "C"])
>>> np.isnan(str_arr)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: Not implemented for this type
>>> obj_arr = np.array([1, 2, "A"], dtype=object)
>>> np.isnan(obj_arr)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: ufunc 'isnan' not supported for the input types, and the inputs could not be safely coerced to any supported types according to the casting rule ''safe''
我想从这两个电话中得到的只是np.array([False, False, False])
。我无法在try
的调用周围放置except TypeError
和np.isnan
,并假设任何生成TypeError
的数组都不包含NaN:毕竟,我和#39;我希望np.isnan(np.array([1, np.NaN, "A"]))
返回np.array([False, True, False])
。
我目前的解决方案是创建一个类型为np.float64
的新数组,循环遍历原始数组的元素,try
将该元素放入新数组中(如果失败,将其保留为零)然后在新阵列上调用np.isnan
。然而,这当然是相当缓慢的。 (至少对于大型对象数组。)
def isnan(arr):
if isinstance(arr, np.ndarray) and (arr.dtype == object):
# Create a new array of dtype float64, fill it with the same values as the input array (where possible), and
# then call np.isnan on the new array. This way, np.isnan is only called once. (Much faster than calling it on
# every element in the input array.)
new_arr = np.zeros((len(arr),), dtype=np.float64)
for idx in xrange(len(arr)):
try:
new_arr[idx] = arr[idx]
except Exception:
pass
return np.isnan(new_arr)
else:
try:
return np.isnan(arr)
except TypeError:
return False
这个特殊的实现也只适用于一维数组,我不能想出一个让for
循环在任意数量的维度上运行的好方法。
有没有更有效的方法来确定object
- 类型数组中的哪些元素是NaN?
编辑: 我正在运行Python 2.7.10。
请注意,[x is np.nan for x in np.array([np.nan])]
会返回False
:np.nan
并非总是与内容中的对象np.nan
不同。
我不希望字符串 "nan"
被视为等同于np.nan
:我希望isnan(np.array(["nan"], dtype=object))
返回np.array([False])
。
多维度不是一个大问题。 (一点ravel
- 和 - reshape
没有解决的问题。:p)
依赖于is
运算符来测试两个NaN的等价性的任何函数都不会始终有效。 (如果您认为他们应该,请问自己is
运营商实际上做了什么!)
答案 0 :(得分:4)
你可以使用list comp来获取任何nan的索引,在这种情况下可能更快:
obj_arr = np.array([1, 2, np.nan, "A"], dtype=object)
inds = [i for i,n in enumerate(obj_arr) if str(n) == "nan"]
或者如果你想要一个布尔掩码:
mask = [True if str(n) == "nan" else False for n in obj_arr]
使用is np.nan
似乎也无需转换为str:
In [29]: obj_arr = np.array([1, 2, np.nan, "A"], dtype=object)
In [30]: [x is np.nan for x in obj_arr]
Out[30]: [False, False, True, False]
对于平面和多维数组,您可以检查形状:
def masks(a):
if len(a.shape) > 1:
return [[x is np.nan for x in sub] for sub in a]
return [x is np.nan for x in a]
如果np.nan失败可能会检查类型然后我们np.isnan
def masks(a):
if len(a.shape) > 1:
return [[isinstance(x, float) and np.isnan(x) for x in sub] for sub in arr]
return [isinstance(x, float) and np.isnan(x) for x in arr]
有趣的是,当数据类型为 object 时,x is np.nan
似乎工作正常:
In [76]: arr = np.array([np.nan,np.nan,"3"],dtype=object)
In [77]: [x is np.nan for x in arr]
Out[77]: [True, True, False]
In [78]: arr = np.array([np.nan,np.nan,"3"])
In [79]: [x is np.nan for x in arr]
Out[79]: [False, False, False]
取决于dtype,会发生不同的事情:
In [90]: arr = np.array([np.nan,np.nan,"3"])
In [91]: arr.dtype
Out[91]: dtype('S32')
In [92]: arr
Out[92]:
array(['nan', 'nan', '3'],
dtype='|S32')
In [93]: [x == "nan" for x in arr]
Out[93]: [True, True, False]
In [94]: arr = np.array([np.nan,np.nan,"3"],dtype=object)
In [95]: arr.dtype
Out[95]: dtype('O')
In [96]: arr
Out[96]: array([nan, nan, '3'], dtype=object)
In [97]: [x == "nan" for x in arr]
Out[97]: [False, False, False]
显然,当你的数组中有字符串时,nan会被强制转换为numpy.string_'s
,因此x == "nan"
在这种情况下有效,当你传递对象时,类型是float,所以如果你总是使用object dtype那么行为应该是一致的。
答案 1 :(得分:4)
如果您愿意使用pandas库,那么涵盖此案例的便捷功能是pd.isnull:
pandas.isnull(obj)[source]
检测缺失值(数值数组中的NaN,对象数组中的无/ NaN)
以下是一个例子:
$ python
>>> import numpy
>>> import pandas
>>> array = numpy.asarray(['a', float('nan')], dtype=object)
>>> pandas.isnull(array)
array([False, True])
答案 2 :(得分:1)
定义几个小而大的测试数组
In [21]: x=np.array([1,23.3, np.nan, 'str'],dtype=object)
In [22]: xb=np.tile(x,300)
你的职能:
In [23]: isnan(x)
Out[23]: array([False, False, True, False], dtype=bool)
直接列表解析,返回一个数组
In [24]: np.array([i is np.nan for i in x])
Out[24]: array([False, False, True, False], dtype=bool)
np.frompyfunc
具有与np.vectorize
类似的矢量化能力,但由于某种原因未得到充分利用(并且根据我的经验更快)
In [25]: def myisnan(x):
return x is np.nan
In [26]: visnan=np.frompyfunc(myisnan,1,1)
In [27]: visnan(x)
Out[27]: array([False, False, True, False], dtype=object)
由于它返回dtype对象,我们可能想要转换它的值:
In [28]: visnan(x).astype(bool)
Out[28]: array([False, False, True, False], dtype=bool)
它可以很好地处理多维数组:
In [29]: visnan(x.reshape(2,2)).astype(bool)
Out[29]:
array([[False, False],
[ True, False]], dtype=bool)
现在有一些时间:
In [30]: timeit isnan(xb)
1000 loops, best of 3: 1.03 ms per loop
In [31]: timeit np.array([i is np.nan for i in xb])
1000 loops, best of 3: 393 us per loop
In [32]: timeit visnan(xb).astype(bool)
1000 loops, best of 3: 382 us per loop
i is np.nan
测试的重点 - 它仅适用于标量。如果数组是dtype对象,则迭代会生成标量。但是对于dtype float数组,我们得到numpy.float64
的值。对于那些np.isnan(i)
是正确的测试。
In [61]: [(i is np.nan) for i in np.array([np.nan,np.nan,1.3])]
Out[61]: [False, False, False]
In [62]: [np.isnan(i) for i in np.array([np.nan,np.nan,1.3])]
Out[62]: [True, True, False]
In [63]: [(i is np.nan) for i in np.array([np.nan,np.nan,1.3], dtype=object)]
Out[63]: [True, True, False]
In [64]: [np.isnan(i) for i in np.array([np.nan,np.nan,1.3], dtype=object)]
...
TypeError: Not implemented for this type
答案 3 :(得分:0)
我会使用np.vectorize
和一个自定义函数来测试nan elementwise。
所以,
def _isnan(x):
if isinstance(x, type(np.nan)):
return np.isnan(x)
else:
return False
my_isnan = np.vectorize(_isnan)
然后
X = np.array([[1, 2, np.nan, "A"], [np.nan, True, [], ""]], dtype=object)
my_isnan(X)
返回
array([[False, False, True, False],
[ True, False, False, False]], dtype=bool)
答案 4 :(得分:0)
这就是我最终为自己建立的:
FLOAT_TYPES = (float, np.float64, np.float32, np.complex, np.complex64, np.complex128)
def isnan(arr):
"""Equivalent of np.isnan, except made to also be friendly towards arrays of object/string dtype."""
if isinstance(arr, np.ndarray):
if arr.dtype == object:
# An element can only be NaN if it's a float, and is not equal to itself. (NaN != NaN, by definition.)
# NaN is the only float that doesn't equal itself, so "(x != x) and isinstance(x, float)" tests for NaN-ity.
# Numpy's == checks identity for object arrays, so "x != x" will always return False, so can't vectorize.
is_nan = np.array([((x != x) and isinstance(x, FLOAT_TYPES)) for x in arr.ravel()], dtype=bool)
return is_nan.reshape(arr.shape)
if arr.dtype.kind in "fc": # Only [f]loats and [c]omplex numbers can be NaN
return np.isnan(arr)
return np.zeros(arr.shape, dtype=bool)
if isinstance(arr, FLOAT_TYPES):
return np.isnan(arr)
return False
答案 5 :(得分:-1)
在不转换为字符串或离开Numpy环境(也是非常重要的IMO)的情况下执行此操作的方法是使用np.nan的等式定义,其中
In[1]: x=np.nan
In[2]: x==x
Out[2]: False
仅在x == np.nan的情况下才是这样。因此,对于Numpy数组,
的元素检查x!=x
为True
x==np.nan