【问题标题】:awk to match, merge two files while modifying column input and adding an extra column to the output fileawk 进行匹配,合并两个文件,同时修改列输入并将额外的列添加到输出文件
【发布时间】:2016-08-28 19:20:04
【问题描述】:

我想合并 2 个文件并将其分配给一个新文件,同时最好在 unix 中使用 awk 添加一个新的不存在的列:

文件 1:VDR.txt 没有标题,是空格分隔的,如下所示:

chr12-45000000-50000000 --- rs192072617 48225416 0.000 0.270 0.999 0 -1 -1 -1
chr12-45000000-50000000 --- rs181728325 48225429 0.000 0.144 1.000 0 -1 -1 -1
chr12-45000000-50000000 --- rs187216594 48225500 0.000 0.007 1.000 0 -1 -1 -1

文件 2:METAL1.tbl 有一个标题,是制表符分隔的,如下所示:

MarkerName      Allele1 Allele2 Weight  Zscore  P-value Direction       HetISq  HetChiSq        HetDf   HetPVal
rs192072617       a       g       2887.00 1.579   0.1143  ++      0.0     0.032   1       0.8579
rs7929618       c       g       2887.00 -1.416  0.1568  -+      47.4    1.899   1       0.1681
rs181728325      t       c       2887.00 1.469   0.1419  ++      73.9    3.830   1       0.05033
rs7190157       a       c       2887.00 1.952   0.05088 +-      72.7    3.669   1       0.05542
rs12364336      a       g       2887.00 -1.503  0.1328  -+      69.8    3.306   1       0.06902
rs187216594       t       c       2887.00 -0.082  0.9349  +-      74.8    3.964   1       0.04649
rs12562373      a       g       2887.00 -0.290  0.7717  -+      0.0     0.150   1       0.6984

文件的行数不相等,第一个文件 (VDR.txt) 比第二个文件 (METAL1.tbl) 短得多。

我想:

  1. 按第一个文件 (VDR.txt) 的第 3 列和第二个文件 (METAL1.tbl) 的第 1 列合并这些文件。
  2. 仅保留第一个文件 (VDR.txt) 中的第 1、2、3 和 4 列以及第二个文件 (METAL1.tbl) 中的所有列。
  3. 仅保留第一个文件 (VDR.txt) 第一列中第一个破折号 "-" 之前的字符
  4. 在输出文件中添加一个重复某个字符串(例如“VDR”)的新列
  5. 输出文件不必有标题,但如果有必要,最好有下面给出的标题。

所以我想要一个最后看起来像这样的输出文件(output.txt):

gene    MarkerName  chr BP  impute  Allele1 Allele2 Weight  Zscore  P-value Direction   HetISq  HetChiSq    HetDf   HetPVal
VDR rs192072617 chr12   48225416    --- a   g   2887    1.579   0.1143  ++  0   0.032   1   0.8579
VDR rs181728325 chr12   48225429    --- t   c   2887    1.469   0.1419  ++  73.9    3.83    1   0.05033
VDR rs187216594 chr12   48225500    --- t   c   2887    -0.082  0.9349  +-  74.8    3.964   1   0.04649

我的尝试:

$ awk 'FNR==NR {a[$1]=$1" "$2" "$3" "$4" "$5;next}{print $3, gensub(/-.*/, "", $1), $4, $2, a[$3]}' METAL1.tbl VDR.txt

它确实以正确的方式获取了 chr 列和列顺序,但不幸的是,它只打印了 VDR.txt 中想要的列,而不是合并的文件。

我知道这是一个非常复杂的示例,任何帮助或建议将不胜感激。

谢谢,
梅尔

【问题讨论】:

  • 那么你有没有尝试解决这个问题?如果是这样,你应该分享这个。 “这是我的规格,请为我编写代码”形式的问题通常不受欢迎,但“这是我的代码,这是我遇到问题的部分”形式的问题。
  • 文件中的字段是如何识别的?固定宽度?或者分隔符是什么?
  • 我建议将您的数据样本减少到 4 列左右(足以解决根本问题)。你知道unix/linux命令join吗?如果您可以使用分隔数据(如| 或制表符),您可能可以在 1 行中执行您想要的操作。但是我没有仔细查看您的输入和输出(因为它们太宽了;-),所以我可能是错的。祝你好运。
  • 您确实需要简化或澄清您的项目符号 (3) 仅保留第一个文件 (VDR.txt) 第一列中第一个破折号之前的字符 -- 在第一个破折号之前而不是前 4 个没有破折号的染色体数小于 10(例如,不希望有 chr7- 但 chr7)。 您的示例数据没有 chr7 引用,所以它不是清楚这是怎么一回事。从chr12-45000000-50000000 类型名称中获取chr12 很容易理解,但是您只是想从chr7-45000000-50000000 中获取chr7 吗?你既没有展示也没有解释这个额外的皱纹。
  • @JonathanLeffler 我简化了项目符号 3。对不起,我想编写一个功能多样的函数,可以用于可能有一些差异的其他文件。 (但不幸的是有点懒得给出正确的例子)---另外非常感谢你的解决方案,工作得很好而且速度也很快!

标签: bash unix awk merge


【解决方案1】:

只要不需要标题行,就可以直接在一个相当简单的awk 脚本中:

$ awk 'FNR == NR { sub(/-.*/, "", $1); row[$3] = "VDR " $3 " " $1 " " $4 " " $2 }
>      FNR != NR { if ($1 in row) { name = $1; $1 = ""; print row[name] $0 } }' \
>      VDR.txt METAL1.tbl
VDR rs192072617 chr12 48225416 --- a g 2887.00 1.579 0.1143 ++ 0.0 0.032 1 0.8579
VDR rs181728325 chr12 48225429 --- t c 2887.00 1.469 0.1419 ++ 73.9 3.830 1 0.05033
VDR rs187216594 chr12 48225500 --- t c 2887.00 -0.082 0.9349 +- 74.8 3.964 1 0.04649
$

文件必须按显示的顺序列出才能正常工作。

FNR == NR 行处理第一个文件。 sub 消除了第一个破折号以及第一个字段中的所有内容;分配由$3 中的标记名称键入,并包含行开头的信息——固定代码、标记名称、减少的染色体数、BP 和标记为“Impute”的破折号集。

FNR != NR 行处理其他文件。当第 1 列中的值与 row 数组中的某个键匹配时,则从当前行中删除该键(在$0 的开头留下一个空白),然后将来自row 的值串联打印$0

标题行无需特殊处理; MarkerName 的值与第一个文件中的任何实际标记名称都不匹配,因此该行将被忽略。

【讨论】:

  • 并添加BEGIN {print "header you want"}
【解决方案2】:
$ cat > test.awk
NR==FNR {
    sub(/-.*/,"",$1)                                      # remove from 1st dash forward
    a[$3]="VDR" OFS $3 OFS $1 OFS $4 OFS $2               # cols 1-4 of the 1st file
    next                                        
} 
FNR==1 {
    printf "%s", "H0" OFS "H3" OFS "H1" OFS "H4" OFS "H2" # 1st part of header
} 
FNR==1 || $1 in a {                                       # header and matching rows
    print a[$1], $0                                       # print'em
}
$ awk -f test.awk VDR.txt METAL1.tbl
H0      H3      H1      H4      H2      MarkerName      Allele1 Allele2 Weight Zscore   P-value Direction       HetISq  HetChiSq        HetDf   HetPVal
VDR     rs192072617     chr12   48225416        ---     rs192072617     a      g2887.00 1.579   0.1143  ++      0.0     0.032   1       0.8579
VDR     rs181728325     chr12   48225429        ---     rs181728325     t      c2887.00 1.469   0.1419  ++      73.9    3.830   1       0.05033
VDR     rs187216594     chr12   48225500        ---     rs187216594     t      c2887.00 -0.082  0.9349  +-      74.8    3.964   1       0.04649

作为单行:

awk 'NR==FNR { sub(/-.*/,"",$1); a[$3]="VDR" OFS $3 OFS $1 OFS $4 OFS $2; next} FNR==1 {printf "%s", "H0" OFS "H3" OFS "H1" OFS "H4" OFS "H2"} FNR==1 || $1 in a {print a[$1], $0}' VDR.txt METAL1.tbl

【讨论】:

  • 非常感谢您的帮助,这很好用,但我正在寻找像 Jonathan Leffler 的解决方案这样的单线解决方案。
【解决方案3】:

我已经对两个数据文件进行了排序以使用 join 命令 - 这会影响输出中的行顺序 - 如果不希望这样做,我可以使用另一种方法

export LANG=C
genef=$1
metalf=$2
gene=$(basename $genef .txt)
join -13 -21 <(sort -k3,3 $genef) <(sort -k1,1 $metalf)|
awk -vgene=$gene '
{
  marker=$1
  chr=substr($2, 1, index($2, "-")-1)
  bp=$4
  impute=$3
  printf("%s\t%s\t%s\t%s\t%s", gene, marker, chr, bp, impute)
  for(i=12; i<=NF; ++i)
    printf("\t%s", $i)
  printf("\n")
}
'

这是制表符分隔的输出

VDR     rs181728325     chr12   48225429        ---     t       c       2887.00 1.469    0.1419  ++      73.9    3.830   1       0.05033
VDR     rs187216594     chr12   48225500        ---     t       c       2887.00 -0.082   0.9349  +-      74.8    3.964   1       0.04649
VDR     rs192072617     chr12   48225416        ---     a       g       2887.00 1.579    0.1143  ++      0.0     0.032   1       0.8579

【讨论】:

  • 感谢您的建议,但我不喜欢 join 的原因是我必须订购文件...这对于第一个文件 (VDR.txt) 来说是一件容易的事,但是不是第二个(METAL1.tbl),它是一个非常大(~ 600 Mb)的文件。对 METAL1.tbl 进行排序只会不断给我错误并增加了更多的处理时间,这就是我在 awk 周围寻找解决方案的原因。
  • 好点 - 600MB 的速度相当快 - 但 awk 的单次传递总是好的 - 你在 sort 中看到什么错误?
猜你喜欢
  • 2015-02-11
  • 2015-07-06
  • 1970-01-01
  • 1970-01-01
  • 2019-08-27
  • 1970-01-01
  • 2014-02-20
  • 2020-10-02
  • 1970-01-01
相关资源
最近更新 更多