Seleziona linee casuali da un file


240

In uno script Bash, voglio selezionare N righe casuali dal file di input e l'output su un altro file.

Come si può fare?


Ordina il file in modo casuale e seleziona N prime righe.
Piotr Praszmo,


31
questo non è un duplicato: vuole N righe contro 1 riga.
OneSolitaryNoob


1
Non sono d'accordo sort -Rperché fa molto lavoro in eccesso, in particolare per file lunghi. È possibile utilizzare $RANDOM, % wc -l, jot, sed -n(à la stackoverflow.com/a/6022431/563329 ), e la funzionalità bash (array, redirect di comando, ecc) per definire la propria peekfunzione che verrà effettivamente eseguito su file 5.000.000 linea.
isomorfismi

Risposte:


627

Utilizzare shufcon l' -nopzione come mostrato di seguito, per ottenere Nlinee casuali:

shuf -n N input > output

2
Se hai solo bisogno di un insieme casuale di linee, non in un ordine casuale, allora shuf è molto inefficiente (per file di grandi dimensioni): è meglio fare il campionamento del serbatoio, come in questa risposta .
petrelharp,

Ho eseguito questo su un file di 500M per estrarre 1.000 righe e ci sono voluti 13 minuti. Il file non era accessibile da mesi e si trova su un'unità SSD Amazon EC2.
T. Brian Jones

quindi, in sostanza, è più casuale sort -R?
Mona Jalal,

1
@MonaJalal no solo più velocemente, dal momento che non è necessario confrontare le linee.
rogerdpack,

Alla fine produce la stessa linea più di una volta?
Federico Nord,

161

Ordina il file in modo casuale e scegli le prime 100righe:

$ sort -R input | head -n 100 >output

43
sortordina effettivamente le linee identiche insieme, quindi se potresti avere linee duplicate e hai shuf(uno strumento gnu) installato, è meglio usarlo per questo.
Kevin,

22
Andalso, questo ti farà sicuramente aspettare molto se hai un file considerevolmente grande - 80kk righe -, mentre shuf -nagisce abbastanza istantaneamente.
Rubens,

28
sort -R non è disponibile in Mac OS X (10.9)
Mirko Ebert,

3
@ tfb785: sort -Rè probabilmente l'opzione GNU, installa i coreutils GNU. a proposito, shuffa anche parte di coreutils.
jfs,

1
@JFSebastian Il codice: sort -R input | head -n <num_lines>. Il file di input era 279 GB, con 2bi + linee. Non posso condividerlo, però. Ad ogni modo, il punto è che puoi tenere alcune righe in memoria con shuffle per fare la selezione casuale di cosa produrre. Ordinamento ordinerà l' intero file, indipendentemente dalle tue esigenze.
Rubens,

18

Bene Secondo un commento sulla risposta shuf, ha misurato 78.000.000.000 di righe in meno di un minuto.

Sfida accettata...

EDIT: ho battuto il mio record

powershuf lo ha fatto in 0,047 secondi

$ time ./powershuf.py -n 10 --file lines_78000000000.txt > /dev/null 
./powershuf.py -n 10 --file lines_78000000000.txt > /dev/null  0.02s user 0.01s system 80% cpu 0.047 total

La ragione per cui è così veloce, beh, non leggo l'intero file e muovo il puntatore del file 10 volte e stampo la riga dopo il puntatore.

Gitlab Repo

Vecchio tentativo

Per prima cosa avevo bisogno di un file di 78.000.000.000 di righe:

seq 1 78 | xargs -n 1 -P 16 -I% seq 1 1000 | xargs -n 1 -P 16 -I% echo "" > lines_78000.txt
seq 1 1000 | xargs -n 1 -P 16 -I% cat lines_78000.txt > lines_78000000.txt
seq 1 1000 | xargs -n 1 -P 16 -I% cat lines_78000000.txt > lines_78000000000.txt

Questo mi dà un file con 78 miliardi di nuove righe ;-)

Ora per la parte shuf:

$ time shuf -n 10 lines_78000000000.txt










shuf -n 10 lines_78000000000.txt  2171.20s user 22.17s system 99% cpu 36:35.80 total

Il collo di bottiglia era CPU e non utilizzava più thread, ha bloccato 1 core al 100%, gli altri 15 non sono stati utilizzati.

Python è quello che uso regolarmente, quindi è quello che userò per renderlo più veloce:

#!/bin/python3
import random
f = open("lines_78000000000.txt", "rt")
count = 0
while 1:
  buffer = f.read(65536)
  if not buffer: break
  count += buffer.count('\n')

for i in range(10):
  f.readline(random.randint(1, count))

Questo mi ha portato poco meno di un minuto:

$ time ./shuf.py         










./shuf.py  42.57s user 16.19s system 98% cpu 59.752 total

L'ho fatto su un Lenovo X1 extreme 2nd gen con i9 e Samsung NVMe che mi dà molta velocità di lettura e scrittura.

So che può andare più veloce, ma lascerò un po 'di spazio per provare gli altri.

Fonte contatore linea : Luther Blissett


Bene, secondo la tua descrizione del funzionamento interiore di powershuf, sembra che sia solo casuale. Usando un file con solo due righe, una lunga 1 carattere e l'altra lunga 20 caratteri, mi aspetto che entrambe le linee vengano scelte con pari probabilità. Questo non sembra essere il caso del tuo programma.
xhienne
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.