快速 numpy 花式索引

     2023-02-23     161

关键词:

【中文标题】快速 numpy 花式索引【英文标题】:Fast numpy fancy indexing 【发布时间】:2013-01-01 10:46:40 【问题描述】:

我的 numpy 数组切片代码(通过花哨的索引)非常慢。目前是程序的瓶颈。

a.shape
(3218, 6)

ts = time.time(); a[rows][:, cols]; te = time.time(); print('%.8f' % (te-ts));
0.00200009

什么是正确的 numpy 调用来获取由矩阵 a 的行“rows”和列“col”的子集组成的数组? (其实我需要这个结果的转置)

【问题讨论】:

time.time 不是衡量时间的好方法。一般来说,最好使用timeit 1.你的程序到底在做什么? 2.使用适当的python分析器。我发现切片不太可能是您的瓶颈 Oren - 如果您使用 @mgilson 样式提及,它将向用户发送通知(每个评论一个)。 @mgilson:我记得在某些情况下(不过是 4 年前)遇到了问题,可能不再适用。手册说以下For all cases of index arrays, what is returned is a copy of the original data, not a view as one gets for slicesdocs.scipy.org/doc/numpy/user/… @Wolph Numpy 1.15 仍然如此:高级索引总是返回数据的副本(与返回视图的基本切片相反) 【参考方案1】:

让我试着总结一下 Jaime 和 TheodrosZelleke 的优秀答案,并混合一些 cmets。

    Advanced (fancy) indexing 总是返回一个副本,而不是一个视图。 a[rows][:,cols] 意味着 两个 奇特的索引操作,因此创建并丢弃了一个中间副本 a[rows]。方便且可读,但效率不高。此外请注意[:,cols] 通常会从 C-cont 生成 Fortran 连续副本。来源。 a[rows.reshape(-1,1),cols] 是一个高级索引表达式,它基于 rows.reshape(-1,1)cols 是 broadcast 的预期结果的形状。

    一个常见的经验是,扁平化数组中的索引可能比花式索引更有效,因此另一种方法是

    indx = rows.reshape(-1,1)*a.shape[1] + cols
    a.take(indx)
    

    a.take(indx.flat).reshape(rows.size,cols.size)
    

    效率取决于内存访问模式以及起始数组是 C 计数还是 Fortran 连续,因此需要进行实验。

    仅在真正需要时才使用花哨的索引:basic slicing a[rstart:rstop:rstep, cstart:cstop:cstep] 返回一个视图(尽管不是连续的)并且应该更快!

【讨论】:

【参考方案2】:

令我惊讶的是,这种计算第一个线性一维索引的冗长表达式比问题中提出的连续数组索引快 50%

(a.ravel()[(
   cols + (rows * a.shape[1]).reshape((-1,1))
   ).ravel()]).reshape(rows.size, cols.size)

更新: OP 更新了初始数组形状的描述。使用更新后的大小,加速现在高于 99%

In [93]: a = np.random.randn(3218, 1415)

In [94]: rows = np.random.randint(a.shape[0], size=2000)

In [95]: cols = np.random.randint(a.shape[1], size=6)

In [96]: timeit a[rows][:, cols]
10 loops, best of 3: 186 ms per loop

In [97]: timeit (a.ravel()[(cols + (rows * a.shape[1]).reshape((-1,1))).ravel()]).reshape(rows.size, cols.size)
1000 loops, best of 3: 1.56 ms per loop

初始答案: 以下是实录:

In [79]: a = np.random.randn(3218, 6)
In [80]: a.shape
Out[80]: (3218, 6)

In [81]: rows = np.random.randint(a.shape[0], size=2000)
In [82]: cols = np.array([1,3,4,5])

时间法一:

In [83]: timeit a[rows][:, cols]
1000 loops, best of 3: 1.26 ms per loop

时间法2:

In [84]: timeit (a.ravel()[(cols + (rows * a.shape[1]).reshape((-1,1))).ravel()]).reshape(rows.size, cols.size)
1000 loops, best of 3: 568 us per loop

检查结果是否实际相同:

In [85]: result1 = a[rows][:, cols]
In [86]: result2 = (a.ravel()[(cols + (rows * a.shape[1]).reshape((-1,1))).ravel()]).reshape(rows.size, cols.size)

In [87]: np.sum(result1 - result2)
Out[87]: 0.0

【讨论】:

对于 OP 的新要求,它也比我提供的更标准的答案快两倍。不错! 并不是说这样的技巧不能加快速度(至少在特定情况下),但所有这一切都在很大程度上依赖于输入数组是 C 连续的事实。 不足为奇:看到这个answer到一个相关的问题。【参考方案3】:

如果您使用精美的索引和广播进行切片,您可以获得一些速度:

from __future__ import division
import numpy as np

def slice_1(a, rs, cs) :
    return a[rs][:, cs]

def slice_2(a, rs, cs) :
    return a[rs[:, None], cs]

>>> rows, cols = 3218, 6
>>> rs = np.unique(np.random.randint(0, rows, size=(rows//2,)))
>>> cs = np.unique(np.random.randint(0, cols, size=(cols//2,)))
>>> a = np.random.rand(rows, cols)
>>> import timeit
>>> print timeit.timeit('slice_1(a, rs, cs)',
                        'from __main__ import slice_1, a, rs, cs',
                        number=1000)
0.24083110865
>>> print timeit.timeit('slice_2(a, rs, cs)',
                        'from __main__ import slice_2, a, rs, cs',
                        number=1000)
0.206566124519

如果您从百分比的角度来考虑,将速度提高 15% 总是好的,但在我的系统中,对于您的阵列大小,执行切片所需的时间减少了 40 us,很难相信需要 240 us 的操作将成为您的瓶颈。

【讨论】:

原来我有一个 3218x1415 数组,而不是 3218x6。我只提取几列和很多行。上面的代码显示 slice_1 的调用时间是 0.08 秒, slice_2 的调用时间是 0.0004 秒。也许这就是我需要的!【参考方案4】:

使用np.ix_,您可以达到与 ravel/reshape 相似的速度,但代码更清晰:

a = np.random.randn(3218, 1415)
rows = np.random.randint(a.shape[0], size=2000)
cols = np.random.randint(a.shape[1], size=6)
a = np.random.randn(3218, 1415)
rows = np.random.randint(a.shape[0], size=2000)
cols = np.random.randint(a.shape[1], size=6)

%timeit (a.ravel()[(cols + (rows * a.shape[1]).reshape((-1,1))).ravel()]).reshape(rows.size, cols.size)
#101 µs ± 2.36 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)


%timeit ix_ = np.ix_(rows, cols); a[ix_]
#135 µs ± 7.47 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

ix_ = np.ix_(rows, cols)
result1 = a[ix_]
result2 = (a.ravel()[(cols + (rows * a.shape[1]).reshape((-1,1))).ravel()]).reshape(rows.size, cols.size)
​
np.sum(result1 - result2)
0.0

【讨论】:

各种 numpy 花式索引方法的性能,也与 numba

...02-1223:20:44【问题描述】:因为对于我的程序,Numpy数组的快速索引是非常必要的,而且考虑到性能,花哨的索引并没有很好的声誉,所以我决定进行一些测试。尤其是Numba发展很快,我尝试了哪些方法与numba配合得很好。作为 查看详情

numpy花式索引

一给定一个列表,返回索引为1,3,4,5,6的数组   2针对二维数组    需要注意的一点是,对于花式索引。对照下后面的两种方式,查询结果的不同。 查看详情

numpy切片和索引

...bsp; 注意:  将data中所有的负值都设置为0 花式索引 花式索引(Fancyindexing)是一个Numpy术语,它指的是利用整数数组进行索引。例子: 花式索引取特定子集 花式索引使用负数索引 花式索引--使用二... 查看详情

花式索引的 Numpy 循环广播

】花式索引的Numpy循环广播【英文标题】:NumpyCyclicBroadcastofFancyIndexing【发布时间】:2017-12-2803:14:36【问题描述】:A是一个numpy数组,形状为(6,8)我想要:x_id=np.array([0,3])y_id=np.array([1,3,4,7])A[[x_id,y_id]+=1#thisdoesn\'tactuallywork.::2之类的... 查看详情

Numpy 花式索引和赋值

】Numpy花式索引和赋值【英文标题】:Numpyfancyindexingandassignment【发布时间】:2012-03-1412:05:26【问题描述】:通常numpy会强制分配的左侧和右侧匹配,例如,如果我执行a[:]=b,b必须是相同的形状或广播到与a相同的形状。但该规则... 查看详情

numpy 的花式索引是如何实现的?

】numpy的花式索引是如何实现的?【英文标题】:Howisnumpy\'sfancyindexingimplemented?【发布时间】:2017-11-1808:04:06【问题描述】:我正在对2D列表和numpy数组进行一些实验。由此,我提出了3个问题,我很想知道答案。首先,我初始化了... 查看详情

花式索引

花式索引是一个Numpy属于,指的是利用整数数组进行索引。有一个8*4数组。1In[37]:arr=np.empty((8,4))23In[38]:foriinrange(8):4...:arr[i]=i56In[39]:arr7Out[39]:8array([[0.,0.,0.,0.],9[1.,1.,1.,1.],10[2.,2.,2.,2.],11[3.,3.,3.,3.],12[4.,4. 查看详情

Numpy:快速找到第一个价值索引

】Numpy:快速找到第一个价值索引【英文标题】:Numpy:findfirstindexofvaluefast【发布时间】:2011-11-2918:48:39【问题描述】:如何在Numpy数组中找到第一次出现的数字的索引?速度对我来说很重要。我对以下答案不感兴趣,因为它们会... 查看详情

numpy——高级索引(代码片段)

...和切片的索引外,数组可以由整数数组索引、布尔索引及花式索引1整数索引  除了支持python中list那样索引之外,还支持以下实例获取数组中(0,0),(1,1)和(2,0)位置处的元素  importnumpyasnpx=np.array([[1,2],[3,4],[5,6]])y=x[[0,1,2],[0,1,0]]#... 查看详情

[学习笔记][数据分析]02numpy入门与应用

...※  布尔型数组的长度必须跟被索引的轴长度一致  花式索引是利用“整数数组”进行索引。整数数组为索引时候的index。    查看详情

numpy学习(索引和切片,合并,分割,copy与deepcopy)(代码片段)

...5][45][45][34567891011121314][4567891011121314][3456789][35791113]  花式索引程序示例importnumpyasnp#指定索引位置index=[1,5,-7]array3=array[index]print(array3)#使用布尔 查看详情

一文掌握numpy数组的创建索引和切片操作(代码片段)

...与切片2.1一维数组2.2二维数组2.3多维数组2.4布尔型索引2.5花式索引1.numpy数组的创建1.1array函数创建数组最简单也是最直观的方式,就是使用array函数直接创建numpy数组。importnumpyasnpndarray=np.array([1,2,3,4])print(ndarray)#[1234]ndarray&... 查看详情

Nginx 花式索引页眉和页脚从不加载

】Nginx花式索引页眉和页脚从不加载【英文标题】:Nginxfancy-indexheaderandfooterneverload【发布时间】:2022-01-1612:27:45【问题描述】:我正在创建一个Nginx文件服务器,并且我正在尝试启用fancy-index模块以获得自定义页眉和页脚,但我... 查看详情

python的numpy库学习总结和介绍(超详细)模块

...索引3.掩码索引(布尔型索引)4.花哨的索引(有的书翻译成花式索引)  5.组合索引6.数据的修改 四、操作数据1.通用函数 2.聚合操作 3.数组元素的排序五.从线性代数的角度来使用numpy1.矩阵的乘法2.矩阵的转置和逆  3.numpy的多项... 查看详情

numpy 从索引列表创建 2D 掩码 [+ 然后从掩码数组中绘制]

...然后从剩余元素中抽取随机样本而不进行替换。我需要既快速/高效(希望避免for循环)又占用内存小的东西, 查看详情

数据分析2numpy(ndarray数组,属性,创建,索引切片,运算,函数,随机数),pandas(series创建,缺失值处理,特性,索引,dataframe)

...引6.切片 7.数组的向量运算和矢量运算8.布尔型索引9.花式索引10.一元函数 11.数学统计函数12.随机数生成  Pandas主要是两大数据类型,DataFrame,Series 1.Series1.Series的创建  2.缺失值处理     Se... 查看详情

C++ OpenCV 中的快速索引

】C++OpenCV中的快速索引【英文标题】:FastindexinginC++OpenCV【发布时间】:2018-07-1811:54:28【问题描述】:我对Python中的OpenCV非常满意。我最近切换到C++版本,我一直遇到cv::Mat索引的问题,因为我太习惯于numpy的数组。特别是,我试... 查看详情

numpy常用方法及应用总汇(代码片段)

...组与标量4.索引和切片4.1基本索引和切片4.2布尔型索引4.3花式索引5.数组转置和轴对换6.数组函数6.1通用函数:元素级数字函数6.2where函数6.3数学和统计方法6.4排序方法6.5集合运算函数线性代数Numpy1.基本操作1.1数组转换创建数组的... 查看详情