我刚开始使用Numpy,并注意到对Numpy数组中的每个元素进行迭代的速度比相同的要慢4倍,但是要列出列表。我现在知道这违背了Numpy的目的,如果可能,我应该对函数进行向量化。我的问题是,为什么它要慢4倍。这似乎是一个很大的数目。
我使用以下方法进行了测试 %timeit
import numpy as np
b = np.eye(1000)
a = b.tolist()
%timeit b[100][100] #1000000 loops, best of 3: 692 ns per loop
%timeit a[100][100] #10000000 loops, best of 3: 70.7 ns per loop
%timeit b[100,100] #1000000 loops, best of 3: 343 ns per loop
%timeit b.item(100,100) #1000000 loops, best of 3: 297 ns per loop
我试图用来dis.dis
查看引擎盖下发生了什么,但是得到了:
TypeError: don't know how to disassemble method-wrapper objects
然后,我尝试查看Numpy源代码,但找不到对应于数组元素访问的文件。我很好奇是什么导致了额外的开销,更重要的是,将来如何自己解决这个问题。看来python不能轻易编译为C代码,这样我才能看到其中的区别。但是,是否有办法查看每行生成的字节码,以了解差异?
总而言之:从NumPy数组中获取项目需要创建新的Python对象,而列表并非如此。同样,对于NumPy数组,索引比列表要稍微复杂一些,这可能会增加一些额外的开销。
回顾一下,您列出的NumPy操作执行以下操作:
b[100][100]
返回b
数组的第100行,然后获取该行的索引100处的值,并将该值作为对象(例如np.int64
类型)返回。b[100,100]
直接 在第100行和第100列返回值(首先不返回中间数组)。b.item(100,100)``b[100,100]
除了将值转换为原生Python类型并返回外,其他操作与上述操作相同。现在,这些操作中,(1)最慢,因为它需要两个连续的NumPy索引操作(我将在下面解释为什么它比列表索引慢)。(2)最快,因为仅执行了一个索引操作。操作(3)可能较慢,因为它是方法调用(在Python中通常较慢)。
为什么 列表 访问仍然比更快b[100,100]
?
Python列表是指向内存中对象的指针的数组。例如,列表 [1, 2, 3]
不直接包含那些整数,而是在每个整数对象已经存在的情况下指向内存地址的指针。为了从列表中获得一项,Python只是返回对该对象的引用。
NumPy数组不是对象的集合。该数组np.array([1, 2, 3])
只是一个连续的内存块,其位设置为代表这些整数值。要从该数组获取整数,必须在与该数组分开的内存中构造一个新的Python对象。例如,np.int64
索引操作可能会返回一个对象:该对象先前不存在,必须创建。
a[100][100]
(从列表中获取)比b[100,100]
(从数组中获取)更快的另外两个原因是:
BINARY_SUBSCR
索引列表和数组时都执行字节码操作码,但是针对Python列表进行了优化。
处理Python列表的整数索引的内部C函数非常简短。另一方面,NumPy索引要复杂得多,需要执行大量代码才能确定所使用的索引类型,以便可以返回正确的值。
下面将详细介绍使用a[100][100]
和访问列表和数组中元素的步骤b[100,100]
。
列表和数组都触发了相同的四个字节码操作码:
0 LOAD_NAME 0 (a) # the list or array
3 LOAD_CONST 0 (100) # index number (tuple for b[100,100])
6 BINARY_SUBSCR # find correct "getitem" function
7 RETURN_VALUE # value returned from list or array
注意:例如a[100][100][100]
,如果您开始对多维列表进行链式索引,则开始重复这些字节码指令。使用元组索引的NumPy数组不会发生这种情况:b[100,100,100]
仅使用四个指令。这就是为什么随着尺寸数量的增加,时序上的差距开始缩小的原因。
访问列表和数组的功能是不同的,在每种情况下都需要找到正确的函数。此任务由BINARY_SUBSCR
操作码处理:
w = POP(); // our index
v = TOP(); // our list or NumPy array
if (PyList_CheckExact(v) && PyInt_CheckExact(w)) { // do we have a list and an int?
/* INLINE: list[int] */
Py_ssize_t i = PyInt_AsSsize_t(w);
if (i < 0)
i += PyList_GET_SIZE(v);
if (i >= 0 && i < PyList_GET_SIZE(v)) {
x = PyList_GET_ITEM(v, i); // call "getitem" for lists
Py_INCREF(x);
}
else
goto slow_get;
}
else
slow_get:
x = PyObject_GetItem(v, w); // else, call another function
// to work out what is needed
Py_DECREF(v);
Py_DECREF(w);
SET_TOP(x);
if (x != NULL) continue;
break;
此代码针对Python列表进行了优化。如果该函数看到列表,它将快速调用该函数PyList_GET_ITEM
。现在可以在所需的索引处访问此列表(请参阅下面的下一部分)。
但是,如果没有看到列表(例如,我们有一个NumPy数组),它将采用“
slow_get”路径。这又调用另一个函数PyObject_GetItem
来检查对象映射到哪个“
getitem”函数:
PyObject_GetItem(PyObject *o, PyObject *key)
{
PyMappingMethods *m;
if (o == NULL || key == NULL)
return null_error();
m = o->ob_type->tp_as_mapping;
if (m && m->mp_subscript)
return m->mp_subscript(o, key);
...
在NumPy的阵列的情况下,正确的功能位于mp_subscript
在PyMappingMethods
结构。
请注意,在可以调用此正确的“ get”函数之前,还要进行其他函数调用。这些调用会增加的开销b[100]
,尽管开销将取决于Python /
NumPy的编译方式,系统架构等。
在上面可以看到该函数PyList_GET_ITEM
已被调用。这是一个简短的函数,基本上看起来像这样*:
PyList_GetItem(PyObject *op, Py_ssize_t i)
{
if (!PyList_Check(op)) { // check if list
PyErr_BadInternalCall();
return NULL;
}
if (i < 0 || i >= Py_SIZE(op)) { // check i is in range
if (indexerr == NULL) {
indexerr = PyUnicode_FromString(
"list index out of range");
if (indexerr == NULL)
return NULL;
}
PyErr_SetObject(PyExc_IndexError, indexerr);
return NULL;
}
return ((PyListObject *)op) -> ob_item[i]; // return reference to object
}
*PyList_GET_ITEM
实际上是此函数的宏形式,它执行相同的操作(减去错误检查)。
这意味着使项目在i
Python列表的索引处相对简单。在内部,Python检查所包含的项目的类型是否i
为列表,是否在列表的正确范围内,然后将对引用的引用返回给列表中的对象。
相反,NumPy必须做更多的工作才能返回所请求索引的值。
可以用各种不同的方式对数组建立索引,而NumPy必须决定需要哪个索引例程。各种索引例程主要由中的代码处理mapping.c
。
用于索引NumPy数组的所有内容都将通过该函数prepare_index
,该函数开始解析索引并存储有关广播,维数等的信息。这是该函数的呼叫签名:
NPY_NO_EXPORT int
prepare_index(PyArrayObject *self, PyObject *index,
npy_index_info *indices,
int *num, int *ndim, int *out_fancy_ndim, int allow_boolean)
/* @param the array being indexed
* @param the index object
* @param index info struct being filled (size of NPY_MAXDIMS * 2 + 1)
* @param number of indices found
* @param dimension of the indexing result
* @param dimension of the fancy/advanced indices part
* @param whether to allow the boolean special case
*/
该功能必须进行很多检查。即使对于诸如的相对简单的索引b[100,100]
,也必须推断出很多信息,以便NumPy可以将引用(视图)返回到正确的值。
总之,找到NumPy的“ getitem”函数所花费的时间更长,并且处理数组索引的函数必定比Python列表的单个函数更复杂。
我有一个下拉列表,我使用它来允许用户建立到后端的查询参数。 然后,用户将单击一个按钮,我将访问用户在下拉列表中设置的值,以调用后端endpoint。 任何帮助或提示都是非常欢迎的。
问题内容: 我有一个带有包含列表对象的列的Pandas DataFrame 如何访问每个列表的第一个元素并将其保存到DataFrame的新列中?要获得这样的结果: 我知道这可以通过遍历每一行来完成,但是有什么“ pythonic”方法吗? 问题答案: 您可以使用和功能
我有一个问题: 我有一个列表Java,我填充了不同的值。例如,我有: 我也有其他价值观。现在,我想在这个列表中只搜索第一个字段。例如,我想要A的indexOf。我尝试过写这段代码: 但我得到-1作为回报。我想知道在加载数组时如何访问列表中的字段。
如果你有一个列表=1,3,5,7,9,... 如果你试图找到列表中的前一个元素(即[t-1],第三个元素是3,即5之前的那个元素),那么这个符号与得到比该元素少一个l(即5-1=4)有什么不同 我有一个增长率的列表,我想要昨天的数据,但我得到的数据总是比今天的少 我认为最后一行的x-1才是问题所在。有什么想法吗?谢谢
问题内容: 我是AngularJS的新手,怀疑我没有掌握一个概念。我也在使用Twitter Bootstrap,并且加载了jQuery。 工作流程:用户单击列表中的链接,“主”部分被更新,并且链接用户单击了活动类。 基本HTML标记: 在jQuery中执行此操作: 但是我无法弄清楚如何集成Angular和jQuery以完成此操作,因为我正在使用Angular从服务器获取主列表(以JSON形式)并更
问题内容: 我需要从给定列表中选择一些元素,知道它们的索引。假设我要创建一个新列表,该列表包含给定列表[-2、1、5、3、8、5、6]中索引为1、2、5的元素。我所做的是: 有什么更好的方法吗?像c = a [b]? 问题答案: 您可以使用: 或者您可以使用numpy: 但实际上,您当前的解决方案很好。这可能是所有人中最整洁的。