Numpy.trim_zeros用于结构化数组而不创建新数组

时间:2016-01-27 11:47:20

标签: numpy

是否可以修剪结构化numpy数组的零''记录'而不复制它;即,在开始或结束时为“未使用的”零条目释放的空闲内存;实际上,我只对最后修剪零感兴趣。

1d数组有内置函数numpy.trim_zeros()。它的返回值:

  

返回:

     

trimmed:1-D数组或序列

     

修剪输入的结果。保留输入数据类型。

但是,我不能说这是否不会创建副本并且只释放内存。我不够精通从源代码中告诉它的行为。

更具体地说,我有以下代码:

import numpy
edges = numpy.zeros(3, dtype=[('i', 'i4'), ('j', 'i4'), ('length', 'f4')])
# fill the first two records with sensible data:
edges[0]['i'] = 0
edges[0]['j'] = 1
edges[0]['length'] = 2.0
edges[1]['i'] = 1
edges[1]['j'] = 2
edges[1]['length'] = 2.0
# list memory adress and size
edges.__array_interface__
edges = numpy.trim_zeros(edges)  # does not work for structured array
edges.__array_interface__

更新

我的问题有些“双重”:

1)内置函数是否只是释放内存还是复制数组?

  

答案:副本创建一个切片(= view); [ipython console] import numpy; numpy??(另请参阅Resize NumPy array to smaller size without copyView onto a numpy array?

2)为结构化数组提供类似功能的解决方案是什么?

  

答案:

begin=(edges!=numpy.zeros(1,edges.dtype)).argmax()
end=len(edges)-(edges!=numpy.zeros(1,edges.dtype))[::-1].argmax()
# 1) create slice without copy but no memory is free
goodedges=edges[begin:end]
# 2) or copy and free memory (temporary both arrays exist)
goodedges=edges[begin:end].copy()
del edges

2 个答案:

答案 0 :(得分:1)

恕我直言,有两个问题。

  • 首先,trim_zeros函数无法识别复合dtype上的零。

您可以begin=(edges!=zeros(1,edges.dtype)).argmax()找到它们 和end=len(edges)-(edges!=zeros(1,edges.dtype))[::-1].argmax()。然后goodedges=edges[begin:end]是有趣的数据。

  • 其次,trim_zeros函数不释放内存:
  

退货-------   修剪:1-D阵列或序列。       修剪输入的结果。保留输入数据类型。

所以我认为你必须手动完成:goodedges=edges[begin:end].copy();del edges

答案 1 :(得分:1)

要扩展我的评论,让我们在一个简单的整数数组上尝试trim_zeros

In [252]: arr = np.zeros(10,int)
In [253]: arr[3:8]=np.ones(5)
In [254]: arr
Out[254]: array([0, 0, 0, 1, 1, 1, 1, 1, 0, 0])
In [255]: arr1=np.trim_zeros(arr)
In [256]: arr1
Out[256]: array([1, 1, 1, 1, 1])

现在比较__array_interface__词典:

In [257]: arr.__array_interface__
Out[257]: 
{'descr': [('', '<i4')],
 'shape': (10,),
 'version': 3,
 'strides': None,
 'data': (150760432, False),
 'typestr': '<i4'}

In [258]: arr1.__array_interface__
Out[258]: 
{'descr': [('', '<i4')],
 'shape': (5,),
 'version': 3,
 'strides': None,
 'data': (150760444, False),
 'typestr': '<i4'}

shape反映了我们想要的变化。但请查看data指针,... 432和... 444。 arr1只是在同一个缓冲区中指向12个字节(3个整数)。

如果我删除arr或重新分配{偶数arr=arr1),则arr1会继续指向此数据缓冲区。 numpy保留某种引用计数,并且仅在所有引用都消失时才回收数据缓冲区。

trim_zeros的代码是(在ipython中使用'??'获取)

File:        /usr/lib/python3/dist-packages/numpy/lib/function_base.py
def trim_zeros(filt, trim='fb'):
    first = 0
    trim = trim.upper()
    if 'F' in trim:
        for i in filt:
            if i != 0.: break
            else: first = first + 1
    last = len(filt)
    if 'B' in trim:
        for i in filt[::-1]:
            if i != 0.: break
            else: last = last - 1
    return filt[first:last]

工作在最后一行,并清楚地返回一个切片,一个视图。大多数代码处理2个修剪选项(F和B)。请注意,它使用迭代来查找firstlast非零。对于在开头或结尾只有几个额外0的数组,这应该没问题。但这并不是SO问题经常寻求的“矢量化”操作。

在此问题出现之前,我甚至不知道trim_zeros存在,但我对其代码和行动并不感到惊讶。

在另一方面,这是创建edges数组的更紧凑方式。

In [259]: edges =np.zeros(3, dtype=[('i', 'i4'), ('j', 'i4'), ('length', 'f4')])
In [260]: edges[:2]=[(0,1,2.0),(1,2,2.0)]

删除您可以使用的所有zero元素:

edges[edges!=numpy.zeros(1,edges.dtype)]

这是一份副本。它确实删除了“嵌入”零,但如果只有零填充在前面的插槽中,那么这可能不是问题。

如果您在列表中收集edges数据并在最后构建数组,则可能根本不需要此修剪:

edges1 = np.array([(0,1,2.0),(1,2,2.0)], dtype=edges.dtype)