2011-02-02 6 views
18

Я пытаюсь реализовать симуляцию для решетчатой ​​модели (решетка болтцмана) в Python. Каждый сайт решетки обладает рядом свойств и взаимодействует с соседними сайтами в соответствии с определенными правилами. Я подумал, что было бы разумно сделать класс со всеми свойствами и создать сетку экземпляров этого класса. (Как я неопытных с Python, это не может быть хорошей идеей, на всех, так что не стесняйтесь комментировать мой подход.)numpy array of objects

Вот это игрушка пример того, что я делаю

class site: 
    def __init__(self,a,...): 
     self.a = a 
     .... other properties ... 
    def set_a(self, new_a): 
     self.a = new_a 

Теперь я хочу иметь дело с 2D/3D решеткой (сеткой) таких сайтов, поэтому я попытался сделать следующее (здесь в качестве примера используется сетка 2D 3x3, но в моделировании мне понадобится порядок> 1000x1000X1000)

lattice = np.empty((3,3), dtype=object) 
lattice[:,:] = site(3) 

Проблема в том, что каждая точка решетки относится к одному экземпляру, например

lattice[0,0].set_a(5) 

также установит значение решетки [0,2] .a - 5. Это поведение нежелательно. Чтобы избежать этой проблемы я могу цикл по каждой точке сетки и присвоить элемент объектов элемента, как

for i in range(3): 
    for j in range(3): 
     lattice[i,j] = site(a) 

Но есть лучший способ (не включая петлю), чтобы присвоить объекты многомерного массива?

Благодаря

+7

Если вы имеете дело с массивом> 1000x1000X1000, _don't_ используйте массив объектов !! Он будет использовать вопиющие объемы памяти по сравнению с использованием «нормального» массива numpy. Объектные массивы здесь не то, что вам нужно ... –

+4

от моделирования Я думаю, вы подразумеваете моделирование жидкости? Если да, то я рекомендую вам пересмотреть свой подход. Возможно, элементы ваших массивов должны быть численными, чтобы вы могли использовать всю силу линейной алгебры ;-). Процессы распространения частиц и столкновения должны выполняться во всем мире! Никакая локальная решетка объектов не может справиться с этим в любое разумное время вычисления. Просто ударные, не знаю, на что вы стремитесь ;-). Спасибо – eat

+0

@eat: Я делаю жидкостное моделирование. Я хотел закодировать общую сетку сайтов, где все локальные свойства были собраны в классе (столкновение является локальным в моей книге, а не распространением tho), но, я думаю, вы в конце концов. По крайней мере, bpowah научил меня векторизовать функцию __init__ :) – jonalm

ответ

18

Вы можете vectorize__init__ функцию класса для:

import numpy as np 

class Site: 
    def __init__(self, a): 
     self.a = a 
    def set_a(self, new_a): 
     self.a = new_a 

vSite = np.vectorize(Site) 

init_arry = np.arange(9).reshape((3,3)) 

lattice = np.empty((3,3), dtype=object) 
lattice[:,:] = vSite(init_arry) 

Это может выглядеть чище, но не имеет преимущества по сравнению с вашим решением по замкнутому кругу. Рекомендации по составлению списка создают промежуточный список python, который может привести к поражению производительности.

+0

Удивительный наконечник. Благодарю. – jonalm

+0

+1. Хороший пример для векторизации. – tom10

+0

Что делать, если 'Site' принимает несколько параметров? –

6

Недостающий кусок для вас является то, что Python рассматривает все в качестве ссылки. (Есть некоторые «незыблемые» объекты, строки и числа и кортежи, которые рассматриваются скорее как ценности.) Когда вы

lattice[:,:] = site(3) 

вы говорите «Python: сделать новый объект site, и сказать каждому элементу lattice, чтобы указать на этот объект. " Для того, чтобы увидеть, что это действительно так, распечатать массив, чтобы увидеть, что адреса памяти объектов все равно:

array([[<__main__.Site object at 0x1029d5610>, 
     <__main__.Site object at 0x1029d5610>, 
     <__main__.Site object at 0x1029d5610>], 
     [<__main__.Site object at 0x1029d5610>, 
     <__main__.Site object at 0x1029d5610>, 
     <__main__.Site object at 0x1029d5610>], 
     [<__main__.Site object at 0x1029d5610>, 
     <__main__.Site object at 0x1029d5610>, 
     <__main__.Site object at 0x1029d5610>]], dtype=object) 

Путь петли один правильный способ сделать это. С помощью массивов numpy это может быть вашим лучшим вариантом; со списками Python, вы можете также использовать список понимание:

lattice = [ [Site(i + j) for i in range(3)] for j in range(3) ] 

Вы можете использовать список понимание с numpy.array строительства:

lattice = np.array([ [Site(i + j) for i in range(3)] for j in range(3) ], 
        dtype=object) 

Теперь, когда вы печатаете lattice, это

array([[<__main__.Site object at 0x1029d53d0>, 
     <__main__.Site object at 0x1029d50d0>, 
     <__main__.Site object at 0x1029d5390>], 
     [<__main__.Site object at 0x1029d5750>, 
     <__main__.Site object at 0x1029d57d0>, 
     <__main__.Site object at 0x1029d5990>], 
     [<__main__.Site object at 0x1029d59d0>, 
     <__main__.Site object at 0x1029d5a10>, 
     <__main__.Site object at 0x1029d5a50>]], dtype=object) 

, чтобы вы могли видеть, что каждый объект в нем уникален.

Следует также отметить, что методы «сеттер» и «приемник» (например, set_a) являются непитоновыми. Лучше устанавливать и получать атрибуты напрямую, а затем использовать декоратор @property, если вам действительно нужно предотвратить доступ на запись к атрибуту.

Также обратите внимание, что это стандартно для классов Python, которые должны быть написаны с использованием CamelCase, а не в нижнем регистре.

+0

Большое спасибо за ввод. Но я не знаю, правильно ли я вас понимаю; Для экземпляра класса A = site (4,5), лучше ли (более Pythonic) обновлять переменную с помощью «A.a = 6», чем «A.set_a (6)»? Любые ссылки на хорошие руководства по питоническому кодированию? – jonalm

+0

Будьте осторожны с этим 'Site' реализует' __len__'. –

3

Я не знаю, о лучше, но в качестве альтернативы явного набора петель, вы можете написать

lattice = np.empty((3,3), dtype=object) 
lattice.flat = [site(3) for _ in lattice.flat] 

, который должен работать независимо от формы решетки.