Dessiner au hasard un certain nombre de lignes à partir d'un file de données

J'ai une list de données, comme

12345 23456 67891 -20000 200 600 20 ... 

Supposons que la taille de cet set de données (lignes de file) soit N Je veux dessiner au hasard m lignes à partir de ce file de données. Par conséquent, la sortie devrait être deux files, l'un est le file comprenant ces m lignes de données, et l'autre comprend Nm lignes de données.

Existe-t-il un moyen de le faire en utilisant une command Linux?

Cela pourrait ne pas être le moyen le plus efficace mais cela fonctionne:

 shuf <file> > tmp head -n $m tmp > out1 tail -n +$(( m + 1 )) tmp > out2 

Avec $m contenant le nombre de lignes.

Ce script bash / awk choisit les lignes au hasard et maintient la séquence d'origine dans les deux files de sortie.

 awk -vm=4 -v N=$(wc -l <file) -v out1=/tmp/out1 -v out2=/tmp/out2 \ 'BEGIN{ srand() do{ lnb = 1 + int(rand()*N) if ( !(lnb in R) ) { R[lnb] = 1 ct++ } } while (ct<m) } { if (R[NR]==1) print > out1 else print > out2 }' file cat /tmp/out1 echo ======== cat /tmp/out2 

Sortie, basée sur datatables de la question.

 12345 23456 200 600 ======== 67891 -20000 20 

Comme pour toutes les choses Unix, il y a un utilitaire pour cette TM .

Programme du jour: split
split splita un file de différentes manières, -b octets, -l lignes, -n nombre de files de sortie. Nous utiliserons l'option -l . Puisque vous voulez sélectionner des lignes randoms et pas seulement le premier m , nous allons d'abord sort le file au hasard. Si vous voulez lire sur le sort , référez-vous à ma réponse ici .

Maintenant, le code actuel. C'est assez simple, vraiment:

 sort -R input_file | split -l $m output_prefix 

Cela fera deux files, l'un avec m lignes et l'autre avec Nm lignes, nommé output_prefixaa et output_prefixab . Assurez-vous que m est le plus gros file que vous voulez ou que vous obtiendrez plusieurs files de longueur m (et un avec N % m ).

Si vous voulez vous assurer d'utiliser la bonne taille, voici un petit code pour le faire:

 m=10 # size you want one file to be N=$(wc -l input_file) m=$(( m > N/2 ? m : N - m )) sort -R input_file | split -l $m output_prefix 

Edit: Il est venu à mon attention que certaines implémentations de sort n'ont pas de drapeau -R . Si vous avez perl , vous pouvez replace perl -e 'use List::Util qw/shuffle/; print shuffle <>;' perl -e 'use List::Util qw/shuffle/; print shuffle <>;' .

Si cela ne vous dérange pas de réorganiser les lignes et que vous avez GNU coreutils (c.-à-d. Sur Linux non embedded ou Cygwin, pas trop ancien puisque shuf est apparu dans la version 6.0), shuf ("shuffle") réordonne les lignes d'un file au hasard. Vous pouvez donc mélanger le file et envoyer les premières lignes dans un file et le rest dans un autre.

Il n'y a pas de moyen idéal pour faire cette dépêche. Vous ne pouvez pas simplement enstringr la head et la tail car la head tamponnerait en avant. Vous pouvez utiliser split , mais vous n'obtenez aucune flexibilité en ce qui concerne les noms de files de sortie. Vous pouvez utiliser awk , bien sûr:

 <input shuf | awk -vm=$m '{ if (NR <= m) {print >"output1"} else {print} }' 

Vous pouvez utiliser sed , ce qui est obscur mais peut-être plus rapide pour les gros files.

 <input shuf | sed -e "1,${m} w output1" -e "1,${m} d" >output2 

Ou vous pouvez utiliser tee pour dupliquer datatables, si votre plate-forme a /dev/fd ; c'est ok si m est petit:

 <input shuf | { tee /dev/fd/3 | head -n $m >output1; } 3>&1 | tail -n +$(($m+1)) >output2 

Portable, vous pouvez utiliser awk pour envoyer chaque ligne à tour de rôle. Notez que awk n'est pas très bon pour initialiser son générateur de nombres randoms; le caractère random n'est pas seulement définitivement inadapté à la cryptography, mais il n'est même pas très bon pour les simulations numériques. La graine sera la même pour toutes les invocations awk sur n'importe quel système avec une période d'une seconde.

 <input awk -v N=$(wc -l <input) -vm=3 ' BEGIN {srand()} { if (rand() * N < m) {--m; print >"output1"} else {print >"output2"} --N; }' 

Si vous avez besoin d'un meilleur random, vous pouvez faire la même chose en Perl, qui germe son RNG décemment.

 <input perl -e ' open OUT1, ">", "output1" or die $!; open OUT2, ">", "output2" or die $!; my $N = `wc -l <input`; my $m = $ARGV[0]; while (<STDIN>) { if (rand($N) < $m) { --$m; print OUT1 $_; } else { print OUT2 $_; } --$N; } close OUT1 or die $!; close OUT2 or die $!; ' 42 

En supposant que m = 7 et N = 21 :

 cp ints ints.bak for i in {1..7} do rnd=$((RANDOM%(21-i)+1)) # echo $rnd; sed -n "${rnd}{p,q}" 10k.dat >> mlines sed -i "${rnd}d" ints done 

Remarque: Si vous remplacez 7 par une variable comme $1 ou $m , vous devez utiliser seq , pas la {from..to} , qui ne fait pas d'extension de variable.

Il fonctionne en supprimant ligne par ligne du file, qui devient de plus en plus court, de sorte que le numéro de ligne, qui peut être supprimé, doit être de plus en plus petit.

Cela ne devrait pas être utilisé pour les files plus longs et pour de nombreuses lignes, car pour chaque nombre, le demi-file doit être lu en moyenne pour le 1er et le file entier pour le 2ème code sed .