두 파일 사이에서 순차적으로 교체된 라인의 인덱스를 추출합니다.

두 파일 사이에서 순차적으로 교체된 라인의 인덱스를 추출합니다.

탭으로 구분된 두 개의 큰 파일(>10GB)이 있고 정렬하면 해당 내용이 동일하다는 것을 알고 있습니다.

Source그러나 동일한 "키"(여기서 키는 열을 기반 으로 Location그룹화된 행 으로 정의됨)를 공유할 때 행의 순서와 교체된 행의 인덱스에 관심이 있습니다 .

즉, 이 두 파일 사이의 행은 동일한 그룹에 속해 있는 경우에만(즉, 동일한 소스와 위치를 공유하는 경우) 서로 비교되어야 합니다.

예를 들어, 아래 예에서 행 4, 5, 6은 file1.tsv다음의 행 4, 5, 6과 비교되어야 합니다.file2.tsv

참고: 파일은 일반 TSV입니다. 더 나은 가시성을 위해 여기에 추가 공간을 추가하고 열을 중앙에 정렬하고 오른쪽으로 정렬하세요. 이 공백은 원본 파일의 일부가 아닙니다.

파일 1.tsv

     Identifier  Position Source  Location
     AY1:2301        87    ch1        14
    BC1U:4010       105    ch1        14
    AC44:1230        90    ch1        15
    AJC:93410        83    ch1        16
    ABYY:0001       101    ch1        16
       ABC:01        42    ch1        16
      HH:A9CX       413    ch1        17
      LK:9310         2    ch1        17
    JFNE:3410       132    ch1        18
    MKASDL:11        14    ch1        18
   MKDFA:9401        18    ch1        18
  MKASDL1:011       184    ch2        50
   LKOC:AMC02        18    ch2        50
     POI:1100       900    ch2        53
    MCJE:09HA        11    ch2        53
   ABYCI:1123        15    ch2        53
     MNKA:410         1    ch2        53

파일 2.tsv

     Identifier  Position Source  Location
     AY1:2301        87    ch1        14
    BC1U:4010       105    ch1        14
    AC44:1230        90    ch1        15
       ABC:01        42    ch1        16
    ABYY:0001       101    ch1        16
    AJC:93410        83    ch1        16
      HH:A9CX       413    ch1        17
      LK:9310         2    ch1        17
    MKASDL:11        14    ch1        18
    JFNE:3410       132    ch1        18
   MKDFA:9401        18    ch1        18
  MKASDL1:011       184    ch2        50
   LKOC:AMC02        18    ch2        50
     MNKA:410         1    ch2        53
     POI:1100       900    ch2        53
   ABYCI:1123        15    ch2        53
    MCJE:09HA        11    ch2        53

"diff"와 유사하지만 "그룹" 수준에서 수행하고 싶습니다(행이 Source동일한 합계를 공유하는 경우에만 비교됨 Location).

추출하고 싶어요원래동일한 "소스/위치" 내에서 줄의 순서가 "교환"되면 "줄 번호"그룹"(또는 키).

전체 줄의 내용이 일치해야 합니다.

하지만 어떻게 해야할지 모르겠습니다. 내가 생각할 수 있는 것은 for 루프를 작성하는 것뿐입니다. 이는 원래 데이터 세트에 수백만 개의 행이 있을 때 매우 비효율적입니다.

예상되는 결과:

Group_Source:Location  df1.index  df2.index

ch1:16                         4          6
ch1:16                         6          4
ch1:18                         9         10
ch1:18                        10          9
ch2:53                        14         15
ch2:53                        15         17
ch2:53                        17         14

가정:

  • 두 데이터 프레임 모두 동일한 수의 행을 갖습니다.
  • 두 데이터프레임은 동일합니다(행 순서만 바뀌므로 둘 다 소스 기준, 위치 기준, 위치 기준, 식별자 기준으로 정렬된 경우 정확히 동일합니다).
  • "교환된" 행은 항상 모든 열의 내용과 정확히 일치합니다.

답변1

입력 파일의 크기로 인해 이는 제가 사용할 수 있는 드문 경우 중 하나이므로 getline>10G 대신 한 번에 몇 줄만 메모리에 유지합니다.

$ cat tst.awk
BEGIN {
    OFS = "\t"
    print "Group_Source:Location", "df1.index", "df2.index"
}
NR != FNR { exit }
{ srcLoc = $3 ":" $4 }
srcLoc != prevSrcLoc {
    if ( NR > 1 ) {
        diff()
    }
    prevSrcLoc = srcLoc
}
{
    file1[$1,$2] = FNR - 1
    if ( (getline < ARGV[2]) > 0 ) {
        file2[$1,$2] = FNR - 1
    }
}
END { diff() }

function diff(          idPos) {
    for ( idPos in file1 ) {
        if ( file1[idPos] != file2[idPos] ) {
            print prevSrcLoc, file1[idPos], file2[idPos]
        }
    }
    delete file1
    delete file2
}

$ awk -f tst.awk file1.tsv file2.tsv
Group_Source:Location   df1.index       df2.index
ch1:16  6       4
ch1:16  4       6
ch1:18  10      9
ch1:18  9       10
ch2:53  17      14
ch2:53  15      17
ch2:53  14      15

더 많은 정보를 getline원하시면 읽어주세요http://awk.freeshell.org/AllAboutGetline.

Identifier위 코드는 2개 파일 간의 4개 필드를 모두 비교하기 때문에 입력에서 및/또는이 반복되는 경우에도 작동합니다. Position예제 입력에 표시된 것처럼 소스 및 위치 값이 두 파일 간에 동일한 순서로 있다고 가정합니다.

답변2

예를 들면 다음과 같습니다 awk.

$ awk '{ 
        if(FNR==1){
            next
        }
        else if(FNR==NR){
            a[$1]=FNR-1;
        } 
        else if ( a[$1] != FNR-1 ){
            print $3":"$4, FNR-1, a[$1]
        }
    }' file1.tsv file2.tsv 
ch1:16 4 6
ch1:16 6 4
ch1:18 9 10
ch1:18 10 9
ch2:53 14 17
ch2:53 15 14
ch2:53 17 15

설명하다

  • if(FNR==1){ next }: FNR현재 읽고 있는 파일의 라인 번호(레코드 번호)를 저장합니다. 따라서 이것이 입력 파일의 첫 번째 줄인 경우 헤더를 처리하고 싶지 않으므로 건너뛰십시오.
  • else if(FNR==NR){ ... }: NR어떤 파일을 읽고 있는지에 관계없이 현재 입력 줄 번호를 저장합니다. 따라서 FNR같으면 NR첫 번째 파일을 읽고 있다는 의미입니다.
  • a[$1]=FNR-1: 따라서 이것이 첫 번째 파일인 경우 첫 번째 필드를 연관 배열의 인덱스(키)로 저장하고 해당 값은 현재 파일의 줄 번호( FNR)가 되지만 계산을 원하지 않기 때문에 1을 뺍니다. 머리글 .
  • else if ( a[$1] != FNR-1 ){: 이는 else if이전 파일과 관련되어 있으므로 FNR이 파일이 와 같지 않은 경우에만 입력하므로 NR두 번째 파일을 읽는 경우에만 입력됩니다. 그래서 우리가 두 번째 파일을 읽는다면그리고이 줄의 첫 번째 필드에 있는 배열에 저장된 값은 a현재 파일의 줄 번호에서 1을 뺀 것과 같지 않으므로 인쇄하려고 합니다.
  • print $3":"$4, FNR-1, a[$1]: 따라서 우리는 세 번째 필드인 a :와 네 번째 필드를 인쇄한 다음 FNR 빼기 1과 a첫 번째 필드의 배열에 저장된 값을 인쇄합니다.

마지막으로 패딩과 헤더를 사용하여 예쁘게 인쇄하려면 다음을 사용하세요.

$ awk 'BEGIN{
            printf "%-26s%-12s%-12s\n", \
                "Group_Source:Location","df1.index","df2.index"
        } 
        { 
            if(FNR==1){ next }
            else if(FNR==NR){ a[$1]=FNR-1 } 
            else if ( a[$1] != FNR-1){
                printf "%-26s%-12s%-12s\n", $3":"$4, FNR-1, a[$1]
            }
        }' file1.tsv file2.tsv 
Group_Source:Location     df1.index   df2.index   
ch1:16                    4           6           
ch1:16                    6           4           
ch1:18                    9           10          
ch1:18                    10          9           
ch2:53                    14          17          
ch2:53                    15          14          
ch2:53                    17          15          

중요한: 이 방법을 사용하려면 첫 번째 파일의 각 줄(헤더 제외)에 대해 메모리에 소량의 데이터를 유지해야 합니다. 이는 대용량 파일의 경우 문제가 될 수 있지만 이러한 작업이 수행될 가능성이 있는 대부분의 컴퓨터에서는 그렇지 않을 수도 있습니다. 이것이 문제라면 추천하고 싶습니다에드의 대답이는 훨씬 더 빠르고 메모리 문제도 없어야 합니다.

관련 정보