从NumPy对象数组中获取属性

9

假设我有一个名为Star的类,它具有一个属性color。我可以使用star.color获取颜色。

但是如果我有一个由这些Star对象组成的NumPy数组,那么获取颜色的首选方法是什么?

我可以使用以下方法:

colors = np.array([s.color for s in stars])

但这是最好的方法吗?如果我可以像其他一些语言那样只需执行colors = star.colorcolors = star->color等操作,那就太好了。在numpy中有没有简单的方法可以做到这一点?


可能是重复的问题:numpy对象数组 - YXD
3个回答

9

您想要的最接近的东西是使用一个recarray而不是Python对象的ndarray

num_stars = 10
dtype = numpy.dtype([('x', float), ('y', float), ('colour', float)])
a = numpy.recarray(num_stars, dtype=dtype)
a.colour = numpy.arange(num_stars)
print a.colour

打印输出
[ 0.  1.  2.  3.  4.  5.  6.  7.  8.  9.]

使用Python对象的NumPy数组通常比使用普通的list效率低,而recarray以更有效的格式存储数据。


很棒。这使它们就像我想要的IDL结构数组一样。如果我已经定义了一个常规的Python类,我该如何使用它?有简单的方法吗? - Dave31415
@Dave31415:IDL?所以您是一位天文学家,或者有没有人在天文学之外真的在使用它?至于您的问题:如果没有看到类定义,这有点难以回答。使用NumPy时,通常不希望“方法”仅对单个记录进行操作,而是希望函数可以一次对整个数组进行操作。因此,您需要将方法向量化。 - Sven Marnach
试图成为一名前天文学家。所以我想你的意思是说,对象数组不是numpy的首选数据结构。那么什么是呢?我可以创建属性为numpy数组的类。这是更好的方法吗?听起来不像是我想要的。 - Dave31415
@Dave31415:我现在有点困惑。我的意思是,在使用NumPy时,最好使用NumPy的recarray而不是Python对象的NumPy数组。如果一个普通的Star实例列表可以胜任你的工作,那么你也可以使用普通的列表。再次强调,如果不了解你的用例,很难给出建议。 - Sven Marnach
1
首先,我的类可能没有方法。它们基本上就像C或Python中的“结构”。如果是这样,那么recarray似乎很适合这种情况,对吗?在这种情况下,你是否需要定义类或直接定义dtype? - Dave31415
@Dave31415:在这种情况下,您不需要定义一个类,使用recarray就可以了。 - Sven Marnach

4
你可以使用numpy.fromiter(s.color for s in stars)(注意没有方括号)。这样可以避免创建中间列表,如果你正在使用numpy,我想你可能会关心它。感谢@SvenMarnach和@DSM进行了以下更正。

2
很遗憾,这样做不起作用:你会得到类似 array(<generator object <genexpr> at 0x9cff34c>, dtype=object) 的东西。(我曾经在我的代码中有一个错误,最终是因为我认为这样做会起作用。) - DSM
1
你需要使用 numpy.fromiter() 来完成这个任务。 - Sven Marnach
3
注意:要在最新版本的numpy中使其工作,您需要使用numpy.fromiter((s.color for s in stars), float)。另外,添加count=len(stars)将使其在处理长数组时更加高效。 - Danica

0
如果star是一种更复杂的class,这里提供了一种通过在顶部使用辅助class来获取和设置属性的方法。
import numpy as np

class star:
    def __init__(self, mass=1, radius=1):
        self.mass = mass
        self.radius = radius

class Stars(list):

    __getattr__ = lambda self, attr: np.array([getattr(s, attr) for s in self])

    def __setattr__(self, attr, vals):
        if hasattr(vals, '__len__'):
            [s.__setattr__(attr, val) for (s,val) in zip(self,vals)]
        else:
            [s.__setattr__(attr, vals) for s in self]


s1 = star(1, 1.1)
s2 = star(2, 3)

S = Stars([s1, s2])

print(S.mass)
print(S.radius)

S.density = S.mass / S.radius**3
print(S.density)
print(s1.density)

当然,如果这个类可以重新实现为一个recarray,那么它应该会更有效率。然而,这样的重新实现可能是不可取的。

请注意,外部计算,如密度计算,仍然是向量化的。通常这些计算可能成为瓶颈,而不是设置和获取属性。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接