- c - 在位数组中找到第一个零
- linux - Unix 显示有关匹配两种模式之一的文件的信息
- 正则表达式替换多个文件
- linux - 隐藏来自 xtrace 的命令
我正在尝试执行以下操作
from numpy import *
x = array([[3,2,3],[711,4,104],.........,[4,4,782,7845]]) # large nparray
for item in x:
set(item)
x = array([[3,2,3],[711,4,104],.........,[4,4,782,7845]]) # large nparray
for item in x:
item.tolist()
set
需要更长的时间比到
list
?
O(n)
?
最佳答案
TL;DR:set()
函数使用 Python 迭代协议(protocol)创建一个集合。但是在 NumPy 数组上迭代(在 Python 级别)太慢以至于使用 tolist()
在进行迭代之前将数组转换为 Python 列表会(快得多)。
要理解为什么迭代 NumPy 数组如此缓慢,重要的是要知道 Python 对象、Python 列表和 NumPy 数组是如何存储在内存中的。
Python 对象需要一些簿记属性(如引用计数、指向其类的链接等)以及它所代表的值。例如整数 ten = 10
看起来像这样:
蓝色圆圈是您在 Python 解释器中为变量 ten
使用的“名称”。而较低的对象(实例)实际上代表整数(因为簿记属性在这里并不重要,我在图像中忽略了它们)。
一个 Python list
只是 Python 对象的集合,例如 mylist = [1, 2, 3]
会像这样保存:
这次列表引用了 Python 整数 1
, 2
和 3
和名称 mylist
仅引用 list
实例。
但是一个数组 myarray = np.array([1, 2, 3])
不将 Python 对象存储为元素:
值 1
, 2
和 3
直接存储在 NumPy array
实例。
有了这些信息,我就可以解释为什么要迭代 array
与 list
上的迭代相比要慢得多:
每次访问 list
中的下一个元素时list
只返回一个存储的对象。这非常快,因为该元素已经作为 Python 对象存在(它只需要将引用计数加一)。
另一方面,当您想要 array
的元素时在返回之前,它需要为包含所有簿记内容的值创建一个新的 Python“框”。当您遍历数组时,它需要为数组中的每个元素创建一个 Python 框:
创建这些框很慢,主要原因是迭代 NumPy 数组比迭代存储值的 Python 集合(列表/元组/集/字典)慢得多 和他们的盒子 :
import numpy as np
arr = np.arange(100000)
lst = list(range(100000))
def iterateover(obj):
for item in obj:
pass
%timeit iterateover(arr)
# 20.2 ms ± 155 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit iterateover(lst)
# 3.96 ms ± 26.6 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
set
“构造函数”只是对对象进行迭代。
tolist
方法要快得多。最后,生成的 Python 列表中的每个值都需要位于“Python 框”中,因此
tolist
没有太多工作要做可以避免。但我确定的一件事是
list(array)
比
array.tolist()
慢:
arr = np.arange(100000)
%timeit list(arr)
# 20 ms ± 114 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit arr.tolist()
# 10.3 ms ± 253 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
O(n)
运行时复杂性,但常数因素非常不同。
set()
至
tolist()
- 这不是一个特别好的比较。比较
set(arr)
会更有意义至
list(arr)
或
set(arr.tolist())
至
arr.tolist()
:
arr = np.random.randint(0, 1000, (10000, 3))
def tosets(arr):
for line in arr:
set(line)
def tolists(arr):
for line in arr:
list(line)
def tolists_method(arr):
for line in arr:
line.tolist()
def tosets_intermediatelist(arr):
for line in arr:
set(line.tolist())
%timeit tosets(arr)
# 72.2 ms ± 2.68 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit tolists(arr)
# 80.5 ms ± 2.18 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit tolists_method(arr)
# 16.3 ms ± 140 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
%timeit tosets_intermediatelist(arr)
# 38.5 ms ± 200 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
set
你最好使用
set(arr.tolist())
.对于更大的数组,使用
np.unique
可能是有意义的。但是因为您的行只包含 3 个可能会变慢的项目(对于数千个元素,它可能会快得多!)。
set
s 但因为它们是输入的,所以它们很可能也避免了“Python 框”并将值直接存储在
set
中。 :
list
更复杂s 因为它们涉及
hash
es 和空槽(Python 对集合使用开放寻址,所以我假设 numba 也会)。
array
numba
set
直接保存值。所以当你转换一个 NumPy
array
到 NumPy
set
(或反之亦然)它根本不需要使用“Python 盒子”,所以当你创建
set
时s in a numba
nopython 功能它甚至会比
set(arr.tolist())
快得多手术:
import numba as nb
@nb.njit
def tosets_numba(arr):
for lineno in range(arr.shape[0]):
set(arr[lineno])
tosets_numba(arr) # warmup
%timeit tosets_numba(arr)
# 6.55 ms ± 105 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
set(arr.tolist())
快五倍方法。但重要的是要强调我没有返回
set
s 从函数。当您
返回
set
从 nopython numba 函数到 Python Numba 创建了一个 python 集合——包括为集合中的所有值“创建盒子”(这是 numba 隐藏的东西)。
list
,同样的装箱/拆箱会发生s 到 Numba nopython 函数或从这些函数返回列表。那么什么是
O(1)
Python 中的操作是
O(n)
与 Numba 一起操作!这就是为什么通常最好将 NumPy 数组传递给 numba nopython 函数(即
O(1)
)。
from numpy import *
应该避免,当你这样做时,你隐藏了几个 python 内置函数(
sum
,
min
,
max
,...),它把很多东西放到你的全局变量中。更好用
import numpy as np
.
np.
在函数调用前面使代码更清晰,不需要输入太多。
关于python - 将 NumPy 数组转换为集合花费的时间太长,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44224696/
我一直在读一本分配给类(class)的书,它提到数组访问需要 O(1) 时间。我意识到这非常快(也许尽可能快),但是如果您有一个循环必须多次引用它,那么分配一个临时变量以在数组中查找值有什么好处吗?或
我一直试图找出为什么这个查询花了这么长时间。以前,它的执行时间约为 150 毫秒到 200 毫秒,但现在需要 25 秒或更长时间。这是从昨晚到今天之间的事。唯一改变的就是将数据添加到表中。 根据下面的
我有一个 ng repeat 重复数据。 - data.image(src)部分为null,src=null的不再重复。 我用一个简单的 ng-if 解决了它。
我有一个包含大量测试的 Laravel 项目。我正在使用 pcov 来计算代码覆盖率,大约需要 4 分钟。但是 pcov 不支持分支覆盖,所以我决定使用 xdebug。 使用 xdebug 测试执行,
我已经被这个问题困扰了一段时间了,我被难住了。 Automapper 需要 4 秒来映射 19 个对象。在我的机器(24GB 内存,3.6Ghz i7)上,该操作应该花费毫秒或纳秒。 这是映射调用。
我有一个包含大量测试的 Laravel 项目。我正在使用 pcov 来计算代码覆盖率,大约需要 4 分钟。但是 pcov 不支持分支覆盖,所以我决定使用 xdebug。 使用 xdebug 测试执行,
我在机器 A 上有一个 java 进程通过 TCP 与机器 B 上的 Tomcat 通信。 TCP 连接(只是 syn-syn/ack 交换)始终需要 100 毫秒的数量级,而 ping 请求需要 1
我做了一项任务,从 sqlserver 获取超过 200 万条记录并将它们填充到 Asp.net GridView 中。 问题是,查询需要超过 2 分钟才能获得记录,而我的查询现在已经完全优化。 当我
我希望将 165 秒变成 2:40 而不是 0:2:45 函数需要能够适应秒值的大小。 我知道有无数种方法可以做到这一点,但我正在寻找一种干净的方法来做到这一点,除了 jQuery 之外没有任何外部库
我是一名优秀的程序员,十分优秀!