有些问题已经接近,但我没有找到具体的答案。我正在尝试沿着给定的轴对一个numpy 3D数组进行一些就地排序。我不想简单的排序,我想根据我自己的索引求助数组。例如
a = np.random.rand((3,3,3))
让我们说我想根据旧数组的以下索引求最后一个维度:
new_order = [1,2,0]
我希望能够说:
a[:,:,new_order] = a
但这不符合预期。建议?
答案 0 :(得分:6)
np.ndarray.sort
是唯一一个声称属实的地方,并没有给你太多控制权。
将订单索引放在正确的位置 - 但可能会产生不可预测的结果。显然它正在进行某种顺序分配,左边的早期赋值会影响右边的值。
In [719]: a=np.arange(12).reshape(3,4)
In [720]: a[:,[0,1,3,2]]=a
In [721]: a
Out[721]:
array([[ 0, 1, 2, 2],
[ 4, 5, 6, 6],
[ 8, 9, 10, 10]])
要做到这种分配,可以预见到需要某种缓冲。
In [728]: a[:,[0,1,3,2]]=a.copy()
In [729]: a
Out[729]:
array([[ 0, 1, 3, 2],
[ 4, 5, 7, 6],
[ 8, 9, 11, 10]])
权利的索引绕过这个,但这不是就地。变量a
指向一个新对象。
In [731]: a=a[:,[0,1,3,2]]
In [732]: a
Out[732]:
array([[ 0, 1, 3, 2],
[ 4, 5, 7, 6],
[ 8, 9, 11, 10]])
但使用[:]
进行分配可能会解决此问题:
In [738]: a=np.arange(12).reshape(3,4)
In [739]: a.__array_interface__
Out[739]:
{'data': (181868592, False), # 181... is the id of the data buffer
'descr': [('', '<i4')],
'shape': (3, 4),
'strides': None,
'typestr': '<i4',
'version': 3}
In [740]: a[:]=a[:,[0,1,3,2]]
In [741]: a.__array_interface__
Out[741]:
{'data': (181868592, False), # same data buffer
'descr': [('', '<i4')],
'shape': (3, 4),
'strides': None,
'typestr': '<i4',
'version': 3}
In [742]: a
Out[742]:
array([[ 0, 1, 3, 2],
[ 4, 5, 7, 6],
[ 8, 9, 11, 10]])
a.data
id相同的事实表明这是一个就地动作。但是用其他索引来测试它会很好,以确保它能达到你想要的效果。
但是,'inplace'排序是否必要?如果阵列非常大,可能需要避免内存错误。但我们必须测试替代方案,看看它们是否有效。
如果有其他变量使用相同的数据, inplace
也很重要。例如
b = a.T # a transpose
a[:]=
b
行将重新排序a
。 b
和data
继续共享相同的a=
。使用b
时,a
保持不变。 b
和{{1}}现已脱钩。
答案 1 :(得分:1)
不幸的是,numpy
没有内置的解决方案。唯一的方法是使用一些巧妙的分配方法或编写自己的自定义方法。
使用循环检测,用于记住索引的附加集和用于缓存轴的辅助数组,为此我编写了一个自定义方法,该方法对于重新排列大型ndarrays
很有用:
import numpy as np
def put_at(index, axis=-1, slc=(slice(None),)):
"""Gets the numpy indexer for the given index based on the axis."""
return (axis < 0)*(Ellipsis,) + axis*slc + (index,) + (-1-axis)*slc
def reorder_inplace(array, new_order, axis=0):
"""
Reindex (reorder) the array along an axis.
:param array: The array to reindex.
:param new_order: A list with the new index order. Must be a valid permutation.
:param axis: The axis to reindex.
"""
if np.size(array, axis=axis) != len(new_order):
raise ValueError(
'The new order did not match indexed array along dimension %{0}; '
'dimension is %{1} but corresponding boolean dimension is %{2}'.format(
axis, np.size(array, axis=axis), len(new_order)
)
)
visited = set()
for index, source in enumerate(new_order):
if index not in visited and index != source:
initial_values = np.take(array, index, axis=axis).copy()
destination = index
visited.add(destination)
while source != index:
if source in visited:
raise IndexError(
'The new order is not unique; '
'duplicate found at position %{0} with value %{1}'.format(
destination, source
)
)
array[put_at(destination, axis=axis)] = array.take(source, axis=axis)
destination = source
source = new_order[destination]
visited.add(destination)
array[put_at(destination, axis=axis)] = initial_values
示例:
In[4]: a = np.arange(15).reshape(3, 5)
In[5]: a
Out[5]:
array([[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14]])
在轴0
上重新排序:
In[6]: reorder_inplace(a, [2, 0, 1], axis=0)
In[7]: a
Out[7]:
array([[10, 11, 12, 13, 14],
[ 0, 1, 2, 3, 4],
[ 5, 6, 7, 8, 9]])
在轴1
上重新排序:
In[10]: reorder_inplace(a, [3, 2, 0, 4, 1], axis=1)
In[11]: a
Out[11]:
array([[ 3, 2, 0, 4, 1],
[ 8, 7, 5, 9, 6],
[13, 12, 10, 14, 11]]
1000 x 1000
小阵列的计时和内存
In[5]: a = np.arange(1000 * 1000).reshape(1000, 1000)
In[6]: %timeit reorder_inplace(a, np.random.permutation(1000))
8.19 ms ± 18.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In[7]: %memit reorder_inplace(a, np.random.permutation(1000))
peak memory: 81.75 MiB, increment: 0.49 MiB
In[8]: %timeit a[:] = a[np.random.permutation(1000), :]
3.27 ms ± 9.49 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
In[9]: %memit a[:] = a[np.random.permutation(1000), :]
peak memory: 89.56 MiB, increment: 0.01 MiB
对于小型阵列,内存消耗没有太大差异,但是numpy
版本的速度要快得多。
20000 x 20000
的时间和内存
In[5]: a = np.arange(20000 * 20000).reshape(20000, 20000)
In[6]: %timeit reorder_inplace(a, np.random.permutation(20000))
1.16 s ± 1.39 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In[7]: %memit reorder_inplace(a, np.random.permutation(20000))
peak memory: 3130.77 MiB, increment: 0.19 MiB
In[8]: %timeit a[:] = a[np.random.permutation(20000), :]
1.84 s ± 2.26 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In[9]: %memit a[:] = a[np.random.permutation(20000), :]
peak memory: 6182.80 MiB, increment: 3051.76 MiB
当数组的大小增加一个缺口时,numpy
版本的速度就会慢得多。 numpy
版本的内存消耗也非常高。自定义就地重新排序使用的金额可以忽略不计。
答案 2 :(得分:0)
你在这里,
a = a[:, :, new_order]
此外,对于Matlab用户的页面,这里有几个'numpy',当我开始使用时,我发现它很有用: