53 Stimmen

Mit Unix zufällig Zeilen aus einer Datei auswählen, ohne sie zu schlürfen

Ich habe eine Datei mit 10^7 Zeilen, aus der ich 1/100 der Zeilen zufällig auswählen möchte aus der Datei auswählen. Dies ist der AWK-Code, den ich habe, aber er schlürft den gesamten Inhalt der Datei vor der Hand. Mein PC-Speicher kann solche Schlürfe nicht verarbeiten. Gibt es eine andere Möglichkeit, dies zu tun?

awk 'BEGIN{srand()}
!/^$/{ a[c++]=$0}
END {  
  for ( i=1;i<=c ;i++ )  { 
    num=int(rand() * c)
    if ( a[num] ) {
        print a[num]
        delete a[num]
        d++
    }
    if ( d == c/100 ) break
  }
 }' file

0 Stimmen

Zu Ihrer Information: 1% von 10.000.000 ist zu viel - man braucht nur etwa 1000 für eine Fehlermarge von +/-3% und 10.000 für eine Fehlermarge von +/-1%.

0 Stimmen

@Steven: Vielen Dank für diese Informationen. Wie haben Sie die obige MOE-Zahl hergeleitet? Irgendeine Referenz? Ich möchte wirklich mehr lernen, da mein statistischer Hintergrund schwach ist. Übrigens scheint Ihre Meinung eine andere zu sein als die von Cadrian unten (d.h. 1% ist nicht genug)

1 Stimmen

91voto

cadrian Punkte 7202

Wenn Sie so viele Zeilen haben, sind Sie sicher, dass Sie genau 1% oder eine statistische Schätzung würde ausreichen?

In diesem zweiten Fall einfach bei jeder Zeile 1% zufällig verteilen...

awk 'BEGIN {srand()} !/^$/ { if (rand() <= .01) print $0}'

Wenn Sie die Kopfzeile und eine zufällige Auswahl der nachfolgenden Zeilen haben möchten, verwenden Sie:

awk 'BEGIN {srand()} !/^$/ { if (rand() <= .01 || FNR==1) print $0}'

59voto

Bill Punkte 3404

Sie haben awk verwendet, aber ich weiß nicht, ob das erforderlich ist. Wenn nicht, gibt es eine triviale Möglichkeit, dies mit Perl zu tun (und ohne die gesamte Datei in den Speicher zu laden):

cat your_file.txt | perl -n -e 'print if (rand() < .01)'

(einfachere Form, aus Kommentaren):

perl -ne 'print if (rand() < .01)' your_file.txt

22voto

Steven Huwig Punkte 18929

Ich habe genau diesen Code in Gawk geschrieben - Sie haben Glück. Er ist teilweise lang, weil er die Eingabereihenfolge beibehält. Es gibt wahrscheinlich Leistungsverbesserungen, die gemacht werden können.

Dieser Algorithmus ist korrekt, ohne dass die Eingabegröße im Voraus bekannt ist. Ich habe eine Rosettenstein hier darüber. (Ich habe diese Version nicht gepostet, weil sie unnötige Vergleiche anstellt).

Ursprünglicher Thread: Zur Überprüfung vorgelegt - Zufallsstichproben in awk.

# Waterman's Algorithm R for random sampling
# by way of Knuth's The Art of Computer Programming, volume 2

BEGIN {
    if (!n) {
        print "Usage: sample.awk -v n=[size]"
        exit
    }
    t = n
    srand()

}

NR <= n {
    pool[NR] = $0
    places[NR] = NR
    next

}

NR > n {
    t++
    M = int(rand()*t) + 1
    if (M <= n) {
        READ_NEXT_RECORD(M)
    }

}

END {
    if (NR < n) {
        print "sample.awk: Not enough records for sample" \
            > "/dev/stderr"
        exit
    }
    # gawk needs a numeric sort function
    # since it doesn't have one, zero-pad and sort alphabetically
    pad = length(NR)
    for (i in pool) {
        new_index = sprintf("%0" pad "d", i)
        newpool[new_index] = pool[i]
    }
    x = asorti(newpool, ordered)
    for (i = 1; i <= x; i++)
        print newpool[ordered[i]]

}

function READ_NEXT_RECORD(idx) {
    rec = places[idx]
    delete pool[rec]
    pool[NR] = $0
    places[idx] = NR  
}

17voto

ashawley Punkte 3966

Dies sollte auf fast jedem GNU/Linux-Rechner funktionieren.

$ shuf -n $(( $(wc -l < $file) / 100)) $file

Es würde mich überraschen, wenn die Speicherverwaltung durch den GNU-Befehl shuf unangemessen durchgeführt würde.

5voto

advait Punkte 173

Ich weiß es nicht. awk aber es gibt eine großartige Technik, um eine allgemeinere Version des von Ihnen beschriebenen Problems zu lösen, und im allgemeinen Fall ist sie sehr viel schneller als die for line in file return line if rand < 0.01 Es könnte also nützlich sein, wenn Sie vorhaben, Aufgaben wie die oben genannten viele (Tausende, Millionen) Male zu erledigen. Sie ist bekannt als Lagerstättenbeprobung y diese Seite hat eine ziemlich gute Erklärung für eine Version, die auf Ihre Situation anwendbar ist.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X