2011-06-20 6 views
7

Estoy buscando una manera de seleccionar aleatoriamente un archivo de un árbol de directorios de manera que cada archivo individual tenga exactamente la misma probabilidad de ser elegido como todos los otros archivos. Por ejemplo, en el siguiente árbol de archivos, cada archivo debe tener una probabilidad del 25% de ser elegido:Selección aleatoria de un archivo de un árbol de directorios de manera completamente equitativa

  • /some/padre/dir/
    • foo.jpg
    • sub_dir/
      • bar.jpg
      • Baz.jpg
      • another_sub/
        • qux.png

Mi solución provisional que estoy usando mientras codificar el resto de la aplicación es tener una función de este modo:

def random_file(dir): 
    file = os.path.join(dir, random.choice(os.listdir(dir))); 
    if os.path.isdir(file): 
     return random_file(file) 
    else: 
     return file 

Sin embargo, esto sesga, obviamente, el resultados dependiendo de dónde están en el árbol y cuántos hermanos están al lado de ellos en su directorio para que terminen con las siguientes probabilidades de ser seleccionado:

  • /some/padre/dir/
    • foo.jpg - 50%
    • sub_dir/(50%)
      • Bar.jpg - 16,6%
      • Baz.jpg - 16,6%
      • another_sub/(16,6%)
        • qux.png - 16,6%

El contexto de la función es una aplicación en la rotación de fondo que estoy escribiendo, por lo que la capacidad de filtrar las extensiones de archivos no deseados de estar en los resultados sería un bono (a pesar de que podría simplemente vigor que al elegir de nuevo si no es el tipo de archivo que quiero ... que se vuelve complicado si hay una gran cantidad de archivos del tipo "incorrecto", sin embargo).

+1

¿Quieres todas las imágenes estén en un "ejecutar" una vez? Podría simplemente iterar a través de todos los archivos, agregarlos a una lista y mezclar esa lista una vez. – Jacob

+0

@Graham, +1 buena pregunta – nsd

Respuesta

12

Sólo se pueden seleccionar todos los archivos con la misma probabilidad si se conoce el número total de archivos de antemano, por lo que necesita para crear una lista completa primero:

files = [os.path.join(path, filename) 
     for path, dirs, files in os.walk(dir) 
     for filename in files 
     if not filename.endswith(".bak")] 
return random.choice(files) 
+0

Se ve bien, gracias! Al final fui con una ligera alteración para obtener una lista blanca de extensiones de archivo permitidas en lugar de una sola no permitida, agregando un argumento de "extensiones" al método y luego cambiando el if a: 'if os.path.splitext (archivo) [1] en extensiones ' –

+0

Esto exagera un poco el caso. Es completamente posible seleccionar de todos los archivos con la misma probabilidad sin saber primero el número total de archivos. –

+0

@Michael: ¿Te importa explicar esto un poco más? –

3

¿Por qué no solo almacena todos los archivos en una lista (con su ruta) y elige de eso?

3

Como otras respuestas mencionadas, se puede elegir uno al azar, ya sea mediante la recopilación de todas las rutas de archivos en una lista y usando random.choice. Alternativamente, es posible realizar una selección en línea que no use memoria extra al usar más números aleatorios.Para los artículos n, es igual selección entre los primeros artículos n-1, o el n elemento con probabilidad 1/n. Esto se puede calcular al ejecutar la lista de posibilidades.

Se puede recorrer todos los nombres de archivo con:

def recursive_files(dir): 
    for path, _, fnames in os.walk(dir): 
     for fname in fnames: 
      yield os.path.join(path, fname) 

Y hacer la selección en línea con esto:

import random 
def online_choice(iterable): 
    for n, x in enumerate(iterable, 1): 
     if random.randrange(n) == 0: 
      pick = x 
    return pick 
+0

Esto parece muy interesante: supongo que la victoria aquí para mí es que puedo dejar de recurrir a los directorios tan pronto como encuentre uno que "gane". Si es así, ¿por qué no tienes 'return x' dentro del if? Seguramente la forma en que lo tienes, simplemente continúa iterando independientemente del hecho de que ya ha sido elegido. Desafortunadamente, la "selección en línea" es previsiblemente mala como una búsqueda en Google: ¿hay una buena referencia en línea que pueda leer más sobre esto? –

+0

@ Graham Todo lo contrario, tienes que pasar por todos. Solo es mejor en términos de uso de memoria. Definitivamente utilice el programa de Sven para uso práctico. Para obtener más información sobre este enfoque, sugeriría buscar "muestreo de yacimientos". Más interesante es ver este enfoque como una aplicación de probabilidades condicionales de una manera similar a la programación dinámica. Lamentablemente, no tengo una referencia para esa vista más general; Preferiría poder recordar dónde lo leí por primera vez, pero fue hace muchos años. –

+0

Ciertamente encontré esto (y el puntero al "muestreo de yacimientos") interesante, entonces +1. –

Cuestiones relacionadas