awk:如何从文件A中提取在文件B中指定的列索引?

3

我的文件A格式如下,有数十万列和数千行:

1000->100001    DOSE    2.000   2.000   2.000   2.000   2.000 ....
1001->100101    DOSE    1.988   1.988   2.000   2.000   2.000 ....
1001->100101    DOSE    1.933   2.000   2.000   2.000   2.000 ....
1002->100201    DOSE    2.000   2.000   2.000   2.000   2.000 ....
1002->100201    DOSE    2.000   2.000   2.000   2.000   2.000 ....

我的文件B(数千个条目)的格式如下:

SNP,Al1,Al2,Freq1,MAF,AvgCall,Rsq,Genotyped,key,pos,gene_key
20:29649365,C,T,0.93021,0.06979,0.93021,0.10115,,803428,29649365,12
20:29649737,A,G,0.93914,0.06086,0.93914,0.14303,,803442,29649737,12
20:29649765,T,G,0.99963,0.00037,0.99963,0.13918,,803443,29649765,12
20:29650462,A,T,0.89387,0.10613,0.89388,0.12864,,803456,29650462,12

我想做的是从文件A中提取前两列和文件B中“键”列指定的列。我花了一些时间尝试使用awk来完成这个任务,但理论上不应该太难的事情却让我花费了太长时间。
我的具体问题是:一旦从文件B中提取了“键”条目,如何将这些值导入awk命令以提取文件A的内容?
文件B前三列的手动命令:
awk '{print $1, $2, $803428, $803442, $803442}' fileA > output.txt 

编辑:

文件A文件B之间没有共同的列。在文件A中,第803428列的值将代表来自文件B的SNP“20:29649365”的结果。

如果要运行的命令是:

awk '{print $1, $2, $3, $4, $5}' fileA

结果将是:
1000->100001 DOSE 2.000 2.000 2.000 2.000 2.000
1001->100101 DOSE 1.988 1.988 2.000 2.000 2.000
1002->100201 DOSE 1.933 1.999 2.000 2.000 2.000
1003->100301 DOSE 2.000 2.000 2.000 2.000 2.000
1004->100401 DOSE 2.000 2.000 2.000 2.000 2.000

我将使用具体示例来测试给出的awk命令解决方案。

fileA:

1000->100001    DOSE    2.000   2.000   2.000   2.000   2.000
1001->100101    DOSE    1.988   1.988   2.000   2.000   2.000
1001->100101    DOSE    1.933   2.000   1.500   2.000   2.000
1002->100201    DOSE    2.000   2.000   2.000   2.000   1.622
1002->100201    DOSE    2.000   2.000   2.000   2.000   2.000

文件B:

SNP,Al1,Al2,Freq1,MAF,AvgCall,Rsq,Genotyped,key,pos,gene_key
20:29649365,C,T,0.93021,0.06979,0.93021,0.10115,,3,29649365,12
20:29649737,A,G,0.93914,0.06086,0.93914,0.14303,,4,29649737,12
20:29650462,A,T,0.89387,0.10613,0.89388,0.12864,,6,29650462,12

期望输出结果(第1、2、3、4、6列 - 最后3列来自文件B的关键列):

1000->100001    DOSE    2.000   2.000   2.000
1001->100101    DOSE    1.988   1.988   2.000
1001->100101    DOSE    1.933   2.000   2.000
1002->100201    DOSE    2.000   2.000   2.000
1002->100201    DOSE    2.000   2.000   2.000

为什么是 $803428、$803442、$803442,而 $803456 被忽略了呢?另外,文件 B 中的 pos 列实际上是 29649365、29649737...,你是指 key 列吧? - Kent
请根据“fileA”和“fileB”的示例输入显示所需的输出。祝你好运。 - shellter
@Kent - 你关于pos/key错误的说法是正确的 - 我正在更改。803456只是为了举例而被忽略了。 - AndraD
3个回答

3

如果您想从文件B中读取列索引(即key列而非pos),并从文件A打印这些列,同时保留文件B中列索引的顺序,您可以尝试以下方法:

awk 'NR==FNR{c[NR]=$(NF-2);n=NR;next}
{printf "%s %s",$1,$2;
for(i=2;i<=n;i++)printf " %s",$c[i];print ""}' FS=',' fileB FS=' ' fileA

嗨,肯特,命令的效果不如预期-我在我的问题末尾放置了您的awk命令的输出。 - AndraD
@Alexandra 如果你将第三行改成 for(i=1;i<=n;i++)printf "%s ",$c[i]; print ""}' FS=...,会有帮助吗? - Kent
@Alexandra 我明白了,在你的fileB文件中,有一个标题行,这一行应该被忽略。尝试更新后的答案,它应该适用于你。 - Kent
@Alexandra 对不起,我又犯错了...应该是awk ' ...' FS=',' fileB FS=' ' fileA...下次我应该先测试一下。在fileA之前缺少了FS=' '。还有,答案已更新为$(NF-2) - Kent
非常感谢您的帮助 - 命令现在可以工作了。我已经为您的答案点赞,但是我接受了amaurea的答案,因为他先回复了我。 - AndraD
显示剩余3条评论

3

好的,这里是一个更新版,可以复制您的输出结果。

awk 'ARGIND==2&&!/SNP/{cols[++i]=$9}ARGIND==4{printf("%s %s",$1,$2);
      for(j=1;j<=i;j++)printf(" %s%s",$cols[j],j<i?"":"\n");}' FS=',' B.txt FS='[ \t]+' A.txt 

由于两个文件中的不同字段分隔符和必须忽略的B文件头部,这使得情况有点复杂。但是关键在于$接受变量而不仅仅是常数。这种方法将列的列表存储在cols数组中,并且对于A中的每一行进行迭代。

通过虚拟文件FS=','FS='[ \t]+'来切换文件之间的字段分隔符,这意味着我们实际的文件具有2和4的argind。


嗨amaurea,命令的效果不如预期-我在问题的末尾放置了您的awk命令的输出。 - AndraD
@Alexandra:请编辑您的问题,包括您所需的输出,考虑到您已经包含了2个示例文件。否则,您正在要求人们花费时间猜测您需要什么。此外,这种类型的问题(使用不同的术语)每周出现2-3次。您是在上课并且这是一项任务吗?祝你好运。 - shellter
@shellter - 这是与工作相关的。如果您能找到一个特定的问题来解决所描述的问题,请随意引用它。 - AndraD
@Alexandra:我已经更新了我的答案,提供了一个新版本来复现你的输出结果。之前我误解了你想要做什么,认为B中每行只有一列与A中的每行对应,而且只需要从每行中提取一列。新版本会从B中收集列号,然后从A的每一行中提取所有这些列。 - amaurea

1

另一种方法:在文件B上应用一个awk脚本,该脚本生成另一个awk脚本,然后应用于文件A。

#!/bin/bash
awk -F, 'NR>1{a=a",$"$9}END{print"{print $1,$2"a"}"}' < fileB > cols.awk
awk -f cols.awk fileA

或者作为一行代码,不需要中间文件:

#!/bin/bash
awk "$(awk -F, 'NR>1{a=a",$"$9}END{print"{print $1,$2"a"}"}' < fileB)" fileA

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接