【问题标题】:combining features of 'array of objects' with 'object of arrays'将“对象数组”的特征与“数组对象”相结合
【发布时间】:2015-09-28 21:06:43
【问题描述】:

我正在寻找某种范式或实现来有效地处理多组耦合 N 维数组 (ndarrays)。具体来说,我希望有一个实现允许我同时对整个对象数组(例如someObjs = objects[100:200])或这些对象的单个属性(例如somePars1 = objects.par1[100:200])进行切片。

为了扩展上面的例子,我可以通过两种方式构造以下子集:

def subset1(objects, beg, end):
    pars1 = [ obj.par1 for obj in objects[beg:end] ]
    pars2 = [ obj.par2 for obj in objects[beg:end] ]
    return pars1, pars2

def subset2(objects, beg, end):
    pars1 = objects.par1[beg:end]
    pars2 = objects.par2[beg:end]
    return pars1, pars2

它们将是相同的。


编辑:

一种方法是覆盖 __getitem__ (etc) 方法,例如,

class Objects(object):
    def __init__(self, p1, p2):
        self.par1 = p1
        self.par2 = p2
    ...
    def __getitem__(self, key):
        return Objects(self.p1[key], self.p2[key])

但这是非常低效的,并且它复制了子集。也许有办法返回子集的view??

【问题讨论】:

  • 我不太明白这个问题。您是否正在尝试找到一种允许您将索引放置在任一位置的语言?这与大多数语言的结构是对立的。如果您有一个对象列表,则下标表达式 必须 直接应用于列表,而不是元素。无论哪种方式,您的代码都是正确的,具体取决于您设计对象的方式。但是,您不能在尊重类型特征的语言中拥有这种双重性质。
  • @Prune,我认为情况并非如此。请参阅我添加的示例。实现此功能当然是可能的 --- 但我想不出任何有效/高效的方法。
  • 我现在明白了;谢谢。请记住,这本质上是低效的:您正在接受自然结构,然后在其上覆盖人工结构。对人工结构的每一次引用——你想要的视图——都需要拆除和重新排列“正确”组织的元素。但是,视图模式可能是实现可维护性的方法。我不知道这是否告诉您任何新信息;我可能只是在强化你的恐惧。
  • 我认为这实际上是一个好方法。要记住的一件事是 numpy 数组的切片总是返回一个视图(只要您使用切片而不是列表/元组的“花式索引”)。因此,只要您强制执行连续切片,您的示例实际上根本不会复制那么多内存。但是,共享视图的常见注意事项适用:如果您修改一个,您就是在修改所有内容,您需要了解在使用内容时哪些内容会生成副本,哪些内容不会。
  • numpy 文档详细介绍了它:docs.scipy.org/doc/numpy/reference/… 不要过多插入我自己的答案,但您可能会发现这很有用:stackoverflow.com/questions/4370745/view-onto-a-numpy-array/… 它专门处理如何避免复制并确保视图。我现在时间太短,无法完整回答,所以如果有人想浓缩并写一个,请随意!

标签: python algorithm oop numpy


【解决方案1】:

对象数组和对象数组方法

一个示例对象类

In [56]: class MyObj(object):
   ....:     def __init__(self, par1,par2):
   ....:         self.par1=par1
   ....:         self.par2=par2

这些对象的数组 - 只不过是一个带有数组包装器的列表

In [57]: objects=np.array([MyObj(1,2),MyObj(3,4),MyObj(2,3),MyObj(10,11)])
In [58]: objects
Out[58]: 
array([<__main__.MyObj object at 0xb31b196c>,
       <__main__.MyObj object at 0xb31b116c>,
       <__main__.MyObj object at 0xb31b13cc>,
       <__main__.MyObj object at 0xb31b130c>], dtype=object)

`subset``类型的选择:

In [59]: [obj.par1 for obj in objects[1:-1]]
Out[59]: [3, 2]

另一个可以包含这样一个数组的类。这比定义数组子类要简单:

In [60]: class MyObjs(object):
   ....:     def __init__(self,anArray):
   ....:         self.data=anArray
   ....:     def par1(self):
   ....:         return [obj.par1 for obj in self.data]

In [61]: Obs = MyObjs(objects)
In [62]: Obs.par1()
Out[62]: [1, 3, 2, 10]

subset2 选择类型:

In [63]: Obs.par1()[1:-1]
Out[63]: [3, 2]

目前par1 是一种方法,但可以创建一个属性,允许Obs.par1[1:-1] 语法。

如果par1 返回一个数组而不是一个列表,索引会更强大。

如果MyObjs 有一个__getitem__ 方法,那么它可以被索引

Obs[1:-1]

该方法可以通过多种方式定义,但最简单的是将索引“切片”应用于“数据”:

def __getitem__(self, *args):
    # not tested
    return MyObjs(self.data.__getitem(*args))

我只关注语法,而不是效率。一般来说,一般对象的 numpy 数组不是很快或很强大。这样的数组基本上是指向对象的指针列表。

结构化数组和重新数组版本

另一种可能性是np.recarray。另一张海报只是询问他们的名字。它们本质上是结构化数组,其中字段可以作为属性访问。

使用结构化数组定义:

In [64]: dt = np.dtype([('par1', int), ('par2', int)])
In [66]: Obj1 = np.array([(1,2),(3,4),(2,3),(10,11)], dtype=dt)
In [67]: Obj1
Out[67]: 
array([(1, 2), (3, 4), (2, 3), (10, 11)], 
      dtype=[('par1', '<i4'), ('par2', '<i4')])
In [68]: Obj1['par1'][1:-1]
Out[68]: array([3, 2])
In [69]: Obj1[1:-1]['par1']
Out[69]: array([3, 2])

或作为recarray

In [79]: Objrec=np.rec.fromrecords(Obj1,dtype=dt)
In [80]: Objrec.par1
Out[80]: array([ 1,  3,  2, 10])
In [81]: Objrec.par1[1:-1]
Out[81]: array([3, 2])
In [82]: Objrec[1:-1].par1
Out[82]: array([3, 2])

【讨论】:

  • recarray 解决方案看起来棒极了!我需要稍微玩一下,但我认为这将是完美的。非常感谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-01
  • 2017-12-26
  • 2015-11-13
  • 2020-12-05
  • 2015-05-23
  • 2023-03-19
相关资源
最近更新 更多