Selecionar linhas aleatórias de um arquivo

240

Em um script Bash, desejo selecionar N linhas aleatórias do arquivo de entrada e saída para outro arquivo.

Como isso pode ser feito?

user121196
fonte
Classifique o arquivo aleatoriamente e escolha N primeiras linhas.
Piotr Praszmo 12/02/2012
Consulte também stackoverflow.com/questions/12354659/… .
Acumenus
31
isso não é uma duplicata - ele quer N linhas vs 1 linha.
OneSolitaryNoob
1
Não concordo sort -R, pois faz muito trabalho em excesso, principalmente para arquivos longos. Você pode usar $RANDOM, % wc -l, jot, sed -n(à la stackoverflow.com/a/6022431/563329 ), e funcionalidade bash (matrizes, redirecionamentos de comando, etc) para definir a sua própria peekfunção que irão correr em arquivos 5.000.000 linhas.
Isomorphismes

Respostas:

627

Use shufcom a -nopção como mostrado abaixo, para obter Nlinhas aleatórias:

shuf -n N input > output
dogbane
fonte
2
Se você só precisa de um conjunto aleatório de linhas, não em uma ordem aleatória, o shuf é muito ineficiente (para arquivos grandes): o melhor é fazer amostragem de reservatório, como nesta resposta .
petrelharp
Eu executei isso em um arquivo de linha de 500M para extrair 1.000 linhas e levou 13 min. O arquivo não foi acessado em meses e está em uma unidade SSD Amazon EC2.
T. Brian Jones
então isso é essencialmente mais aleatório que isso sort -R?
Mona Jalal
1
@MonaJalal não apenas mais rápido, já que não precisa comparar linhas.
Rogerdpack #
Eventualmente, produz a mesma linha mais de uma vez?
Frederick Nord
161

Classifique o arquivo aleatoriamente e escolha as primeiras 100linhas:

$ sort -R input | head -n 100 >output
user881480
fonte
43
sortna verdade classifica linhas idênticas, portanto, se você tiver linhas duplicadas e tiver shuf(uma ferramenta gnu) instalada, é melhor usá-las para isso.
Kevin
22
Além disso, isso definitivamente fará com que você espere muito se você tiver um arquivo consideravelmente grande - linhas de 80kk -, enquanto shuf -nage instantaneamente.
Rubens
28
tipo -R não está disponível no Mac OS X (10.9)
Mirko Ebert
3
@ tfb785: sort -Rprovavelmente é a opção GNU, instale o GNU coreutils. btw, shuftambém faz parte do coreutils.
jfs
1
@JFSebastian O código: sort -R input | head -n <num_lines>. O arquivo de entrada tinha 279GB, com 2bi + linhas. Não é possível compartilhar. De qualquer forma, o ponto é que você pode manter algumas linhas na memória com shuffle para fazer a seleção aleatória do que deve ser produzido. A classificação classificará o arquivo inteiro , independentemente de quais sejam suas necessidades.
Rubens
18

Bem De acordo com um comentário sobre a resposta shuf, ele embaralhou 78 000 000 000 linhas em menos de um minuto.

Desafio aceito...

EDIT: Eu bati meu próprio recorde

powershuf fez isso em 0,047 segundos

$ 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

O motivo é tão rápido, bem, eu não leio o arquivo inteiro e apenas movo o ponteiro do arquivo 10 vezes e imprimo a linha após o ponteiro.

Repositório Gitlab

Tentativa antiga

Primeiro eu precisava de um arquivo de 78.000.000.000 linhas:

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

Isso me dá um arquivo com 78 bilhões de novas linhas ;-)

Agora, para a 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

O gargalo era a CPU e não usava vários encadeamentos; fixou 1 núcleo em 100% e os outros 15 não foram usados.

Python é o que eu uso regularmente, e é isso que vou usar para tornar isso mais rápido:

#!/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))

Isso me deixou em menos de um minuto:

$ time ./shuf.py         










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

Eu fiz isso em um Lenovo X1 extremo de segunda geração com o i9 e o Samsung NVMe, o que me proporciona muita velocidade de leitura e gravação.

Eu sei que pode ficar mais rápido, mas vou deixar um espaço para tentar outros.

Fonte do contador de linhas : Luther Blissett

Stein van Broekhoven
fonte
Bem, de acordo com sua descrição do funcionamento interno do powershuf, parece que é apenas aleatório. Usando um arquivo com apenas duas linhas, uma com 1 caractere e a outra com 20 caracteres, espero que as duas linhas sejam escolhidas com chances iguais. Este não parece ser o caso do seu programa.
xhienne