열 2가 열 1의 두 개 이상의 값에 해당하는 경우 모든 행을 삭제합니다.

열 2가 열 1의 두 개 이상의 값에 해당하는 경우 모든 행을 삭제합니다.

다음과 같은 탭으로 구분된 테이블이 있습니다.

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 , 사용하세요밀러

기본 단계는 다음과 같습니다.

  1. nest --implode각각에 해당하는 모든 값을 별도의 목록으로 모으는 데 사용됩니다.$1$2

  2. 고유한 값을 계산하는 필터 목록$1

  3. 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

노트:

  1. 다시 알아낼 수 있을지 의심스럽기 때문에 내 참고용으로 더 많은 정보를 제공합니다.

관련 정보