2011-01-06 1 views
20

Alles ist im Titel. Ich frage mich, ob jemand einen schnellen und vernünftigen Speicherbedarf kennt, um alle Zeilen einer 3 Millionen Zeilen Datei zufällig zu mischen. Ich denke, es ist nicht möglich mit einem einfachen vim-Befehl, also ein einfaches Skript mit Python. Ich habe mit Python versucht, indem ich einen Zufallszahlengenerator benutzt habe, aber es ist mir nicht gelungen, einen einfachen Ausweg zu finden.Mischen Sie nach dem Zufallsprinzip Linien von 3 Millionen Zeilen Datei

+2

Sie geben [diese Frage] (http://stackoverflow.co m/questions/1287567/c-is-using-random-and-orderby-a-good-shuffle-algorithm) für einige Ideen. –

+1

"hat es nicht geschafft, einen einfachen Ausweg zu finden." "Ja wirklich?" Bitte poste den Code, der zu komplex wurde. –

+0

Sollte gesagt haben, "hat es nicht geschafft, einen Ausweg zu finden". Ich bin ziemlich neu mit Python, also kenne ich nur einige Befehle. Was ich anstrebte war, alles in einen Vektor zu setzen, eine zufällige Zahl zwischen 1 und 3 Millionen zu wählen, diese Linie herauszunehmen und von vorne mit einer neuen Zufallszahl mit einer zusätzlichen Bedingung zu beginnen, die die vorherigen Zufallszahlen ausschließt. Etc. Daher meine Frage für eine einfache Art und Weise (die Sie und andere zur Verfügung gestellt). Ich werde deine akzeptieren, da du die meisten Stimmen hast. Danke an alle, aber ich habe viel gelernt! – Nigu

Antwort

21
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) 

Das sollte es tun. 3 Millionen Zeilen passen in den Speicher der meisten Maschinen, es sei denn, die Zeilen sind RIESIG (über 512 Zeichen).

+0

3 Millionen Zeile mit durchschnittlich 80 Zeichen pro Zeile wird etwa 240M Bytes sein, das ist riesig für das Laden einer Datei im Speicher. –

+1

@ Vikram.exe. Nicht wirklich. Diese Maschine verfügt über 4 GB Arbeitsspeicher. 240M ist nichts. –

+0

@ S.Lott, yeah Ich stimme zu, es ist nichts, aber ich habe mich nur gefragt, ob wir es irgendwie (mit wenig Aufwand) tun können, ohne die ganze Datei in den Speicher zu laden. –

34

dauert nur wenige Sekunden in Python:

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

Dies funktioniert nicht. 'shuffle' funktioniert nur für relativ kleine Listen, ungefähr 2.000 Artikel oder weniger. Je nach Länge der Zeilen kann es auch keine "vernünftigen" Speicheranforderungen geben. Nun, wenn Sie nur "zufällige" Reihenfolge benötigen, ist dies vielleicht gut genug. Aber vielleicht nicht. Weitere Informationen finden Sie unter http://stackoverflow.com/questions/3062741/maximal-length-of-list-to-shuffle-with-python-random-shuffle. –

+6

Es funktioniert sicherlich * und funktioniert gut. Dass es nur 2 ** 19937 Permutationen erzeugen kann, ist trivial, grenzt an irrelevant. Jeder RNG-basierte Shuffle hat dieselbe "Begrenzung". –

+2

Wie ist eine 'sort()' -basierte Lösung besser als 'shuffle()'? Es vermeidet dieses vermeintliche Problem nicht. –

3

Auf vielen Systemen der sort Shell-Befehl nimmt -R seinen Eingang randomisieren.

+2

Beachten Sie, dass die Option "-R" immer noch identische Zeilen sortiert, was möglicherweise nicht das gewünschte Verhalten ist. –

+3

'shuf' wird Zeilen ohne Rücksicht auf Gleichheit randomisieren, und ist vielleicht die schnellste Lösung – fuzzyTew

2

Hier ist eine andere Version

Bei der Shell, verwenden Sie diese.

python decorate.py | sort | python undecorate.py 

decorate.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) 

Verwendet fast kein Gedächtnis.

+0

Wie oben beschrieben, sortiert 'sorte -R nach einem zufälligen Schlüssel. Einfacher als Dekoration und Dekore der Datei. –

+0

@Chris B. Wie schon oben erwähnt, wird '-R' immer noch identische Zeilen gruppieren, was nicht der Fall ist. Wenn das das gewünschte Verhalten ist, dann ist das der richtige Weg. – aaronasterling

+1

Wie oben bereits erwähnt, wird' shuf' Zeilen zufällig setzen mit jeder Permutation gleich wahrscheinlich, und erfordert keinen benutzerdefinierten Code früh besser als das Schreiben und Debuggen Ihres eigenen Programms. –

1

Dies ist die gleiche wie Herr Kugelman der ist, aber die Verwendung von vim Python-Schnittstelle Built-in:

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

Wenn Sie alles in den Speicher zu tun laden nicht wollen und es dort zu sortieren, Sie haben um die Zeilen auf der Festplatte zu speichern, während sie zufällig sortiert werden. Das wird sehr langsam sein.

Hier ist eine sehr einfache, dumme und langsame Version. Beachten Sie, dass dies eine überraschend große Menge Speicherplatz beanspruchen kann und sehr langsam ist. Ich habe es mit 300.000 Zeilen ausgeführt und es dauert einige Minuten. 3 Millionen Zeilen könnten sehr gut eine Stunde dauern. Also: Mach es in Erinnerung. Ja wirklich. Es ist nicht so groß.

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) 

Eine andere Version wäre die Dateien in einer SQLite-Datenbank zu speichern und die Linien zufällig aus der Datenbank ziehen. Das wird wahrscheinlich schneller sein.

+0

"Das wird sehr langsam sein"? Langsamer ja. Sehr langsam ist strittig. Jeder einzelne Schritt ist ziemlich schnell. –

+1

@ S.Lott: Nun, hängt vom Dateisystem ab. Ich habe ext3 benutzt. 30.000 Artikel benötigten 5,5 Sekunden. 100.000 Artikel benötigten 16,3 Sekunden. 200.000 Artikel benötigen 339 Sekunden. Ich denke, die Verzeichnissuche wird langsam mit vielen Elementen. 3 Millionen Artikel werden * Stunden * dauern. Mindestens. Eine Datenbank könnte ziemlich schnell sein, aber ich kann mich nicht darum kümmern, sie zu testen. :-) Eine andere Möglichkeit wäre, die Datei zu lesen und einen Index für die Startposition jedes Elements zu erstellen, und seek() s. Das sollte schneller sein als das. –

+0

Interessante Daten. Ich denke, ich habe zu lange mit sehr großen Servern verbracht. –

12

Ich habe gerade versucht, dies auf eine Datei mit 4,3 Millionen Zeilen und das schnellste Ding war 'Shuf' Befehl unter Linux. Verwenden Sie es so:

Es dauerte 2-3 Sekunden, um zu beenden.

0

Hier ist eine andere Art und Weise random.choice verwenden, dies auch entlasten einige graduelle Speicher bereitstellen kann, aber mit einer schlechteren Big-O :)

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) 
+0

"ein besseres Big-O" <- Leider nicht :-(. Das wiederholte Entfernen in 'lines.remove (l)' gibt Ihrem Algorithmus eine Laufzeit, die in der Anzahl der Zeilen quadratisch ist. Sie wird unbrauchbar sein (Laufzeit) von Stunden bis Tagen) für eine 3 Millionen Zeile Datei –

+0

Hoppla, du hast recht :-) habe es gerade repariert –

0

Folgende Vimscript verwendet werden können Linien tauschen:

sehen
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 

die Funktion im visuellen Modus auswählen und :@" dann führen Sie es mit :call Random()

Verwandte Themen