Mezclar aleatoriamente líneas de 3 millones de líneas de archivo

Todo está en el título. Me pregunto si alguien conoce una forma rápida y razonable de memoria que permita mezclar aleatoriamente todas las líneas de un archivo de 3 millones de líneas. Supongo que no es posible con un simple comando vim, por lo que cualquier script simple que use Python. Probé con python utilizando un generador de números aleatorios, pero no conseguí encontrar una salida simple.

import random with open('the_file','r') as source: data = [ (random.random(), line) for line in source ] data.sort() with open('another_file','w') as target: for _, line in data: target.write( line ) 

Deberias hacer eso. 3 millones de líneas cabrán en la memoria de la mayoría de las máquinas a menos que las líneas sean GRANDES (más de 512 caracteres).

Toma solo unos segundos en Python:

 >>> import random >>> lines = open('3mil.txt').readlines() >>> random.shuffle(lines) >>> open('3mil.txt', 'w').writelines(lines) 

Acabo de probar esto en un archivo con 4.3M de líneas y el comando ‘shuf’ en Linux fue el más rápido. Úsalo así:

 shuf huge_file.txt -o shuffled_lines_huge_file.txt 

Tardó 2-3 segundos en terminar.

En muchos sistemas, el comando sort shell toma -R para aleatorizar su entrada.

Aquí hay otra versión

En la shell, usa esto.

 python decorate.py | sort | python undecorate.py 

decorar.py

 import sys import random for line in sys.stdin: sys.stdout.write( "{0}|{1}".format( random.random(), line ) ) 

undecorate.py

 import sys for line in sys.stdin: _, _, data= line.partition("|") sys.stdout.write( line ) 

Utiliza casi ningún recuerdo.

Es lo mismo que el Sr. Kugelman, pero usando la interfaz python incorporada de vim:

 :py import vim, random as r; cb = vim.current.buffer ; l = cb[:] ; r.shuffle(l) ; cb[:] = l 

Si no desea cargar todo en la memoria y ordenarla allí, debe almacenar las líneas en el disco mientras realiza una clasificación aleatoria. Eso será muy lento.

Aquí hay una versión muy simple, estúpida y lenta. Tenga en cuenta que esto puede tomar una cantidad sorprendente de espacio en disco, y será muy lento. Lo corrí con 300.000 líneas, y tarda varios minutos. 3 millones de líneas podrían muy bien tardar una hora. Entonces: hazlo en la memoria. De Verdad. No es tan grande

 import os import tempfile import shutil import random tempdir = tempfile.mkdtemp() print tempdir files = [] # Split the lines: with open('/tmp/sorted.txt', 'rt') as infile: counter = 0 for line in infile: outfilename = os.path.join(tempdir, '%09i.txt' % counter) with open(outfilename, 'wt') as outfile: outfile.write(line) counter += 1 files.append(outfilename) with open('/tmp/random.txt', 'wt') as outfile: while files: index = random.randint(0, len(files) - 1) filename = files.pop(index) outfile.write(open(filename, 'rt').read()) shutil.rmtree(tempdir) 

Otra versión sería almacenar los archivos en una base de datos SQLite y extraer las líneas aleatoriamente de esa base de datos. Eso probablemente va a ser más rápido que esto.

Aquí hay otra forma de usar random.choice , esto también puede proporcionar un cierto alivio gradual de la memoria, pero con un Big-O peor 🙂

 from random import choice with open('data.txt', 'r') as r: lines = r.readlines() with open('shuffled_data.txt', 'w') as w: while lines: l = choice(lines) lines.remove(l) w.write(l) 

El siguiente Vimscript se puede usar para intercambiar líneas:

 function! Random() let nswaps = 100 let firstline = 1 let lastline = 10 let i = 0 while i <= nswaps exe "let line = system('shuf -i ".firstline."-".lastline." -n 1')[:-2]" exe line.'d' exe "let line = system('shuf -i ".firstline."-".lastline." -n 1')[:-2]" exe "normal! " . line . 'Gp' let i += 1 endwhile endfunction 

Seleccione la función en modo visual y escriba :@" luego ejecútela con :call Random()