저는 탭으로 구분된 5개의 열로 구성된 수백 개의 텍스트 파일을 가지고 있습니다. 첫 번째 열에는 인덱스가 포함되고 다음 4개 열에는 발생 횟수가 포함됩니다. 이제 값이 0인 3개 열(즉, 아래 예에서는 7개 행)을 포함하는 행 수를 계산하려고 합니다.
1 0 0 0 9
2 0 9 0 0
3 10 0 0 0
4 0 10 4 0
5 0 0 0 10
6 0 0 0 10
7 0 0 0 10
8 0 10 0 0
9 5 0 5 0
이것을 R에서 루프로 코딩할 수 있지만 원본 파일에는 각각 6천만 개 이상의 라인이 포함되어 있으므로 awk 또는 sed 및 wc -l을 사용하여 이를 해결할 수 있는 방법이 없는지 궁금합니다.
답변1
예, 다음에서 할 수 있습니다 awk
.
awk '{
k=0;
for(i=2;i<=NF;i++){
if($i == 0){
k++
}
}
if(k==3){
tot++
}
}
END{
print tot
}' file
또한 (GNU) sed
및 wc
:
$ sed -nE '/\b0\b.*\b0\b.*\b0\b/p' file | wc -l
7
그러나 개인적으로는 Perl을 대신 사용하겠습니다.
$ perl -ale '$tot++ if (grep{$_ == 0 } @F) == 3 }{ print $tot' file
7
또는 약간 덜 압축되었습니다.
$ perl -ale 'if( (grep{$_ == 0 } @F) == 3 ){
$tot++
}
END{
print $tot
}' file
7
그리고 여러분 중 골퍼들을 위해:
$ perl -ale '(grep{$_==0}@F)==3&&$t++}{print$t' file
7
설명하다
-ale
:-a
Perl을 awk처럼 동작하게 만듭니다. 입력 파일의 각 줄을 읽고 이를 공백으로 나누어 배열로 만듭니다@F
. 각 호출 에 대한 입력의 후행 줄 바꿈을-l
추가 및 제거하며 각 입력 줄에 적용해야 하는 스크립트입니다.\n
print
-e
$tot++ if (grep{$_ == 0 } @F) == 3
:$tot
정확히 3개의 필드가 있을 때마다 1씩 증가합니다0
. 첫 번째 필드는 1에서 시작하므로 0이 될 수 없다는 것을 알고 있으므로 제외할 필요가 없습니다.}{
END{}
: 이는 파일이 처리된 후 실행될 코드 블록을 제공하는 간단한 방법일 뿐입니다 . 따라서}{ print $tot
값이 있는 세 개의 필드를 포함하는 총 행 수가 인쇄됩니다0
.
답변2
그리고 GNU grep
또는립그렙
$ LC_ALL=C grep -c $'\t''0\b.*\b0\b.*\b0\b' ip.txt
7
$ rg -c '\t0\b.*\b0\b.*\b0\b' ip.txt
7
where 은 $'\t'
탭 문자와 일치하므로 첫 번째 열이 0
.
대용량 파일을 사용하여 예제를 실행합니다.
$ perl -0777 -ne 'print $_ x 1000000' ip.txt > f1
$ du -h f1
92M f1
$ time LC_ALL=C grep -c $'\t''0\b.*\b0\b.*\b0\b' f1 > f2
real 0m0.416s
$ time rg -c '\t0\b.*\b0\b.*\b0\b' f1 > f3
real 0m1.271s
$ time LC_ALL=C awk 'gsub(/\t0/,"")==3{c++} END{print c+0}' f1 > f4
real 0m8.645s
$ time perl -ale '$tot++ if (grep{$_ == 0 } @F) == 3 }{ print $tot' f1 > f5
real 0m14.349s
$ time LC_ALL=C sed -n 's/\t0\>//4;t;s//&/3p' f1 | wc -l > f6
real 0m14.075s
$ time LC_ALL=C sed -n 's/\t0\>/&/3p' f1 | wc -l > f8
real 0m6.772s
$ time LC_ALL=C awk '{
k=0;
for(i=2;i<=NF;i++){
if($i == 0){
k++
}
}
if(k==3){
tot++
}
}
END{
print tot
}' f1 > f7
real 0m10.675s
LC_ALL=C
파일에 ASCII가 아닌 문자가 포함될 수 있으면 삭제하십시오. 일반적 으로 테스트 실행 시보 ripgrep
다 빠릅니다 . 저자 에 따르면 이는 유니코드 단어 경계를 피하기 위해 사용될 수 있지만 이는 위의 상황과 비슷한 결과를 낳습니다.GNU grep
GNU grep
ripgrep
(?-u:\b)
답변3
GNU sed 사용:
sed -E 's/\t0\>/&/3;t;d' file | wc -l
Isaac이 지적했듯이, 정확히 3을 계산하려면 다음과 같이 할 수 있습니다.
sed -n 's/\t0\>//4;t;s//&/3p' file | wc -l
답변4
Perl을 사용하여 왼쪽의 TAB과 오른쪽의 단어 경계로 둘러싸인 0이 있는 줄 수를 총 3회 계산합니다. 마지막으로 이 줄의 줄 번호를 인쇄합니다.
perl -lne '$c += 3 == (() = /\t0\b/g)}{print $c' file
7
또 다른 방법은 필드를 살펴보는 것입니다.
perl -F'\t' -lane '$c++ if 3 == grep ! $_, @F[1..$#F]}{print $c' file
s///
또 다른 방법은 스칼라 컨텍스트에서 명령을 사용하는 것입니다.
perl -lne '$c += s/\t0\b//g == 3}{print $c' file
우리는 이를 위해 Gnu awk를 사용합니다:
awk -F'\t' '
{
gsub(FS, FS FS)
$0 = $0 FS
if ($0 != gensub(FS"0"FS, "", 3, $0)) ++c
}
END{print c}
' file
Gnu grep도 도움이 될 수 있습니다.
grep -cP '(.*\t0\b.*){3}' file