4

我想在 numpy 中扩展结构化数组对象,以便我可以轻松添加新元素。

例如,对于一个简单的结构化数组

>>> import numpy as np
>>> x=np.ndarray((2,),dtype={'names':['A','B'],'formats':['f8','f8']})
>>> x['A']=[1,2]
>>> x['B']=[3,4]

我想轻松地添加一个新元素x['C']=[5,6],但随后出现与未定义名称相关的错误'C'

只需添加一个新方法即可np.ndarray

import numpy as np
class sndarray(np.ndarray):
    def column_stack(self,i,x):
        formats=['f8']*len(self.dtype.names)
        new=sndarray(shape=self.shape,dtype={'names':list(self.dtype.names)+[i],'formats':formats+['f8']})
        for key in self.dtype.names:
            new[key]=self[key]

        new[i]=x

        return new

然后,

>>> x=sndarray((2,),dtype={'names':['A','B'],'formats':['f8','f8']})
>>> x['A']=[1,2]
>>> x['B']=[3,4]
>>> x=x.column_stack('C',[4,4])
>>> x
sndarray([(1.0, 3.0, 4.0), (2.0, 4.0, 4.0)], 
  dtype=[('A', '<f8'), ('B', '<f8'), ('C', '<f8')])

有什么方法可以像字典一样添加新元素?例如

>>> x['C']=[4,4]
>>> x
sndarray([(1.0, 3.0, 4.0), (2.0, 4.0, 4.0)], 
  dtype=[('A', '<f8'), ('B', '<f8'), ('C', '<f8')])

更新:

通过使用__setitem__,我离理想的解决方案还有一步之遥,因为我不知道如何:

更改 self 引用的对象

import numpy as np

class sdarray(np.ndarray):
    def __setitem__(self, i,x):
    if i in self.dtype.names:
        super(sdarray, self).__setitem__(i,x)
    else:
        formats=['f8']*len(self.dtype.names)
        new=sdarray(shape=self.shape,dtype={'names':list(self.dtype.names)+[i],'formats':formats+['f8']})
        for key in self.dtype.names:
           new[key]=self[key]

        new[i]=x

        self.with_new_column=new

然后

>>> x=sndarray((2,),dtype={'names':['A','B'],'formats':['f8','f8']})
>>> x['A']=[1,2]
>>> x['B']=[3,4]
>>> x['C']=[4,4]
>>> x=x.with_new_column #extra uggly step!
>>> x
sndarray([(1.0, 3.0, 4.0), (2.0, 4.0, 4.0)], 
  dtype=[('A', '<f8'), ('B', '<f8'), ('C', '<f8')])

更新 2 在选择的答案中正确实施后,我发现问题已经被pandas DataFrame对象解决了:

>>> import numpy as np
>>> import pandas as pd
>>> x=np.ndarray((2,),dtype={'names':['A','B'],'formats':['f8','f8']})
>>> x=pd.DataFrame(x)
>>> x['A']=[1,2]
>>> x['B']=[3,4]
>>> x['C']=[4,4]
>>> x
   A  B  C
0  1  3  4
1  2  4  4
>>> 
4

1 回答 1

3

改用numpy.recarray,在我的子类中,numpy 1.6.1你会得到一个额外的方法field,当你从numpy.ndarray.

这个问题这个问题(如果使用 numpy 1.3)还讨论了将字段添加到structured array. 从那里你会看到使用:

import numpy.lib.recfunctions as rf
rf.append_fields( ... )

可以大大简化你的生活。乍一看,我认为这个函数会附加到原始数组,但它创建了一个新实例。下面class显示的是使用您的解决方案__setitem__(),效果很好。

您发现的导致您找到丑陋解决方案的问题已在另一个问题中报告。问题是当你这样做时,self=... 你只是将new对象存储在一个变量中,但实体sdarray没有被更新。也许可以直接class从其方法内部销毁和重建,但基于讨论class可以创建以下内容,其中ndarray不是子类,而是在内部存储和调用。添加了一些其他方法以使其工作并看起来像您直接使用ndarray. 我没有详细测试它。

对于自动调整大小,这里提出了一个很好的解决方案。您也可以将其合并到您的代码中。

import numpy as np

class sdarray(object):
    def __init__(self, *args, **kwargs):
        self.recarray =  np.recarray( *args, **kwargs)

    def __getattr__(self,attr):
        if hasattr( self.recarray, attr ):
            return getattr( self.recarray, attr )
        else:
            return getattr( self, attr )

    def __len__(self):
        return self.recarray.__len__()

    def __add__(self,other):
        return self.recarray.__add__(other)

    def __sub__(self,other):
        return self.recarray.__sub__(other)

    def __mul__(self,other):
        return self.recarray.__mul__(other)

    def __rmul__(self,other):
        return self.recarray.__rmul__(other)

    def __getitem__(self,i):
        return self.recarray.__getitem__(i)

    def __str__(self):
        return self.recarray.__str__()

    def __repr__(self):
        return self.recarray.__repr__()

    def __setitem__(self, i, x):
        keys = []
        formats = []
        if i in self.dtype.names:
            self.recarray.__setitem__(i,x)
        else:
            for name, t in self.dtype.fields.iteritems():
                keys.append(name)
                formats.append(t[0])
            keys.append( i )
            formats.append( formats[-1] )
            new = np.recarray( shape = self.shape,
                              dtype = {'names'  : keys,
                                       'formats': formats} )
            for k in keys[:-1]:
                new[k] = self[k]
            new[i] = x
            self.recarray = new
于 2013-04-19T22:57:12.087 回答