2011-02-02 17 views
18

Estoy tratando de implementar una simulación para un modelo de celosía (celtice boltzmann) en Python. Cada sitio del enrejado tiene varias propiedades e interactúa con los sitios vecinos de acuerdo con ciertas reglas. Pensé que podría ser inteligente hacer una clase con todas las propiedades y hacer una grilla de instancias de esa clase. (Como estoy sin experiencia con Python, esto podría no ser una buena idea en absoluto, así que siéntase libre de comentar en mi enfoque.)numpy array of objects

Aquí está un ejemplo de juguete de lo que estoy haciendo

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

Ahora quiero tratar con una retícula 2D/3D (cuadrícula) de dichos sitios, así que traté de hacer lo siguiente (aquí hay una grilla 2D 3x3 como ejemplo, pero en la simulación necesitaría el orden de> 1000x1000X1000)

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

Ahora, el problema es que cada punto reticular se refiere a la misma instancia, por ejemplo

lattice[0,0].set_a(5) 

también establecerá el valor de reticulado [0,2] .a en 5. Este comportamiento no es deseado. Para evitar el problema que puede bucle sobre cada punto de la rejilla y asignar el elemento a elemento objetos, como

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

Pero, ¿existe una mejor manera (que no implican los bucles) para asignar objetos a una matriz multidimensional?

Gracias

+7

Si se trata de una matriz> 1000x1000X1000, _don't_ no use una matriz de objetos !! Utilizará grandes cantidades de memoria en comparación con el uso de una matriz numpy "normal". Las matrices de objetos no son lo que quieres aquí ... –

+4

por simulación Supongo que te refieres a la simulación de fluidos? Si es así, te recomendaré que reconsideres tu enfoque. Quizás los elementos de tus matrices deberían ser numéricos, para que puedas aprovechar todo el poder del álgebra lineal ;-). ¡Los procesos de propagación de partículas y colisión deben realizarse globalmente! Ningún enrejado de objeto local es capaz de manejar eso en cualquier tiempo de cálculo razonable. Sólo duros, no sé realmente lo que pretendes ;-). Gracias – eat

+0

@eat: estoy haciendo simulación de fluidos. Quería codificar una grilla genérica de sitios, donde todas las propiedades locales se recopilaban en una clase (la colisión es local en mi libro, no la propagación), pero supongo que tienes razón después de todo. Al menos bpowah me enseñó a vectorizar la función __init__ :) – jonalm

Respuesta

18

Puede vectorize función de la clase __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) 

Esto puede parecer más limpio pero no tiene ninguna ventaja de rendimiento sobre su solución de bucle. Las respuestas de comprensión de lista crean una lista pitón intermedia que causaría un golpe de rendimiento.

+0

Awesome tip. Gracias. – jonalm

+0

+1. Buen ejemplo para vectorizar. – tom10

+0

¿Qué sucede si 'Site' toma más de un parámetro? –

6

La pieza que falta para usted es que Python trata todo como una referencia. (Hay algunos "inmutables" objetos, cadenas y números y las tuplas, que son tratados más como valores.) Al hacer

lattice[:,:] = site(3) 

que están diciendo "Python: hacer un nuevo objeto site, y decirle a todos los elementos de lattice para apuntar a ese objeto ". Para ver que este es realmente el caso, imprimir la matriz para ver que las direcciones de memoria de los objetos son todos iguales:

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) 

La forma de bucle es una forma correcta de hacerlo. Con matrices numpy, esa puede ser tu mejor opción; con las listas de Python, también se puede utilizar una lista de comprensión:

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

Se puede utilizar una lista por comprensión con el numpy.array construcción:

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

Ahora cuando imprime lattice, es

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) 

para que pueda ver que cada objeto allí es único.

También debe tener en cuenta que los métodos "setter" y "getter" (por ejemplo, set_a) no son pitónicos. Es mejor configurar y obtener atributos directamente, y luego usar el decorador @property si REALMENTE necesita evitar el acceso de escritura a un atributo.

También tenga en cuenta que es estándar para las clases de Python que se escriban utilizando CamelCase, no en minúsculas.

+0

Muchas gracias por la entrada. Pero no sé si te entiendo correctamente; Para una instancia de clase A = sitio (4,5), ¿es mejor (más Pythonic) actualizar una variable con 'A.a = 6' que 'A.set_a (6)'? ¿Alguna referencia sobre buenas guías para la codificación pitonica? – jonalm

+0

Tenga cuidado con esto es 'Sitio' implementa '__len__'. –

3

No sé acerca mejor, pero como una alternativa a un conjunto explícito de bucles, se podría escribir

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

que debe trabajar cualquiera que sea la forma de la celosía.