탭으로 구분된 파일의 단일 열에 있는 중복 항목에 다른 문자열을 추가하여 고유하게 만드는 방법

탭으로 구분된 파일의 단일 열에 있는 중복 항목에 다른 문자열을 추가하여 고유하게 만드는 방법

4개 열의 탭으로 구분된 파일이 있는데 마지막 열에 중복되는 경우가 있습니다. 다음은 문서에서 발췌한 내용입니다.

chr7    116038644       116039744       GeneA
chr7    116030947       116032047       GeneA
chr7    115846040       115847140       GeneA
chr7    115824610       115825710       GeneA
chr7    115801509       115802609       GeneA
chr7    115994986       115996086       GeneA
chrX    143933024       143934124       GeneB
chrX    143933119       143934219       GeneB
chrY    143933129       143933229       GeneC

열의 각 중복 값 집합에 대해 다음과 같이 변환하고 싶습니다(열의 중복되지 않은 값을 실제로 건드리지 않고).

chr7    116038644       116039744       GeneA-1
chr7    116030947       116032047       GeneA-2
chr7    115846040       115847140       GeneA-3
chr7    115824610       115825710       GeneA-4
chr7    115801509       115802609       GeneA-5
chr7    115994986       115996086       GeneA-6
chrX    143933024       143934124       GeneB-1
chrX    143933119       143934219       GeneB-2
chrY    143933129       143933229       GeneC

또는 Bash awk루프 를 사용하여 이 작업을 어떻게 수행할 수 있나요 ?sedfor

답변1

이 시도

awk -F'\t' -v OFS='\t' '{$4=$4 "-" (++count[$4])}1' file.tsv

이는 네 번째 필드 값의 각 발생을 카운터 배열 count(네 번째 필드의 값이 "인덱스"로 사용됨)에 저장하고 해당 카운터의 사전 증가된 값을 네 번째 필드와 대시에 추가합니다.

위의 "간단한" 예에는 한 가지 단점이 있습니다.파일에 한 번만 나타나는 열 4의 값에 명확성 번호를 추가하기도 합니다. 이를 억제하려면 다음과 같은 이중 전달 접근 방식을 사용할 수 있습니다(가독성을 높이기 위해 명령을 두 줄로 분할 \).

 awk -F'\t' -v OFS='\t' 'NR==FNR{f[$4]++}\
      NR>FNR{if (f[$4]>1) {$4=$4 "-" (++count[$4])}; print}' file.tsv file.tsv

처리할 파일이 기록되어 있습니다.두 배매개변수로 사용되므로 두 번 읽혀집니다.

  • 첫 번째 읽기( 전역 FNR행 카운터와 동일한 파일별 NR행 카운터로 표시됨)에서는 열 4의 각 고유 값이 파일에 나타나는 빈도를 간단히 계산하고 이를 배열에 저장합니다 f.
  • 두 번째로 파일을 읽을 때 "간단한" 방법과 같은 실제 텍스트 처리를 수행하고 발생 카운터를 열 4에 추가합니다. 단, 첫 번째 패스에서 발견된 총 발생 횟수가 1보다 큰 경우에만 해당됩니다.

이 접근 방식은 전체 파일의 버퍼링을 방지하므로 파일이 매우 큰 경우 이점이 될 수 있습니다. 물론 파일을 두 번 읽으므로 처리 시간이 길어집니다.

일반적으로 텍스트 처리를 위해 쉘 루프를 사용할 필요는 거의 없습니다. awk 예를 들어 루프 작업이 더 효율적인 방식으로 자체적으로 수행될 수 있기 때문입니다.

답변2

예제에 표시된 대로 입력 파일이 네 번째 열로 그룹화되어 있다고 가정합니다.

$ cat tst.awk
$NF != prev {
    prt()
    cnt = 0
    prev = $NF
}
{ rec[++cnt] = $0 }
END { prt() }

function prt() {
    for (i=1; i<=cnt; i++) {
        print rec[i] (cnt > 1 ? "-"i : "")
    }
}

.

$ awk -f tst.awk file
chr7    116038644       116039744       GeneA-1
chr7    116030947       116032047       GeneA-2
chr7    115846040       115847140       GeneA-3
chr7    115824610       115825710       GeneA-4
chr7    115801509       115802609       GeneA-5
chr7    115994986       115996086       GeneA-6
chrX    143933024       143934124       GeneB-1
chrX    143933119       143934219       GeneB-2
chrY    143933129       143933229       GeneC

답변3

이것은 "-만 추가됩니다.숫자해당 값이 고유하지 않으면 지정된(대상) 필드(예제에서는 네 번째 필드)에 추가됩니다. 또한 입력이 대상 열을 기준으로 정렬되지 않은 경우를 처리하고 입력 열 수에 관계없이 작동합니다.

다음 AWK 스크립트는 대상 필드별로 입력을 정렬해야 하므로 파이프를 사용하여 원래 행의 번호를 매기고 (현재) 다섯 번째 필드(첫 번째 필드가 선행 숫자임)로 정렬하고 비-행에 접미사를 추가합니다. 다섯 번째 필드의 고유 값으로, 행을 원래 순서로 되돌리고 선행 숫자를 제거합니다.

nl file | sort -b -t '<TAB>' -k5,5 -k1n,1n | awk -F '\t' -v OFS='\t' -v kf=5 '
  function prn () {
    for (i = 1; i <= nfl; i++) {
      if (i == kf)
        printf("%s", prc[i] ( sw || cnt[prc[i]] ? "-"++cnt[prc[i]] : ""))
      else
        printf("%s", prc[i])
      printf("%s", (i == nfl ? ORS : OFS))
    }
  }
  NR > 1 {
    sw = ($kf == prc[kf])
    prn()
  }
  {
    nfl = split($0, prc)
  }
  END {
    if (NR > 0)
      prn()
  } ' | sort -k1n,1n | cut -f 2-

이 AWK 스크립트의 요점은 인쇄하는 것입니다.더 일찍kf행의 첫 번째 필드가 현재 행의 필드와 같은지 또는 kf첫 번째 필드가 한 번 이상 발생했는지 확인합니다 . 두 경우 모두, kf번째 필드는 추가된 횟수와 함께 인쇄됩니다.

명확하게 하려는 열의 실제 위치를 반영하도록 -v kf=5(및 키) 를 조정해야 합니다 .-k5,5 sort

다음 예(행을 섞고 열을 추가한 예)를 고려하면 다음과 같습니다 file.

chr7    116038644   116039744   GeneA   foo
chrX    143933024   143934124   GeneB   foo
chr7    116030947   116032047   GeneA   foo
chr7    115824610   115825710   GeneA   foo
chrY    143933129   143933229   GeneC   foo
chr7    115994986   115996086   GeneA   foo
chrX    143933119   143934219   GeneB   foo
chr7    115801509   115802609   GeneA   foo
chr7    115846040   115847140   GeneA   foo

출력은 다음과 같습니다:

chr7    116038644   116039744   GeneA-1 foo
chrX    143933024   143934124   GeneB-1 foo
chr7    116030947   116032047   GeneA-2 foo
chr7    115824610   115825710   GeneA-3 foo
chrY    143933129   143933229   GeneC   foo
chr7    115994986   115996086   GeneA-4 foo
chrX    143933119   143934219   GeneB-2 foo
chr7    115801509   115802609   GeneA-5 foo
chr7    115846040   115847140   GeneA-6 foo

답변4

간단한 2단계 awk명령:

$ awk -F '\t' '
    BEGIN { OFS=FS }
    pass == 1 { count[$4]++; next }
    count[$4] > 1 { $4 = $4 "-" ++number[$4] }; 1' pass=1 file pass=2 file
chr7    116038644       116039744       GeneA-1
chr7    116030947       116032047       GeneA-2
chr7    115846040       115847140       GeneA-3
chr7    115824610       115825710       GeneA-4
chr7    115801509       115802609       GeneA-5
chr7    115994986       115996086       GeneA-6
chrX    143933024       143934124       GeneB-1
chrX    143933119       143934219       GeneB-2
chrY    143933129       143933229       GeneC

이는 입력 파일이 탭으로 구분되어 있다고 가정합니다. -F '\t'그렇지 않은 경우 수정하거나 삭제하세요.

파일을 처음 탐색할 때 연관 배열은 count네 번째 열에 있는 각 유전자 이름의 발생 횟수로 채워집니다.

파일을 두 번째로 통과할 때 데이터세트에 유전자 이름이 여러 번 나타나는 경우(파일을 통해 첫 번째 통과 시) 대시와 숫자가 유전자 이름 끝에 추가됩니다.

counter추가된 숫자는 배열 에서와 마찬가지로 유전자 이름으로 입력되는 또 다른 카운터입니다 .

관련 정보