我想扩展 numpy 中的结构化数组对象,以便我可以轻松添加新元素。
例如,对于一个简单的结构化数组
>>> import numpy as np
>>> x=np.ndarray((2,),dtype={'names':['A','B'],'formats':['f8','f8']})
>>> x['A']=[1,2]
>>> x['B']=[3,4]
Run Code Online (Sandbox Code Playgroud)
我想轻松添加一个新元素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
Run Code Online (Sandbox Code Playgroud)
然后,
>>> 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')])
Run Code Online (Sandbox Code Playgroud)
有没有什么方法可以以类似字典的方式添加新元素?,例如
>>> 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')])
Run Code Online (Sandbox Code Playgroud)
更新:
通过使用__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
Run Code Online (Sandbox Code Playgroud)
然后
>>> 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')])
Run Code Online (Sandbox Code Playgroud)
更新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
>>>
Run Code Online (Sandbox Code Playgroud)
相反numpy.recarray,在 my 中,numpy 1.6.1您会得到一个额外的方法field,当您从numpy.ndarray.
这个问题或这个问题(如果使用 numpy 1.3)还讨论向structured array. 从那里您将看到使用:
import numpy.lib.recfunctions as rf
rf.append_fields( ... )
Run Code Online (Sandbox Code Playgroud)
可以大大简化您的生活。乍一看,我以为这个函数会附加到原始数组,但它创建了一个新实例。下面显示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
Run Code Online (Sandbox Code Playgroud)