Python动态数组分配,Matlab风格

时间:2012-07-13 14:18:22

标签: python arrays matlab numpy indexing

我正在尝试将我构建的一些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而不是 main .marray。如果将marray作为输入提供,我是否可以强制执行numpy函数输出?我认为使用 array_wrap 应该可行,但我找不到确切的方法。任何帮助将不胜感激。

3 个答案:

答案 0 :(得分:3)

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

答案 1 :(得分:3)

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

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如何线性地存储数据(虽然它是否可以在创建数组时指定为row-major或column-major),但多维数组在这里非常棘手。

>>> 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.]])

答案 2 :(得分:0)

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

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

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

dictarray[5] = 2345