2009-06-23 5 views
3

Ich habe eine Datei mit InhaltWie kann ich den Inhalt einer Datei nach dem Zufallsprinzip abtasten?

abc 
def 
high 
lmn 
... 
... 

Es gibt mehr als 2 Millionen Zeilen in den Dateien. Ich möchte Zeilen zufällig aus den Dateien abtasten und 50K Zeilen ausgeben. Irgendwelche Gedanken darüber, wie man dieses Problem angeht? Ich dachte entlang der Linien von Perl und seiner rand Funktion (Oder ein handlicher Shell-Befehl wäre ordentlich).

Verwandte (Möglicherweise Duplizieren) Fragen:

+0

Ist die Anzahl der Zeilen, die Sie ausgeben möchten, genau oder ist es in Ordnung, dass der Algorithmus etwa 2,5% aller Zeilen ausgibt? –

Antwort

12

Vorausgesetzt, dass Sie im Grunde die Ausgabe etwa 2,5% aller Linien wollen, dies tun würde:

print if 0.025 > rand while <$input>; 
+3

ziemlich ordentlich Trick. – cloudhead

+1

Wenn die Dateigröße variiert, können Sie den Prozentsatz berechnen, indem Sie die Zeilen (siehe perlfaq5) zählen und in die gewünschte Anzahl von Zeilen einteilen. –

+1

Dies ist eine wirklich gute Lösung, weil es die naiven Ansätze zur Lösung dieses Problems vermeidet, die das Springen zu zufälligen Punkten in der Datei oder (noch schlimmer!) Das Sortieren der Eingabe beinhalten. –

5

Shell Art und Weise:

sort -R file | head -n 50000 
+0

Welche Art ist das? Meine (GNU coreutils 5.93) unterstützt nicht -R. –

+0

[sinan @ kas ~] $ sort --Version sort (GNU coreutils) 7.4 Copyright (C) 2009 Free Software Foundation, Inc. –

+0

=> Art --Version sort (GNU coreutils) 6.10 –

2

Wenn Sie eine genaue Anzahl der Linien zu extrahieren:

use strict; 
use warnings; 

# Number of lines to pick and file to pick from 
# Error checking omitted! 
my ($pick, $file) = @ARGV; 

open(my $fh, '<', $file) 
    or die "Can't read file '$file' [$!]\n"; 

# count lines in file 
my ($lines, $buffer); 
while (sysread $fh, $buffer, 4096) { 
    $lines += ($buffer =~ tr/\n//); 
} 

# limit number of lines to pick to number of lines in file 
$pick = $lines if $pick > $lines; 

# build list of N lines to pick, use a hash to prevent picking the 
# same line multiple times 
my %picked; 
for (1 .. $pick) { 
    my $n = int(rand($lines)) + 1; 
    redo if $picked{$n}++ 
} 

# loop over file extracting selected lines 
seek($fh, 0, 0); 
while (<$fh>) { 
    print if $picked{$.}; 
} 
close $fh; 
+1

Wirklich nett Ansatz. Das einzige, was fehlt, ist zu überprüfen, ob $ pick <= $ lines - sonst wird es in der for() Schleife hängen bleiben. –

+0

Guter Fang. Ich habe es aktualisiert, um das zu korrigieren. –

+0

Fehler: int (rand ($ Zeilen)) kann eine 0 aber $ zurückgeben. beginnt bei 1. –

2

Perl Art und Weise:

Verwendung CPAN. Es gibt Modul File::RandomLine, das genau das tut, was Sie brauchen.

+1

Es gibt auch http://search.cpan.org/perldoc?File::Random –

2

Von perlfaq5: "How do I select a random line from a file?"


Kurz der Datei in eine Datenbank oder Pre-Indizierung der Zeilen in der Datei geladen wird, gibt es ein paar Dinge, die Sie tun können.

Hier ist ein Reservoir Sampling-Algorithmus aus dem Camel Book:

srand; 
rand($.) < 1 && ($line = $_) while <>; 

Dies hat einen bedeutenden Vorteil im Raum über in die gesamte Datei lesen Sie einen Beweis für diese Methode in The Art of Computer finden können. Programmierung, Band 2, Abschnitt 3.4.2, von Donald E. Knuth.

Sie können die Datei verwenden :: Zufalls-Modul, das für diesen Algorithmus eine Funktion bereitstellt:

use File::Random qw/random_line/; 
my $line = random_line($filename); 

Ein anderer Weg ist die Tie :: File-Modul zu verwenden, die die gesamte Datei als Array behandelt. Greifen Sie einfach auf ein zufälliges Array-Element zu.

+0

Nach dieser Antwort führte mich zu einer großen Beschreibung der Reservoir Sampling, und eine einfache Möglichkeit, den Camel Book-Code von einer Zeile zu 'k zu erweitern 'Artikel: http://StackOverflow.com/a/12733515/2016618 – Sarkom

Verwandte Themen