다음과 같은 탭으로 구분된 테이블이 있습니다.
GL89 AADAC
GL89 AFGAC
GL89 AFDAC
GL50 AC923
GL50 AC923
GL79 AC923
GL99 AC923
GL99 AC923
GL60 AC100
GL60 AC100
GL20 AC200
GL30 AC300
GL30 AC400
2열의 값 중 하나 이상이 1열의 값 2개 이상에 해당하는 행을 제거하고 싶은데, 이 경우 다음 행을 삭제해야 합니다.
GL50 AC923
GL79 AC923
GL99 AC923
GL99 AC923
나머지 테이블은 유지하세요.
GL89 AADAC
GL89 AFGAC
GL89 AFDAC
GL60 AC100
GL60 AC100
GL20 AC200
GL30 AC300
GL30 AC400
양식이 있나요? 감사합니다!
답변1
awk 'BEGIN{ FS=OFS="\t" }
{ data[$2]= (data[$2]==""?"":(k[$2]==$1? data[$2] ORS: "@") ) $0; k[$2]=$1 }
END{ for(x in data) if(data[x] !~/^@/) print data[x] }' infile
참고: 인쇄할 때 인쇄하면 안되는 레코드를 표시하기 위해 문자를 사용하고 있으므로 @
이 문자가 입력 파일에 있어서는 안 됩니다. 그렇지 않으면 다른 것으로 변경해야 합니다(또는 다음과 같은 문자 세트를 선택하십시오). 대신 문자열).
data[$2]= (data[$2]==""?"":(XXX) ) $0
, 배열이 비어 있지 않으면data
해당 섹션의 결과로 배열 값을 업데이트하고 현재 행을 추가합니다.(XXX)
이 열은$2
배열 키로 사용됩니다.(XXX)
(k[$2]==$1? data[$2] ORS: "@")
동일한 키의 값(키의 최신 값 쌍을 유지하기 위해 배열을 도우미로 사용)이 다른 경우 at 기호 문자가 설정되고, 그렇지 않으면 키t
에 내용 + 개행 문자(ORS)가 추가됩니다. ).@
마지막으로 두 번째 열은 동일하지만 첫 번째 열이 1개 이상 다른 모든 행 은 코드에서 특정 문자로 표시했기 때문에 제거됩니다 .
코드를 더 잘 이해하기 위해 print ...
언제든지 이 명령문을 사용하여 무슨 일이 일어나고 있는지 확인할 수 있습니다.
awk 'BEGIN{ FS=OFS="\t" }
{ data[$2]= (data[$2]==""?"":(k[$2]==$1? data[$2] ORS: "***") ) $0; k[$2]=$1 }
END{ for(x in data) print "<" data[x] ">" }' infile
방금 원래 명령에서 삭제된 레코드를 at 기호로 표시했는데, ***
해당 표시로 시작하는 레코드가 여기에 표시됩니다.
답변2
지원되는 awk를 사용하십시오 length(array)
.
$ cat tst.awk
BEGIN { FS=OFS="\t" }
$2 != prev {
if ( NR > 1 ) {
prt()
}
prev = $2
}
{ cnt[$1]++ }
END { prt() }
function prt( val,i) {
if ( length(cnt) == 1 ) {
for (val in cnt) {
for (i=1; i<=cnt[val]; i++) {
print val, prev
}
}
}
delete cnt
}
$ sort -t$'\t' -k2,2 -k1,1 file | awk -f tst.awk
GL89 AADAC
GL60 AC100
GL60 AC100
GL20 AC200
GL30 AC300
GL30 AC400
GL89 AFDAC
GL89 AFGAC
답변3
이 문제에 적합한 데이터 구조는 다음 set
과 같습니다.dictionary
그리고 파이썬에는 이 두 가지 기능이 모두 내장되어 있습니다.
python3 -c 'import sys
ifile = sys.argv[1]
fs,ors = "\t","\n"
d = {}; L = {}
with open(ifile) as fh:
for l in fh:
c1,c2 = l.rstrip().split(fs)
if c2 in d:
d[c2].add(c1)
L[c2].append(l.rstrip())
else:
d[c2] = { c1 }
L[c2] = [ l.rstrip() ]
print(*[l
for k,v in d.items()
if len(v) == 1
for l in L[k]
], sep=ors)
' file
산출:
GL89 AADAC
GL89 AFGAC
GL89 AFDAC
GL60 AC100
GL60 AC100
GL20 AC200
GL30 AC300
GL30 AC400
답변4
단지 재미를 위해서1 , 사용하세요밀러
기본 단계는 다음과 같습니다.
nest --implode
각각에 해당하는 모든 값을 별도의 목록으로 모으는 데 사용됩니다.$1
$2
고유한 값을 계산하는 필터 목록
$1
nest --explode
필터링된 값을$1
별도의 레코드로 확장하는 데 사용됩니다 .
GL50;GL50;GL79;GL99;GL99
(2)단계에서는 해시맵의 키로 변환하는 것처럼 구분된 목록에서 중복 요소를 제거할 수 있습니다 . 불행하게도 내장된 DSL 문자열-해시맵 함수는 키 값에서만 작동합니다 splitkv
.splitkvx
오른쪽- 별도의 구분 키 문자열을 해시 맵(임의 또는 null 값 포함)으로 변환할 수 있는 방법(예: null 쌍 구분 기호 전달)이 없는 것 같습니다. 따라서 우리는 문자열을 인덱스 맵으로 분할하여 자체 맵을 굴려야 합니다.가치새로운 지도에 입장하기 위한 열쇠입니다.
(2)와 (3)단계는 단지 우리가 필요하기 때문에 필요하다는 점에 유의하십시오.아니요여러 값을 필터링하고 싶습니다.동일한 $1
값 - 그렇지 않으면 단순히 내파된 문자열의 인덱스 맵 길이를 테스트할 수 있습니다(그리고 분해된 결과는 필요하지 않습니다).
그래서
$ mlr --nidx --fs tab nest --ivar ';' -f 1 then filter '
func splitkx(s,t):map {
var m = {};
for(k,v in splitnvx(s,t)){m[v] = 1};
return m
}
length(splitkx($1,";")) == 1' then nest --evar ';' -f 1 file
GL89 AADAC
GL89 AFGAC
GL89 AFDAC
GL60 AC100
GL60 AC100
GL20 AC200
GL30 AC300
GL30 AC400
노트:
- 다시 알아낼 수 있을지 의심스럽기 때문에 내 참고용으로 더 많은 정보를 제공합니다.