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.