조건을 충족하는 임의의 줄 집합을 얻으려고 합니다.
예를 들어, 내 파일이 다음과 같은 경우:
a 1 5
b 4 12
c 2 3
e 6 14
f 7 52
g 1 8
그런 다음 열 3과 열 2의 차이가 3보다 크고 10보다 작은 정확히 두 개의 임의 행을 원합니다(예: 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에서 이를 수행하는 한 가지 방법은 다음과 같습니다(사용자 정의 정렬 루틴이 지원됨).
#!/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
.