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.