我很难调试一个问题,当在nan中使用浮点时,list中的浮点nan和numpy.array中的nan被不同地处理
给定以下列表和数组:
from itertools import groupby
import numpy as np
lst = [np.nan, np.nan, np.nan, 0.16, 1, 0.16, 0.9999, 0.0001, 0.16, 0.101, np.nan, 0.16]
arr = np.array(lst)当我遍历列表时,连续的nan被分组:
>>> for key, group in groupby(lst):
...     if np.isnan(key):
...         print(key, list(group), type(key))
nan [nan, nan, nan] <class 'float'>
nan [nan] <class 'float'>但是,如果我使用这个数组,它会将连续的nan放在不同的组中:
>>> for key, group in groupby(arr):
...     if np.isnan(key):
...         print(key, list(group), type(key))
nan [nan] <class 'numpy.float64'>
nan [nan] <class 'numpy.float64'>
nan [nan] <class 'numpy.float64'>
nan [nan] <class 'numpy.float64'>即使我将数组转换回列表:
>>> for key, group in groupby(arr.tolist()):
...     if np.isnan(key):
...         print(key, list(group), type(key))
nan [nan] <class 'float'>
nan [nan] <class 'float'>
nan [nan] <class 'float'>
nan [nan] <class 'float'>我在用:
numpy 1.11.3
python 3.5我知道通常情况下是nan != nan,所以为什么这些操作会给出不同的结果?groupby怎么可能对nan进行分组呢?
发布于 2017-01-18 16:06:33
Python列表只是指向内存中对象的指针数组。特别地,lst保存指向对象np.nan的指针。
>>> [id(x) for x in lst]
[139832272211880, # nan
 139832272211880, # nan
 139832272211880, # nan
 139832133974296,
 139832270325408,
 139832133974296,
 139832133974464,
 139832133974320,
 139832133974296,
 139832133974440,
 139832272211880, # nan
 139832133974296](我的电脑上的np.nan是139832272211880。)
另一方面,NumPy数组只是内存的连续区域;它们是由位和字节组成的区域,它们被解释为值的序列(浮点数、ints等等)。由NumPy.
问题是,当您要求Python迭代包含浮动值的NumPy数组(在for、-loop或groupby级别上)时,Python需要将这些字节放入适当的Python对象中。它在内存中为数组中的每个值在迭代时创建一个全新的Python对象。
例如,您可以看到每个nan值的不同对象是在调用.tolist()时创建的:
>>> [id(x) for x in arr.tolist()]
[4355054616, # nan
 4355054640, # nan
 4355054664, # nan
 4355054688,
 4355054712,
 4355054736,
 4355054760,
 4355054784,
 4355054808,
 4355054832,
 4355054856, # nan
 4355054880]itertools.groupby能够对np.nan列表进行分组,因为它在比较Python对象时首先检查身份。因为这些指向nan的指针都指向同一个np.nan对象,所以可以进行分组。
但是,NumPy数组上的迭代不允许这种初始身份检查成功,因此,正如您所说的,Python回到了检查等式和nan != nan。
发布于 2017-01-18 17:58:00
K和阿杰尔的答案是正确的,这是因为列表中的nans具有相同的id,而当它们在numpy数组中“迭代”时有不同的it。
这个答案是作为这些答案的补充。
>>> from itertools import groupby
>>> import numpy as np
>>> lst = [np.nan, np.nan, np.nan, 0.16, 1, 0.16, 0.9999, 0.0001, 0.16, 0.101, np.nan, 0.16]
>>> arr = np.array(lst)
>>> for key, group in groupby(lst):
...     if np.isnan(key):
...         print(key, id(key), [id(item) for item in group])
nan 1274500321192 [1274500321192, 1274500321192, 1274500321192]
nan 1274500321192 [1274500321192]
>>> for key, group in groupby(arr):
...     if np.isnan(key):
...         print(key, id(key), [id(item) for item in group])
nan 1274537130480 [1274537130480]
nan 1274537130504 [1274537130504]
nan 1274537130480 [1274537130480]
nan 1274537130480 [1274537130480]  # same id as before but these are not consecutive
>>> for key, group in groupby(arr.tolist()):
...     if np.isnan(key):
...         print(key, id(key), [id(item) for item in group])
nan 1274537130336 [1274537130336]
nan 1274537130408 [1274537130408]
nan 1274500320904 [1274500320904]
nan 1274537130168 [1274537130168]问题是Python在比较值时使用PyObject_RichCompare-operation,如果==由于没有实现而失败,只能测试对象标识。另一方面,itertools.groupby使用PyObject_RichCompareBool (请参阅来源:1,2),它首先测试对象标识,然后再对进行测试。
这可以用一个小的cython片段来验证:
%load_ext cython
%%cython
from cpython.object cimport PyObject_RichCompareBool, PyObject_RichCompare, Py_EQ
def compare(a, b):
    return PyObject_RichCompare(a, b, Py_EQ), PyObject_RichCompareBool(a, b, Py_EQ)
>>> compare(np.nan, np.nan)
(False, True)PyObject_RichCompareBool的源代码如下所示:
/* Perform a rich comparison with object result.  This wraps do_richcompare()
   with a check for NULL arguments and a recursion check. */
/* Perform a rich comparison with integer result.  This wraps
   PyObject_RichCompare(), returning -1 for error, 0 for false, 1 for true. */
int
PyObject_RichCompareBool(PyObject *v, PyObject *w, int op)
{
    PyObject *res;
    int ok;
    /* Quick result when objects are the same.
       Guarantees that identity implies equality. */
    /**********************That's the difference!****************/
    if (v == w) {
        if (op == Py_EQ)
            return 1;
        else if (op == Py_NE)
            return 0;
    }
    res = PyObject_RichCompare(v, w, op);
    if (res == NULL)
        return -1;
    if (PyBool_Check(res))
        ok = (res == Py_True);
    else
        ok = PyObject_IsTrue(res);
    Py_DECREF(res);
    return ok;
}对象标识测试(if (v == w) )确实是在使用普通python比较PyObject_RichCompare(v, w, op);并在其文件中提及之前进行的。
注意: 如果o1和o2是同一个对象,
PyObject_RichCompareBool()总是返回1表示Py_EQ,0返回Py_NE。
发布于 2017-01-18 16:08:17
我不确定这是否是原因,但我只是注意到了nan in lst和arr
>>> lst[0] == lst[1], arr[0] == arr[1]
(False, False)
>>> lst[0] is lst[1], arr[0] is arr[1]
(True, False)也就是说,虽然所有nan都是不相等的,但常规np.nan (类型为float)都是相同的实例,而arr中的nan是numpy.float64类型的不同实例)。因此,我的猜测是,如果没有给出key函数,groupby将在进行更昂贵的等式检查之前测试标识。
这也与arr.tolist()中不分组的观察结果相一致,因为即使这些nan现在又是float,但它们不再是同一个实例。
>>> atl = arr.tolist()
>>> atl[0] is atl[1]
Falsehttps://stackoverflow.com/questions/41723419
复制相似问题