提取两个文件之间按顺序交换的行的索引

par*_*nam 3 bash diff awk file-comparison

我有两个大型制表符分隔文件(>10GB),我知道当它们排序时,它们的内容是相同的。

但是,我对行的顺序和交换的行的索引感兴趣,当它们共享相同的“键”时(此处的键定义为基于SourceLocation列分组的行)。

换句话说,只有当这两个文件之间的行来自同一组时(即,当它们共享相同的源和位置时),才应相互比较。

例如,在下面的示例中,第 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
Run Code Online (Sandbox Code Playgroud)

文件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
Run Code Online (Sandbox Code Playgroud)

我想做一些类似于“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
Run Code Online (Sandbox Code Playgroud)

假设:

  • 两个数据框具有相同的行数
  • 两个数据帧是相同的(仅交换行的顺序,因此如果两者都按源排序,然后按位置排序,然后按位置排序,然后按标识符排序,那么它们将完全相同)
  • “交换”的行在所有列的内容方面始终完全匹配

Ed *_*ton 5

由于输入文件的大小,这是我可能会使用的罕见情况之一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
}
Run Code Online (Sandbox Code Playgroud)

$ 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
Run Code Online (Sandbox Code Playgroud)

有关 的更多信息getline,请阅读http://awk.freeshell.org/AllAboutGetline

即使输入中重复了Identifierand/or ,上面的代码也会起作用Position,因为它比较了 2 个文件之间的所有 4 个字段。它确实假设两个文件之间的源值和位置值的顺序相同,如示例输入所示。