2010-11-26 14 views
6

Python question. Estoy generando una gran variedad de objetos, que solo necesito hacer una pequeña muestra aleatoria. En realidad, generar los objetos en cuestión lleva un tiempo, por lo que me pregunto si sería posible saltear de algún modo los objetos que no necesitan generar y solo crear explícitamente aquellos objetos que se han muestreado.Lazy muestra resultados aleatorios en python

En otras palabras, ahora tengo

a = createHugeArray() 
s = random.sample(a,len(a)*0.001) 

que es más bien un desperdicio. Preferiría algo más flojo como

a = createArrayGenerator() 
s = random.sample(a,len(a)*0.001) 

No sé si esto funciona. La documentación de random.sample no está muy clara, aunque menciona xrange como muy rápida, lo que me hace pensar que podría funcionar. Convertir la creación de la matriz en un generador sería un poco laborioso (mi conocimiento de los generadores está muy oxidado), así que quiero saber si esto funciona con antelación. :)

Una alternativa que puedo ver es hacer una muestra aleatoria a través de xrange, y solo generar aquellos objetos que son realmente seleccionados por índice. Sin embargo, eso no es muy claro, porque los índices generados son arbitrarios e innecesarios, y necesitaría una lógica bastante hacky para apoyar esto en mi método generateHugeArray.

Para puntos de bonificación: ¿cómo funciona random.sample en realidad? Especialmente, ¿cómo funciona si no se conoce el tamaño de la población por adelantado, como con generadores como xrange?

+0

Qué tipo de datos proporciona generateHugeArray() crear? –

+0

Actualmente, una simple lista de objetos antiguos, con todas las permutaciones posibles de un cierto espacio de estado del cual solo necesito una pequeña muestra. Para un juego de lobo cuántico en particular, consulte https://github.com/verhoevenv/bra -ket-wolf/blob/master/multiverse.py # L87 donde primero creo todos los universos y luego creo universos filtrados. – Verhoevenv

Respuesta

2

No parece una manera que evite encontrar la manera de los índices se asignan a sus permutaciones. Si no sabes esto, ¿cómo crearías un objeto aleatorio de tu matriz? Puede usar el truco usando xrange() que usted mismo sugirió, o implementar una clase que defina los métodos __getitem__() y __len__() y pasar el objeto de esta clase como argumento population al random.sample().

Otros comentarios:

  • Conversión createHugeArray() en un generador que no van a comprar cualquier cosa - random.sample() simplemente no funcionará más. Necesita un objeto compatible con len().

  • Así que hace necesita conocer el número de elementos en la población desde el principio.

  • El implementation presenta dos algoritmos diferentes y elige el que utilizará menos memoria. Para relativamente pequeño k (es decir, en el caso que nos ocupa) simplemente guardará los índices ya elegidos en un set y hará una nueva elección aleatoria si golpea uno de ellos.

Editar: Un enfoque completamente diferente sería iterar sobre todas las permutaciones de una vez y decidir por cada permutación si debe ser incluido. Si el número total de permutaciones es n y que le gustaría para seleccionar k de ellos, se podría escribir

selected = [] 
for i in xrange(n): 
    perm = nextPermutation() 
    if random.random() < float(k-len(selected))/(n-i): 
     selected.append(perm) 

Esto sería elegir exactamente k permutaciones al azar.

+0

Ah. Interesante. En mi caso particular, __len__ debería ser posible con algunos combinatorios, __getitem__ necesitará algo de reflexión. ¡Gracias! – Verhoevenv

+1

Pensándolo bien: si utilizo el truco de xrange(), entonces clasifico los índices de muestra, solo tengo que recorrer todas las permutaciones exactamente una vez, simplificando mucho todo. Además, abucheo a los métodos de Markdown y Python. :) – Verhoevenv

+0

Su último algoritmo supone que 'permutación' tiene' __len __() '(' n' es conocido) y '__getitem __()' (trivial para implementar en términos de 'permutación (i)'). ¿Por qué podría uno usarlo en lugar de 'random.sample()'? – jfs

0

Se puede crear una lista de índices de matriz con la muestra y luego generar los objetos de acuerdo a los resultados:

def get_object(index): 
    return MyClass(index) 

o algo así. A continuación, utilice muestra para generar los índices que necesita y llamar a esta función con esos índices:

objs = map(get_object, random.sample(range(length), 0.001 * length)) 

Esta es una pequeña indirecta puesto que en ella sólo elige de una lista de posibles índices de matriz.

+0

Eso es lo que quise decir con mi alternativa, sí, pero su explicación es probablemente más clara. :) Como dije, no es una solución muy Pythonic IMO, pero si no aparece nada más, supongo que está bien. – Verhoevenv

0

explican el funcionamiento de random.sample,

random.sample(container, k) volverán número k de los valores al azar desde el contenedor. Debido a que un generador es iterable, como listas, tuplas y las claves o valores en los dictados, recorrerá el contenedor y luego tomará estos elementos aleatorios.

p. Ej. random.sample(xrange(111),4) volverá algo así como [33,52,111,1]k = 4 significado 4 números aleatorios desde el generador hasta 111. xrange

+0

Sí, entiendo la documentación, pero no veo qué tipo de algoritmo están usando para eso. Pero creo que solo puedo echarle un vistazo a la fuente. – Verhoevenv

0

Supongo que la función createHugeArray() contiene un fragmento de código que se repite una vez para cada objeto que se crea. Y supongo que los objetos se generan a partir de algún tipo de valor inicial o semilla, en cuyo caso createHugeArray() es como la siguiente: (. Solía ​​listas no matrices, pero se entiende la idea)

def createHugeArray(list_of_seeds): 
    huge_array = []     
    for i in list_of_seeds: 
    my_object = makeObject(i) 
    huge_array.append(my_object)   
    return huge_array 

Para realizar el muestreo aleatorio antes de crear realmente los objetos, simplemente agregue una línea que genere un número aleatorio, y luego solo cree el objeto si el número aleatorio está por debajo de un cierto umbral. Digamos que solo quieres un objeto en mil. random.randint (0,999) da un número de 0 a 999; por lo tanto, solo genere un objeto si obtiene cero. El código anterior se convierte en:

import random 

def createHugeArray(list_of_seeds): 
    huge_array = [] 

    for i in list_of_seeds: 
    die_roll = random.randint(0,999) 

    if(die_roll == 0): 
     my_object = makeObject(i) 
     huge_array.append(my_object) 
    return huge_array 

Por supuesto, si mi suposición acerca de cómo funciona el código es incorrecto, entonces esto es inútil, en cuyo caso lo siento y buena suerte :-)

Cuestiones relacionadas