awk를 통해 많은 수 .tsv
의 파일을 처리 중인데 2열에서 중복되거나 더 동일한 값이 관찰되는 모든 행을 삭제해야 합니다.
예를 들어
a b c
value1 2 3
value2 2 5
value3 1 9
value4 1 0
value5 4 0
value6 1 0
되어야 한다
a b c
value5 4 0
사용을 고려했지만 sort -k2 filename | awk 'count[$2]++ > 1'
이상한 결과가 나왔습니다.
$ sort -k2 file.txt | awk 'count[$2]++ > 1' | wc
162 4212 33348
$ sort -k2 file.txt | awk 'count[$2]++ == 1' | wc
3954 102804 704176
$ wc file.txt
1029257 26760682 176254913
...합계가 1029257이어야 하므로 의미가 없습니다. 내가 뭘 잘못했나요?
답변1
awk
$2
파일을 한 줄씩 반복하므로 현재 줄의 필드가 파일에서 고유한지 여부를 알 수 없습니다 .
파일을 두 번 열어야 합니다. 첫 번째는 계산을 위한 것이고 두 번째는 인쇄를 위한 것입니다 count[$2] == 1
.
awk '
NR==FNR && FNR>1 {count[$2]++};
NR>FNR && (FNR==1 || count[$2] == 1)
' file.txt file.txt
NR==FNR
FNR>1
헤더를 계산하고 싶지 않기 때문에 첫 번째 파일에서만 true가 되도록 추가했습니다 .NR>FNR
이는 두 번째 파일에도 적용됩니다.- 헤더(
FNR==1
) 또는 인 경우count[$2]
인쇄 합니다1
.
- 헤더(
답변2
매우 유사하다@pLumo의 답변그러나 NR
두 번 비교하지 않고 두 번 FNR
테스트하지 않고 빈 필드를 포함할 가능성을 처리하지 않습니다.FNR
1
$ awk -F'\t' '
NR == FNR { if (NR > 1) cnt[$2]++; next }
cnt[$2] < 2
' file file
a b c
value5 4 0
또는 원하는 경우:
$ awk -F'\t' '
NR == FNR { cnt[$2] += (NR > 1); next }
cnt[$2] < 2
' file file
a b c
value5 4 0
위의 2단계 접근 방식의 단점은 스트림의 입력을 처리할 수 없다는 것입니다. 이 문제를 처리해야 한다면 다음과 같이 하세요.하위 단위메소드는 모든 입력을 메모리로 읽지 않고도 이를 처리할 수 있습니다( sort
요구 페이지 매김 등을 사용하여 대규모 입력을 처리하기 위한 것이지만 우리는 모든 입력을 sort
모든 고유한 두 번째 필드 값에 전달하지 않고 단 2줄 이하). 원래 순서를 유지합니다. 입력 라인 중:
$ cat tst.sh
#!/usr/bin/env bash
awk '
BEGIN { FS=OFS="\t" }
(cnt[$2] += (NR>1)) < 3 {
print NR, cnt[$2], $0
}
' "${@:--}" |
sort -t $'\t' -k4,4 -k1,1rn |
awk '
BEGIN { FS=OFS="\t" }
$4 != prev {
if ( $2 < 2 ) {
print
}
prev = $4
}
' |
sort -t $'\t' -k1,1n |
cut -d $'\t' -f3-
파일의 입력을 사용합니다.
$ ./tst.sh file
a b c
value5 4 0
그리고 스트림/파이프에서 입력:
$ cat file | ./tst.sh
a b c
value5 4 0
위 스크립트는 모든 버전의 awk, sort 및 cut에서 작동합니다.
답변3
pLumo의 설명에 따르면그들의 대답에, 모든 레코드를 읽을 때까지 어떤 레코드가 고유한지 알 수 없습니다. 그들의 대답은 파일을 두 번 읽음으로써 이 문제를 해결합니다. 이 문제를 해결하는 또 다른 방법은 데이터를 메모리로 읽는 것입니다. 데이터 양이 "작은" 경우(상황에 따라 "작은"에 대한 일부 정의) 이 작업을 수행할 수 있습니다.
이는 다음과 같이 수행할 수 있습니다 awk
.
awk 'NR == 1 { head = $0; next }
{ count[$2]++; text[$2] = $0 }
END { print head; for (i in count) if (count[i] == 1) print text[i] }' file
...주어진 데이터에 대해 출력됩니다.
a b c
value5 4 0
사용밀러( )는 모든 레코드를 메모리로 읽어 들인 후 동일한 값을 가진 레코드 수를 포함하는 mlr
새 열을 계산합니다 . 그런 다음 해당 개수를 기준으로 레코드를 필터링하고 개수가 1인 레코드(고유 레코드)만 유지한 다음 해당 필드를 제외한 모든 항목을 출력합니다.count
b
count
$ mlr --p2t count-similar -g b then filter '$count == 1' then cut -x -f count file
a b c
value5 4 0
이 옵션은 (input beautifulprint) 다음에 (output tab-delimited) 가 오는 약어입니다 --p2t
. 데이터가 탭으로 구분된 경우 대신 사용하세요.--ipprint
--otsv
--tsv
--p2t
답변4
솔루션은 datamash
및 를 사용합니다 awk
.
datamash -H -sW count 1 -f -g 2 <input |
awk '$NF == 1 || NR == 1 {$NF = ""; print}'
사용 gawk
:
awk 'NR==1;
NR>1 { if (ar[$2]) ar[$2] = "repeated";
else ar[$2] = $0 }
END { for (i=1; i<=length (ar); i++) if (ar[i] != "repeated") print ar[i] }'