2015-10-30 72 views
3

我非常喜歡namedtuple集合的功能。具體而言,我喜歡它對於二維空間中的點是多麼有用。Namedtuple in Numpy

In : from collections import namedtuple 

In : Point = namedtuple('Point', ['x', 'y']) 

In : p = Point(1,2) 

In : p.x 
Out: 1 

In : p.y 
Out: 2 

我認爲這比列舉的第一個和第二個條目清晰得多。我想知道是否有辦法讓Point成爲一個數組。例如

In: p1 = Point(1,2) 
In: p2 = Point(3,4) 
In: (p1+p2).x 
Out: 4 

和numpy類似的很好的功能。換句話說,我認爲我希望Point成爲numpy的子類?我可以這樣做嗎?如何?

+3

'np.recarray'是結構化數組,允許您以字段的形式訪問字段 - 對命名的等價物進行排序。但是你不能輕易地在結構化數組上執行2D數學。 – hpaulj

回答

4

point_type這樣的結構化數組沒有定義涉及多個字段的數學運算。

隨着樣品從https://stackoverflow.com/a/33455682/901925

In [470]: point_type = [('x', float), ('y', float)] 
In [471]: points = np.array([(1,2), (3,4), (5,6)], dtype=point_type) 
In [472]: points 
Out[472]: 
array([(1.0, 2.0), (3.0, 4.0), (5.0, 6.0)], 
     dtype=[('x', '<f8'), ('y', '<f8')]) 
In [473]: points[0]+points[1] 
... 
TypeError: unsupported operand type(s) for +: 'numpy.void' and 'numpy.void' 

相反,我可以創建一個二維數組,然後將其視爲point_type - 設置DataBuffer佈局將是相同的:

In [479]: points = np.array([(1,2), (3,4), (5,6)],float) 
In [480]: points 
Out[480]: 
array([[ 1., 2.], 
     [ 3., 4.], 
     [ 5., 6.]]) 
In [481]: points.view(point_type) 
Out[481]: 
array([[(1.0, 2.0)], 
     [(3.0, 4.0)], 
     [(5.0, 6.0)]], 
     dtype=[('x', '<f8'), ('y', '<f8')]) 
In [482]: points.view(point_type).view(np.recarray).x 
Out[482]: 
array([[ 1.], 
     [ 3.], 
     [ 5.]]) 

我可以做數學橫跨行,並繼續以點的形式查看結果:

In [483]: (points[0]+points[1]).view(point_type).view(np.recarray) 
Out[483]: 
rec.array([(4.0, 6.0)], 
     dtype=[('x', '<f8'), ('y', '<f8')]) 
In [484]: _.x 
Out[484]: array([ 4.]) 
In [485]: points.sum(0).view(point_type) 
Out[485]: 
array([(9.0, 12.0)], 
     dtype=[('x', '<f8'), ('y', '<f8')]) 

我也可以與point_type開始,並認爲它是2D的數學,然後視圖它回

pdt1=np.dtype((float, (2,))) 
In [502]: points 
Out[502]: 
array([(1.0, 2.0), (3.0, 4.0), (5.0, 6.0)], 
     dtype=[('x', '<f8'), ('y', '<f8')]) 
In [503]: points.view(pdt1) 
Out[503]: 
array([[ 1., 2.], 
     [ 3., 4.], 
     [ 5., 6.]]) 
In [504]: points.view(pdt1).sum(0).view(point_type) 
Out[504]: 
array([(9.0, 12.0)], 
     dtype=[('x', '<f8'), ('y', '<f8')]) 

因此,有可能查看和在陣列上作爲2d和作爲recarray操作。要是漂亮或有用,它可能需要在用戶定義的類中進行埋設。

另一種選擇從recarray類中挑選想法的選項。在它的核心,它只是一個結構化數組,其中包含專門的__getattribute__(和setattribute)方法。該方法首先檢索正常的數組方法和屬性(例如x.shapex.sum)。然後它會嘗試在定義的字段名中對attr進行罰款。

def __getattribute__(self, attr): 
    try: 
     return object.__getattribute__(self, attr) 
    except AttributeError: # attr must be a fieldname 
     pass 
    fielddict = ndarray.__getattribute__(self, 'dtype').fields 
    try: 
     res = fielddict[attr][:2] 
    except (TypeError, KeyError): 
     raise AttributeError("record array has no attribute %s" % attr) 
    return self.getfield(*res) 
    ... 

points.view(np.recarray).x變得points.getfield(*points.dtype.fields['x'])

一種替代方法將是從namedtuple/usr/lib/python3.4/collections/__init__.py)借用,並定義xy性質,這將索引2D陣列的[:,0][:,1]列。 將這些屬性添加到np.matrix的子類可能是最容易的,讓該類確保大多數數學結果是2d。

1

恐怕不是完全可能的,因爲numpy類和namedtuple類不能一起子類化,但你可以做的是在它們之間進行轉換。

>>> Point = namedtuple('Point', ['x', 'y']) 
>>> p1 = Point(1,2) 
>>> p2 = Point(3,4) 
>>> np.array(p1) 
array([1, 2]) 
>>> np.array(p1) + np.array(p2) 
array([4, 6]) 
>>> Point(*(np.array(p1) + np.array(p2))).x 
4 
2

可以使用得到幾分相似的功能numpy的的structured arrays

In [36]: import numpy as np 
    ...: point_type = [('x', float), ('y', float)] 
    ...: points = np.array([(1,2), (3,4), (5,6)], dtype=point_type) 

In [37]: points[2] 
Out[37]: (5.0, 6.0) 

In [38]: points['x'] 
Out[38]: array([ 1., 3., 5.]) 

使用屬性附加傷害接入(例如,使用points.x),以使可用的所有字段甚至有可能由結構數組轉換成recarray

In [39]: pts = points.view(np.recarray) 

In [40]: pts['x'] 
Out[40]: array([ 1., 3., 5.]) 

In [41]: pts.x 
Out[41]: array([ 1., 3., 5.]) 

In [42]: pts[2] 
Out[42]: (5.0, 6.0) 

請注意,重新排列顯然有一些性能問題,並可能有點煩人的使用。您可能還想查看pandas庫,該庫還允許按屬性訪問字段,並且不存在重新數組的問題。