【问题标题】:Python Dynamic Array allocation, Matlab stylePython 动态数组分配,Matlab 风格
【发布时间】:2012-07-13 09:55:06
【问题描述】:

我正在尝试将我构建的一些 Matlab 库移动到 python 环境中。到目前为止,我面临的最大问题是基于索引规范的数组动态分配。例如,使用 Matlab,输入以下内容:

x = [1 2];
x(5) = 3;

会导致:

x = [ 1     2     0     0     3]

换句话说,我事先不知道 (x) 的大小,也不知道它的内容。必须根据我提供的索引动态定义数组。

在 python 中,尝试以下操作:

from numpy import *
x = array([1,2])
x[4] = 3

会导致以下错误:IndexError: index out of bounds。解决方法是在循环中递增数组,然后将所需的值分配为:

from numpy import *
x = array([1,2])

idx = 4
for i in range(size(x),idx+1):
    x = append(x,0)

x[idx] = 3
print x

它可以工作,但它不是很方便,并且对于 n 维数组可能会变得非常麻烦。我虽然关于子类化 ndarray 来实现我的目标,但我不确定它是否会工作。有人知道更好的方法吗?


感谢您的快速回复。我不知道 setitem 方法(我对 Python 还很陌生)。我只是简单地覆盖了 ndarray 类如下:

import numpy as np

class marray(np.ndarray):

    def __setitem__(self, key, value):

        # Array properties
        nDim = np.ndim(self)
        dims = list(np.shape(self))

        # Requested Index
        if type(key)==int: key=key,
        nDim_rq = len(key)
        dims_rq = list(key)

        for i in range(nDim_rq): dims_rq[i]+=1        

        # Provided indices match current array number of dimensions
        if nDim_rq==nDim:

            # Define new dimensions
            newdims = []
            for iDim in range(nDim):
                v = max([dims[iDim],dims_rq[iDim]])
                newdims.append(v)

            # Resize if necessary
            if newdims != dims:
              self.resize(newdims,refcheck=False)

        return super(marray, self).__setitem__(key, value)

它就像一个魅力!但是,我需要修改上面的代码,以便 setitem 允许在此请求之后更改维度数:

a = marray([0,0])
a[3,1,0] = 0

不幸的是,当我尝试使用诸如

之类的 numpy 函数时
self = np.expand_dims(self,2)

返回的类型是 numpy.ndarray 而不是 ma​​in.marray。如果将 marray 作为输入提供,关于如何强制执行 numpy 函数输出 marray 的任何想法?我认为使用 array_wrap 应该是可行的,但我永远找不到确切的方法。任何帮助将不胜感激。

【问题讨论】:

  • 您的append-in-loop 算法需要二次方时间,因此对于大型数组来说会变得非常慢。
  • 借此机会摆脱所有临时数组重新分配;)
  • a) 如果您不打算使用稀疏值,即您想按顺序设置它们,您可以使用类似 np.fromiter 的方法。 b) 如果您仍然打算使用 ndarray 子类,这篇文章应该澄清为什么不保留类型以及如何修复它docs.scipy.org/doc/numpy-1.10.1/user/basics.subclassing.html

标签: python arrays matlab numpy indexing


【解决方案1】:

冒昧地从Dynamic list that automatically expands 更新了我的旧答案。认为这应该可以完成您需要/想要的大部分工作

class matlab_list(list):
    def __init__(self):
        def zero():
            while 1:
                yield 0
        self._num_gen = zero()

    def __setitem__(self,index,value):
        if isinstance(index, int):
            self.expandfor(index)
            return super(dynamic_list,self).__setitem__(index,value)

        elif isinstance(index, slice):
            if index.stop<index.start:
                return super(dynamic_list,self).__setitem__(index,value)
            else:
                self.expandfor(index.stop if abs(index.stop)>abs(index.start) else index.start)
            return super(dynamic_list,self).__setitem__(index,value)

    def expandfor(self,index):
            rng = []
            if abs(index)>len(self)-1:
                if index<0:
                    rng = xrange(abs(index)-len(self))
                    for i in rng:
                        self.insert(0,self_num_gen.next())
                else:
                    rng = xrange(abs(index)-len(self)+1)
                    for i in rng:
                        self.append(self._num_gen.next())

# Usage
spec_list = matlab_list()
spec_list[5] = 14

【讨论】:

    【解决方案2】:

    这不是你想要的,但是......

    x = np.array([1, 2])
    
    try:
        x[index] = value
    except IndexError:
        oldsize = len(x)   # will be trickier for multidimensional arrays; you'll need to use x.shape or something and take advantage of numpy's advanced slicing ability
        x = np.resize(x, index+1) # Python uses C-style 0-based indices
        x[oldsize:index] = 0 # You could also do x[oldsize:] = 0, but that would mean you'd be assigning to the final position twice.
        x[index] = value
    
    >>> x = np.array([1, 2])
    >>> x = np.resize(x, 5)
    >>> x[2:5] = 0
    >>> x[4] = 3
    >>> x
    array([1, 2, 0, 0, 3])
    

    由于 numpy 如何在后台线性存储数据(尽管可以在创建数组时指定它是以行为主还是以列为主),多维数组在这里非常棘手。

    >>> x = np.array([[1, 2, 3], [4, 5, 6]])
    >>> np.resize(x, (6, 4))
    array([[1, 2, 3, 4],
           [5, 6, 1, 2],
           [3, 4, 5, 6],
           [1, 2, 3, 4],
           [5, 6, 1, 2],
           [3, 4, 5, 6]])
    

    你需要这样做或类似的事情:

    >>> y = np.zeros((6, 4))
    >>> y[:x.shape[0], :x.shape[1]] = x
    >>> y
    array([[ 1.,  2.,  3.,  0.],
           [ 4.,  5.,  6.,  0.],
           [ 0.,  0.,  0.,  0.],
           [ 0.,  0.,  0.,  0.],
           [ 0.,  0.,  0.,  0.],
           [ 0.,  0.,  0.,  0.]])
    

    【讨论】:

      【解决方案3】:

      python dict 可以作为稀疏数组很好地工作。主要问题是初始化稀疏数组的语法不会那么漂亮:

      listarray = [100,200,300]
      dictarray = {0:100, 1:200, 2:300}
      

      但之后插入或检索元素的语法是相同的

      dictarray[5] = 2345
      

      【讨论】:

      • 不幸的是,这在 n 维中不起作用,并且可能不如数组有效。
      猜你喜欢
      • 1970-01-01
      • 2011-08-22
      • 1970-01-01
      • 2020-03-06
      • 1970-01-01
      • 2013-07-25
      • 1970-01-01
      • 2020-09-13
      • 2015-03-09
      相关资源
      最近更新 更多