Seleccionar líneas aleatorias de un archivo

240

En un script Bash, quiero seleccionar N líneas aleatorias del archivo de entrada y salida a otro archivo.

¿Cómo se puede hacer esto?

usuario121196
fuente
Ordene el archivo al azar y elija N primeras líneas.
Piotr Praszmo
Consulte también stackoverflow.com/questions/12354659/… .
Acumenus
31
esto no es un duplicado: quiere N líneas frente a 1 línea.
OneSolitaryNoob
1
No estoy de acuerdo, sort -Rya que hace mucho trabajo en exceso, especialmente para archivos largos. Se puede utilizar $RANDOM, % wc -l, jot, sed -n(a la stackoverflow.com/a/6022431/563329 ), y la funcionalidad de bash (matrices, redirecciones de mando, etc.) para definir su propia peekfunción que en realidad se ejecutará en los archivos de 5.000.000 línea.
isomorfismos el

Respuestas:

627

Use shufcon la -nopción que se muestra a continuación para obtener Nlíneas aleatorias:

shuf -n N input > output
dogbane
fuente
2
Si solo necesita un conjunto aleatorio de líneas, no en un orden aleatorio, entonces shuf es muy ineficiente (para archivos grandes): lo mejor es hacer un muestreo de yacimientos, como en esta respuesta .
petrelharp
Ejecuté esto en un archivo de fila de 500M para extraer 1,000 filas y tomó 13 minutos. No se accedió al archivo en meses y se encuentra en una unidad SSD Amazon EC2.
T. Brian Jones
Entonces, ¿es esto esencialmente más aleatorio que eso sort -R?
Mona Jalal
1
@MonaJalal no solo más rápido, ya que no tiene que comparar líneas en absoluto.
rogerdpack
¿Finalmente produce la misma línea más de una vez?
Frederick Nord
161

Ordene el archivo al azar y elija las primeras 100líneas:

$ sort -R input | head -n 100 >output
usuario881480
fuente
43
sorten realidad ordena líneas idénticas juntas, por lo que si puede tener líneas duplicadas y tiene shuf(una herramienta gnu) instalada, es mejor usarla para esto.
Kevin
22
Además, esto definitivamente te hará esperar mucho si tienes un archivo considerablemente grande (líneas de 80kk), mientras que shuf -nactúa de manera bastante instantánea.
Rubens
28
sort -R no está disponible en Mac OS X (10.9)
Mirko Ebert
3
@ tfb785: sort -Res probablemente la opción GNU, instale GNU coreutils. por cierto, shuftambién es parte de coreutils.
jfs
1
@JFSebastian El código: sort -R input | head -n <num_lines>. El archivo de entrada tenía 279 GB, con 2bi + líneas. Sin embargo, no puedo compartirlo. De todos modos, el punto es que puedes mantener algunas líneas en la memoria con shuffle para hacer la selección aleatoria de qué generar. Ordenar ordenará todo el archivo, independientemente de cuáles sean sus necesidades.
Rubens
18

Bueno, según un comentario sobre la respuesta de shuf, barajó 78 000 000 000 líneas en menos de un minuto.

Desafío aceptado...

EDITAR: batí mi propio récord

powershuf lo hizo en 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

La razón es que es tan rápido, bueno, no leo todo el archivo y simplemente muevo el puntero del archivo 10 veces e imprimo la línea después del puntero.

Gitlab Repo

Viejo intento

Primero necesitaba un archivo de 78,000,000,000 líneas:

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

Esto me da un archivo con 78 mil millones de nuevas líneas ;-)

Ahora para 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

El cuello de botella era CPU y no usaba múltiples hilos, fijó 1 núcleo al 100% y los otros 15 no se usaron.

Python es lo que uso regularmente, así que eso es lo que usaré para hacer esto más 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))

Esto me consiguió poco menos de un minuto:

$ time ./shuf.py         










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

Hice esto en un Lenovo X1 extreme 2nd gen con el i9 y Samsung NVMe, lo que me da mucha velocidad de lectura y escritura.

Sé que puede ser más rápido, pero dejaré espacio para intentarlo.

Fuente del contador de línea : Luther Blissett

Stein van Broekhoven
fuente
Bueno, según su descripción del funcionamiento interno de powershuf, parece que es aleatorio. Usando un archivo con solo dos líneas, una de 1 carácter de largo y la otra de 20 caracteres de largo, espero que ambas líneas se elijan con las mismas posibilidades. Este no parece ser el caso con su programa.
xhienne hace