AWK: get des lignes randoms de file satisfaisant une condition?

Donc, je suis nouveau à awk et j'essaie d'get un nombre défini de lignes randoms qui satisfont une condition.

par exemple si mon file était:

a 1 5 b 4 12 c 2 3 e 6 14 f 7 52 g 1 8 

alors je voudrais exactement deux lignes randoms où la différence entre la colonne 3 et la colonne 2 est supérieure à 3 mais inférieure à 10 (par exemple, les lignes commençant par a, b, e et g seraient qualifiées)

Comment voudrais-je aborder cela?

awk (if something and random) '{print $1,$2,$3}'

Vous pouvez le faire dans awk mais get la sélection random des lignes sera complexe et nécessitera l'écriture d'un peu de code. J'utiliserais plutôt awk pour get les lignes qui correspondent à vos critères, puis utiliser l'outil standard shuf pour choisir une sélection random:

 $ awk '$3-$2>3 && $3-$2 < 10' file | shuf -n2 g 1 8 a 1 5 

Si vous exécutez cela plusieurs fois, vous verrez que vous obtenez une sélection random de lignes:

 $ for i in {1..5}; do awk '$3-$2>3 && $3-$2 < 10' file | shuf -n2; echo "--"; done g 1 8 e 6 14 -- g 1 8 e 6 14 -- b 4 12 g 1 8 -- b 4 12 e 6 14 -- e 6 14 b 4 12 -- 

L'outil shuf fait partie des coreutils GNU, il doit donc être installé par défaut sur la plupart des systèmes Linux et facilement disponible pour la plupart des * nix.

Si vous voulez une réponse pure awk qui ne fait que parcourir la list une fois:

 awk -v count=2 'BEGIN { srand() } $3 - $2 > 3 && $3 - $2 < 10 && rand() < count / ++n { if (n <= count) { s[n] = $0 } else { s[1+int(rand()*count)] = $0 } } END { for (i in s) print s[i] }' input.txt 

Stocké dans un file pour faciliter la lecture:

 BEGIN { srand() } $3 - $2 > 3 && $3 - $2 < 10 && rand() < count / ++n { if (n <= count) { s[n] = $0 } else { s[1+int(rand()*count)] = $0 } } END { for (i in s) print s[i] } 

L'algorithm est une légère variation de l'algorithm de Knuth R ; Je suis sûr que le changement ne modifie pas la dissortingbution mais je ne suis pas un statisticien, donc je ne peux pas le garantir.

A commenté pour ceux less familiers avec awk:

 # Before the first line is read... BEGIN { # ...seed the random number generator. srand() } # For each line: # if the difference between the second and third columns is between 3 and 10 (exclusive)... $3 - $2 > 3 && $3 - $2 < 10 && # ... with a probability of (total rows to select) / (total matching rows so far) ... rand() < count / ++n { # ... If we haven't reached the number of rows we need, just add it to our list if (n <= count) { s[n] = $0 } else { # otherwise, replace a random entry in our list with the current line. s[1+int(rand()*count)] = $0 } } # After all lines have been processed... END { # Print all lines in our list. for (i in s) print s[i] } 

Voici une façon de le faire dans GNU awk (qui prend en charge les routines de sorting personnalisées):

 #!/usr/bin/gawk -f function mycmp(ia, va, ib, vb) { return rand() < 0.5 ? 0 : 1; } BEGIN { srand(); } $3 - $2 > 3 && $3 - $2 < 10 { a[NR]=$0; } END { asort(a, b, "mycmp"); for (i = 1; i < 3; i++) print b[i]; } 

Test avec datatables données:

 $ for i in {1..6}; do printf 'Try %d:\n' $i; ../randsel.awk file; sleep 2; done Try 1: g 1 8 e 6 14 Try 2: a 1 5 b 4 12 Try 3: b 4 12 a 1 5 Try 4: e 6 14 a 1 5 Try 5: b 4 12 a 1 5 Try 6: e 6 14 b 4 12 

Publier une solution perl , car je ne vois pas pourquoi elle doit être en awk (sauf pour le souhait de l'OP):

 #!/usr/bin/perl use ssortingct; use warnings; my $N = 2; my $k; my @r; while(<>) { my @line = split(/\s+/); if ($line[2] - $line[1] > 3 && $line[2] - $line[1] < 10) { if(++$k <= $N) { push @r, $_; } elsif(rand(1) <= ($N/$k)) { $r[rand(@r)] = $_; } } } print @r; 

C'est un exemple classique d' échantillonnage de réservoir . L'algorithm a été copié à partir d' ici et modifié par moi pour répondre aux souhaits spécifiques de l'OP.

Une fois enregistré dans le file reservoir.pl vous l'exécutez avec ./reservoir.pl file1 file2 file3 ou cat file1 file2 file3 | ./reservoir.pl cat file1 file2 file3 | ./reservoir.pl .