Cómo muestrear aleatoriamente un subset de un file

¿Hay algún command de Linux que se pueda usar para muestrear el subset de un file? Por ejemplo, un file contiene un millón de líneas, y queremos muestrear random solo mil líneas de ese file.

Para el azar, quiero decir que cada línea tiene la misma probabilidad de ser elegida y ninguna de las líneas elegidas es repetitiva.

head y la tail pueden elegir un subset del file pero no aleatoriamente. Sé que siempre puedo escribir un script de Python para hacerlo, pero me pregunto si hay algún command para este uso.

El command shuf (parte de coreutils) puede hacer esto:

 shuf -n 1000 file 

No estoy al tanto de ningún command que pueda hacer lo que me pide, pero aquí hay un ciclo que armé y que puede hacer el trabajo:

 for i in `seq 1000`; do sed -n `echo $RANDOM % 1000000 | bc`p alargefile.txt; done > sample.txt 

sed recogerá una línea aleatoria en cada uno de los 1000 pases. Posiblemente haya soluciones más eficientes.

Puede save el código siguiente en un file (por ejemplo, randextract.sh) y ejecutarlo como sigue:

 randextract.sh file.txt 

—- BEGIN FILE —-

 #!/bin/sh -xv #configuration MAX_LINES is the number of lines to extract MAX_LINES=10 #number of lines in the file (is a limit) NUM_LINES=`wc -l $1 | cut -d' ' -f1` #generate a random number #in bash the variable $RANDOM returns diferent values on each call if [ "$RANDOM." != "$RANDOM." ] then #bigger number (0 to 3276732767) RAND=$RANDOM$RANDOM else RAND=`date +'%s'` fi #The start line START_LINE=`expr $RAND % '(' $NUM_LINES - $MAX_LINES ')'` tail -n +$START_LINE $1 | head -n $MAX_LINES 

—- ARCHIVO FINAL —-

O así:

 LINES=$(wc -l < file) RANDLINE=$[ $RANDOM % $LINES ] tail -n $RANDLINE < file|head -1 

Desde la página del hombre bash:

         RANDOM Cada vez que se hace reference a este parámetro, un integer aleatorio
               entre 0 y 32767 se genera.  La secuencia de azar
               los numbers pueden inicializarse asignando un valor a RAN-
               DOM.  Si RANDOM no está configurado, pierde su propiedad especial
               vínculos, incluso si se restablece posteriormente.

Si el tamaño del file no es enorme, puede usar Ordenar random. Esto lleva un poco más de time que shuf, pero aleatoriza la información completa. Por lo tanto, puede hacer lo siguiente para usar head tal como lo solicitó:

 sort -R input | head -1000 > output 

Esto orderaría el file aleatoriamente y te daría las primeras 1000 líneas.

Si tiene un file muy grande (que es una razón común para tomar una muestra), encontrará que:

  1. shuf agota la memory
  2. Usar $RANDOM no funcionará correctamente si el file supera las 32767 líneas

Si no necesita "exactamente" n líneas de muestra, puede muestrear una proporción como esta:

cat input.txt | awk 'BEGIN {srand()} !/^$/ { if (rand() <= .01) print $0}' > sample.txt

Utiliza memory constante , muestras 1% del file (si conoce el número de líneas del file, puede ajustar este factor para muestrear un número de líneas cercano), y funciona con cualquier tamaño de file pero no lo hará devuelve un número preciso de líneas, solo una proporción estadística.

Nota: El código proviene de: https://stackoverflow.com/questions/692312/randomly-pick-lines-from-a-file-without-slurping-it-with-unix

En caso de que el truco shuf -n en files grandes se quede sin memory y aún necesite una muestra de tamaño fijo y se pueda instalar una utilidad externa, intente con la muestra :

 $ sample -N 1000 < FILE_WITH_MILLIONS_OF_LINES 

La advertencia es que la muestra (1000 líneas en el ejemplo) debe caber en la memory.

Descargo de responsabilidad: soy el autor del software recomendado.

Si conoce la cantidad de líneas en el file (como 1e6 en su caso), puede hacer:

 awk -vn=1e6 -vp=1000 ' BEGIN {srand()} rand() * n-- < p {p--; print}' < file 

Si no, siempre puedes hacer

 awk -vn="$(wc -l < file)" -vp=1000 ' BEGIN {srand()} rand() * n-- < p {p--; print}' < file 

Eso haría dos pasadas en el file, pero aún evitará almacenar el file completo en la memory.

Otra ventaja sobre GNU shuf es que conserva el order de las líneas en el file.

Tenga en count que asume que n es el número de líneas en el file. Si desea imprimir p de las primeras n líneas del file (que tiene potencialmente más líneas), deberá detener awk en la n ésima línea como:

 awk -vn=1e6 -vp=1000 ' BEGIN {srand()} rand() * n-- < p {p--; print} !n {exit}' < file