как построить массив объектов numpy (объект включает в себя другой массив) - PullRequest
0 голосов
/ 04 апреля 2020

На другом языке мне нравится использовать массивы объектов, содержащие каждый объект класса, и каждый объект очень эффективно доступен через массив объектов. Я пытаюсь сделать то же самое с Python и numpy. Каждый объект имеет несколько членов различного типа, включая сам массив numpy. Таким образом, в конечном итоге мне нужен массив объектов всех объектов, к которым можно эффективно получить доступ и вернуть любой член, наиболее важно массив членов.

Я пробовал что-то вроде этого:

class TestClass():
    objectarray=np.empty([10, 1], dtype=np.object)  ## static array holding all class objects
    def __init__(self,name,position):
        self.name=name
        self.position=position
        self.intmember= 5
        self.floatmember=3.4
        self.arraymember= np.zeros([5, 5])  ## another array which is a member of the class
        TestClass.objectarray[position]=self

затем:

testobj1 = TestClass('test1',5)  ## create a new object and add it at position 5 into the object array

Кажется, что-то случилось

TestClass.objectarray

array([[None],
       [None],
       [None],
       [None],
       [None],
       [<__main__.TestClass object at 0x000000EF214DC308>],
       [None],
       [None],
       [None],
       [None]], dtype=object)

Однако это не работает:

a= TestClass.objectarray[5]
a.intmember
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-40-dac52811af13> in <module>
      1 a= TestClass.objectarray[5]
----> 2 a.intmember

AttributeError: 'numpy.ndarray' object has no attribute 'intmember'

Что я делаю неправильно? Помните, что это должен быть эффективный механизм внутри большого l oop

(PS (я знаю, что мог бы использовать список объектов, но перебор списков слишком медленный в моем тестировании. Поэтому я хочу использовать numpy массивы, в идеале дополненные numba)

1 Ответ

2 голосов
/ 04 апреля 2020
In [1]: class TestClass(): 
   ...:     objectarray=np.empty([10, 1], dtype=np.object)  ## static array holding all class o
   ...: bjects 
   ...:     def __init__(self,name,position): 
   ...:         self.name=name 
   ...:         self.position=position 
   ...:         self.intmember= 5 
   ...:         self.floatmember=3.4 
   ...:         self.arraymember= np.zeros([5, 5])  ## another array which is a member of the c
   ...: lass 
   ...:         TestClass.objectarray[position]=self 
   ...:                                                                                        
In [2]: testobj1 = TestClass('test1',5)  

Как определено testobj1 имеет атрибут intmember:

In [3]: testobj1                                                                               
Out[3]: <__main__.TestClass at 0x7fceba8acef0>
In [4]: testobj1.intmember                                                                     
Out[4]: 5

Этот объект также поместил себя в массив класса:

In [5]: TestClass.objectarray                                                                  
Out[5]: 
array([[None],
       [None],
       [None],
       [None],
       [None],
       [<__main__.TestClass object at 0x7fceba8acef0>],
       [None],
       [None],
       [None],
       [None]], dtype=object)

Поскольку это Для двумерного массива мы используем двумерную индексацию для ссылки на элемент:

In [8]: TestClass.objectarray[5,0]                                                             
Out[8]: <__main__.TestClass at 0x7fceba8acef0>
In [9]: TestClass.objectarray[5,0].intmember                                                   
Out[9]: 5

Доступ с помощью [5] только для индексации первого измерения; объект все еще встроен в массив:

In [10]: TestClass.objectarray[5]                                                              
Out[10]: array([<__main__.TestClass object at 0x7fceba8acef0>], dtype=object)

Не думаю, что создание массива (10,1) помогло; простой 1d был бы так же хорош:

 objectarray=np.empty([10], dtype=np.object) 

или просто список:

In [12]: class TestClass(): 
    ...:     objectarray=[None]*10 
    ...:     def __init__(self,name,position): 
    ...:         self.name=name 
    ...:         self.position=position 
    ...:         self.intmember= 5 
    ...:         self.floatmember=3.4 
    ...:         self.arraymember= np.zeros([5, 5])  ## another array which is a member of the 
    ...: class 
    ...:         TestClass.objectarray[position]=self 
    ...:                                                                                       
In [13]: testobj1 = TestClass('test1',5)                                                       
In [14]: testobj1                                                                              
Out[14]: <__main__.TestClass at 0x7fceac25f5c0>
In [15]: testobj1.objectarray                                                                  
Out[15]: 
[None,
 None,
 None,
 None,
 None,
 <__main__.TestClass at 0x7fceac25f5c0>,
 None,
 None,
 None,
 None]
In [16]: testobj1.objectarray[5]                                                               
Out[16]: <__main__.TestClass at 0x7fceac25f5c0>
In [17]: testobj1.objectarray[5].intmember                                                     
Out[17]: 5

Доступ к элементу списка быстрее, чем делать то же самое для массива объектов:

In [18]: timeit Out[5][5,0].intmember                                                          
149 ns ± 0.00964 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)
In [19]: timeit Out[15][5].intmember                                                           
90.5 ns ± 0.0478 ns per loop (mean ± std. dev. of 7 runs, 10000000 loops each)

frompyfun c

Я рекомендовал np.frompyfunc в качестве удобного, если не быстрого, способа доступа к массивам типа d объекта или иной работы с ними. Например,

Функция для извлечения значения intmember, если оно присутствует:

In [28]: def getval(item): 
    ...:     try: 
    ...:         return item.intmember 
    ...:     except AttributeError: 
    ...:         return None         

, примененное к массиву объектов:

In [29]: np.frompyfunc(getval,1,1)(Out[5])                                                     
Out[29]: 
array([[None],
       [None],
       [None],
       [None],
       [None],
       [5],
       [None],
       [None],
       [None],
       [None]], dtype=object)

, примененное к списку:

In [30]: np.frompyfunc(getval,1,1)(Out[15])                                                    
Out[30]: 
array([None, None, None, None, None, 5, None, None, None, None],
      dtype=object)

время:

In [31]: timeit np.frompyfunc(getval,1,1)(Out[15])                                             
14.6 µs ± 187 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
In [32]: timeit np.frompyfunc(getval,1,1)(Out[5])                                              
9.53 µs ± 54 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
In [33]: [getval(i) for i in Out[15]]                                                          
Out[33]: [None, None, None, None, None, 5, None, None, None, None]
In [34]: timeit [getval(i) for i in Out[15]]                                                   
6.53 µs ± 93.5 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

понимание списка в списке самое быстрое.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...