当我匹配特定列时使用 gsub

Use gsub when I match specific column

我有一个包含 table DNA 碱基序列的原始文件,带有行和列标签,以及一个单独的 "position" 文件,其中列出了列标签的子集。我需要处理原始文件,对位置文件标识的列中的值执行转换。

示例原始文件:

name pos1 pos2 pos3 pos4 pos5 pos6 pos7
name1 AT TA CT GT CC TC TT
name2 AA TA TT GT TC TC TT
name3 AT TT CG AT CT TC TT
name4 GT TA CT TT CC TC TT

示例位置文件:

pos1
pos3
pos6
pos7

我需要在每个选定的字段上执行这些翻译:

A to T
C to G
G to C
T to A

因此,根据提供的位置文件处理示例原始文件得到的输出为:

name pos1 pos2 pos3 pos4 pos5 pos6 pos7
name1 TA TA GA GT CC AG AA
name2 TT TA AA GT TC AG AA
name3 TA TT GC AT CT AG AA
name4 CA TA GA TT CC AG AA

所以第一行是未修改的,在随后的每一行中,对应于列标签 pos1pos3pos6pos7 的字段被转换,而其他字段保持不变。

我知道如何使用 awk 应用 gsub() 修改整个输入行或具体修改第 nth 字段,但我需要仅修改位置文件中列出的那些字段,如数据文件第一行的列标签所标识。我如何在 awk 中实现它?

假设不必保留准确的字段(列)分隔符——也就是说,您可以自由地将每个列分隔符更改为固定字符串,例如单个 space——您可以在 per-field 的基础上使用 gsub(),然后再重建记录。这解决了限制对特定字段的更改的问题。

另一个问题是根据位置文件中的数据和列标题确定要修改的 字段。这是一种方法:

  • 使用BEGIN块,从位置文件中读取每一行并将其内容记录为数组索引。你可以认为这是将每一行的内容记录在一个散列中 table.

  • 通过遍历字段并检查它们是否存在于标签数组中,将 pre-read 列标签与从主输入的第一行读取的列标签相匹配。对于存在的那些,将字段 number 记录为第二个数组中的索引

  • 对于每个后续行,从其组成字段重建记录,根据字段编号是否记录为需要转换的字段之一,在原始值和修改后的值之间进行选择。

  • 请特别注意 awk 可以通过存储在变量中的字段编号来引用字段。因此,myfield = 2; print $myfield 产生与 print

  • 相同的输出

一个 awk 程序,它可以完成所有可能看起来像这样的事情:

#!/usr/bin/awk

function baseswap(seq) {
  gsub(/A/, "X", seq)
  gsub(/T/, "A", seq)
  gsub(/X/, "T", seq)
  gsub(/C/, "X", seq)
  gsub(/G/, "C", seq)
  gsub(/X/, "G", seq)
  return seq
}

BEGIN  {
         while ((getline < "positions") == 1) {
           labels[] = 1
         }
       }

FNR==1 {
          for (i = 2; i <= NF; i++) {
            if (labels[$i]) {
              fields[i] = 1
            }
          }
          print
          next
       }

       {
         record = 
         for (i = 2; i <= NF; i++) {
           record = record " " (fields[i] == 1 ? baseswap($i) : $i)
         }
         print record
       }
$ cat tst.awk
BEGIN {
    split("A T C G G C T A",t)
    for (i=1;i in t;i+=2) {
        map[t[i]] = t[i+1]
    }
}
NR==FNR {
    fldNames[]
    next
}
FNR==1 {
    for (i=1;i<=NF;i++) {
        if ($i in fldNames) {
            targets[i]
        }
    }
}
FNR>1 {
    [=10=] = tolower([=10=])
    for (fldNr in targets) {
        for (old in map) {
            gsub(tolower(old),map[old],$fldNr)
        }
    }
    [=10=] = toupper([=10=])
}
{ print }

$ awk -f tst.awk positions original
name pos1 pos2 pos3 pos4 pos5 pos6 pos7
NAME1 TA TA GA GT CC AG AA
NAME2 TT TA AA GT TC AG AA
NAME3 TA TT GC AT CT AG AA
NAME4 CA TA GA TT CC AG AA