假设我们有一个类的实例列表,它们都有一个我们知道是浮点的属性 - 调用属性x。在程序的各个点上,我们想要提取x的所有值的numpy数组,以便对x的分布进行一些分析。这个提取过程已经完成了很多,并且已被确定为程序的一个缓慢部分。这是一个非常简单的例子来具体说明我的想法:
import numpy as np
# Create example object with list of values
class stub_object(object):
def __init__(self, x):
self.x = x
# Define a list of these fake objects
stubs = [stub_object(i) for i in range(10)]
# ...much later, want to quickly extract a vector of this particular attribute:
numpy_x_array = np.array([a_stub.x for a_stub in stubs])
这里有一个问题:是否有一种更聪明,更快速的方式来跟踪" x"在"存根"中跨越stub_object实例的属性列表,这样构建" numpy_x_array"比上面的过程更快?
这里有一个粗略的想法我想要敲定:我可以创建一个"全局到班级类型" numpy向量,随着对象集的更新而更新,但我可以在任何时候有效地运行吗?
我真正想要的只是朝着正确的方向努力。"提供关键字我可以谷歌/搜索SO / docs正是我正在寻找的。 p>
对于它的价值,我已经研究了这些,这让我更进一步,但并不是完全在那里:
我看过的其他人,但没有那么有用:
(当然,一个选项是"简单地"彻底检查代码的结构,这样就不会使用"存根"" stub_objects的列表,&# 34;有一个大对象,比如stub_population,它维护列表和/或numpy数组中的相关属性,以及简单地作用于那些数组的元素的方法。其缺点是大量的重构,以及一些减少抽象和灵活性建模" stub_object"作为它自己的东西。如果有一个聪明的方法,我想避免这种情况。)
修改:我使用的是2.7.x
编辑2: @hpaulj,您的示例得到了很大的帮助 - 接受了答案。
这是上面示例代码的非常简单的第一遍版本,它正在做我想要的。有一个非常简单的指示可能是一个数量级的加速,没有显着重新排列代码体。 很好。谢谢!
size = 20
# Create example object with list of values
class stub_object(object):
_x = np.zeros(size, dtype=np.float64)
def __init__(self, x, i):
# A quick cop-out for expanding the array:
if i >= len(self._x):
raise Exception, "Index i = " +str(i)+ " is larger than allowable object size of len(self._x) = "+ str(self._x)
self.x = self._x[i:i+1]
self.set_x(x)
def get_x(self):
return self.x[0]
def set_x(self, x_new):
self.x[0] = x_new
# Examine:
# Define a list of these fake objects
stubs = [stub_object(x=i**2, i) for i in range(size)]
# ...much later, want to quickly extract a vector of this particular attribute:
#numpy_x_array = np.array([a_stub.x for a_stub in stubs])
# Now can do:
numpy_x_array = stub_object._x # or
numpy_x_array = stubs[0]._x # if need to use the list to access
还没有使用属性,但真的非常喜欢这个想法,并且它应该在使代码非常接近未改变方面走得很远。
答案 0 :(得分:3)
基本问题是你的对象是通过内存存储的,每个对象的字典中都有属性。但是对于数组工作,值必须存储在连续的数据缓冲区中。
我已经在其他SO问题中探讨了这个问题,但是你发现的问题更早。我还有很多东西需要补充。
np.array([a_stub.x for a_stub in stubs])
使用itertools
或fromiter
的备选方案不应该更改速度,因为时间消费者是a_stub.x
访问权限,而不是迭代机制。您可以通过测试
np.array([1 for _ in range(len(stubs))]
我怀疑最好的选择是使用一个或多个数组作为主存储,并重构您的类,以便从该存储中获取该属性。
如果您知道您将拥有10个对象,那么请创建一个该大小的空数组。创建对象时,为其指定唯一索引。 x
属性可以是property
,其getter / setter访问该数组的data[i]
元素。通过使x
成为属性而不是主属性,您应该能够保留大部分对象机制。您只需更改几种方法即可尝试不同的存储方法。
我试图使用类属性作为主要数组存储来绘制它,但我仍然有一些错误。
具有访问数组的x
属性的类:
class MyObj(object):
xdata = np.zeros(10)
def __init__(self,idx, x):
self._idx = idx
self.set_x(x)
def set_x(self,x):
self.xdata[self._idx] = x
def get_x(self):
return self.xdata[self._idx]
def __repr__(self):
return "<obj>x=%s"%self.get_x()
x = property(get_x, set_x)
In [67]: objs = [MyObj(i, 3*i) for i in range(10)]
In [68]: objs
Out[68]:
[<obj>x=0.0,
<obj>x=3.0,
<obj>x=6.0,
...
<obj>x=27.0]
In [69]: objs[3].x
Out[69]: 9.0
In [70]: objs[3].xdata
Out[70]: array([ 0., 3., 6., 9., 12., 15., 18., 21., 24., 27.])
In [71]: objs[3].xdata += 3
In [72]: [o.x for o in objs]
Out[72]: [3.0, 6.0, 9.0, 12.0, 15.0, 18.0, 21.0, 24.0, 27.0, 30.0]
在适当的位置更改数组是最简单的。但也可以替换阵列本身(从而“增长”类集)
In [79]: MyObj.xdata=np.ones((20,))
In [80]: a = MyObj(11,25)
In [81]: a
Out[81]: <obj>x=25.0
In [82]: MyObj.xdata
Out[82]:
array([ 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
25., 1., 1., 1., 1., 1., 1., 1., 1.])
In [83]: [o.x for o in objs]
Out[83]: [1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0]
我们必须小心修改属性。例如,我试过
objs[3].xdata += 3
打算为全班改变xdata
。但最终只为该对象分配了一个新的xdata
数组。我们还应该能够自动增加对象索引(这些天我比numpy
方法更熟悉Python类结构。)
如果我将getter
替换为取一个切片的那个:
def get_x(self):
return self.xdata[self._idx:self._idx+1]
In [107]: objs=[MyObj(i,i*3) for i in range(10)]
In [109]: objs
Out[109]:
[<obj>x=[ 0.],
<obj>x=[ 3.],
...
<obj>x=[ 27.]]
np.info
(或.__array_interface__
)向我提供有关xdata
数组的信息,包括其数据缓冲区指针:
In [110]: np.info(MyObj.xdata)
class: ndarray
shape: (10,)
strides: (8,)
itemsize: 8
aligned: True
contiguous: True
fortran: True
data pointer: 0xabf0a70
byteorder: little
byteswap: False
type: float64
第一个对象的切片指向同一个地方:
In [111]: np.info(objs[0].x)
class: ndarray
shape: (1,)
strides: (8,)
itemsize: 8
....
data pointer: 0xabf0a70
...
下一个对象指向下一个float(进一步8个字节):
In [112]: np.info(objs[1].x)
class: ndarray
shape: (1,)
...
data pointer: 0xabf0a78
....
我不确定切片/视图的访问是否值得。