条件を満たす任意の行セットを取得しようとしています。
たとえば、私のファイルが次のような場合:
a 1 5
b 4 12
c 2 3
e 6 14
f 7 52
g 1 8
次に、列3と列2の差が3より大きく10より小さい、正確に2つのランダムな行が必要です(たとえば、a、b、e、gで始まる行が適しています)。
この問題にどのように対処する必要がありますか?
awk (if something and random) '{print $1,$2,$3}'
答え1
これを行うこともできますが、awk
行をランダムに選択するのは複雑で多くのコードが必要です。awk
あなたの基準に一致する行を取得し、標準ツールを使用してランダムに選択するためにshuf
使用します。
$ awk '$3-$2>3 && $3-$2 < 10' file | shuf -n2
g 1 8
a 1 5
数回実行すると、ランダムに選択された行が表示されます。
$ 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
--
このshuf
ツールはGNU coreutilsの一部であるため、ほとんどのLinuxシステムにデフォルトでインストールする必要があり、ほとんどの* nixで簡単に使用できます。
答え2
リストを一度だけ繰り返す純粋なawk回答が必要な場合:
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
読みやすくするためにファイルに保存されます。
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]
}
アルゴリズムが若干異なりますクヌスアルゴリズムR;この変更は分布が変わらないと確信していますが、私は統計学者ではないため保証できません。
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]
}
答え3
GNU awkでこれを行う1つの方法は次のとおりです(カスタムソートルーチンがサポートされています)。
#!/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];
}
与えられたデータでテスト:
$ 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
答え4
perl
ソリューションを含める理由がまったくないため、ソリューションを公開しますawk
(OPの希望を除く)。
#!/usr/bin/perl
use strict;
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;
これは典型的な例です貯水池サンプリング。このアルゴリズムは以下からコピーされた。ここOPの特定の希望に合わせて私が修正しました。
ファイルに保存すると、reservoir.pl
使用./reservoir.pl file1 file2 file3
または実行できますcat file1 file2 file3 | ./reservoir.pl
。