我有一个大文件,我需要从File1中检索特定的列,如果与file2的列表匹配,则它是〜200000行和〜1000列。 (比B更喜欢Bash) 例如我的虚拟数据文件如下,
file1
gene s1 s2 s3 s4 s5
a 1 2 1 2 1
b 2 3 2 3 3
c 1 1 1 1 1
d 1 1 2 2 2
和file2
sample
s4
s3
s7
s8
我想要的输出是
gene s3 s4
a 1 2
b 2 3
c 1 1
d 2 2
同样,我有3个不同的file2,并且我必须从同一file1中选择不同的样本到一个新文件中。 如果你们能为我提供宝贵的建议,我将非常感激
P.S:我是生物学家,我几乎没有编码经验
致谢
Ateeq
答案 0 :(得分:1)
$ cat file1
gene s1 s2 s3 s4 s5
a 1 2 1 2 1
b 2 3 2 3 3
c 1 1 1 1 1
d 1 1 2 2 2
$ cat file2
gene
s4
s3
s8
s7
$ cat a
awk '
NR == FNR {
columns[ NR ] = $0
printf "%s\t", $0
next
}
FNR == 1 {
print ""
split( $0, headers )
for (x = 1 ; x <= length(headers) ; x++ )
{
aheaders[ headers[x]] = x
}
next
}
{
for ( x = 1 ; x <= length( columns ) ; x++ )
{
if (length( aheaders[ columns[x] ] ) == 0 )
printf "N/A\t"
else
printf "%s\t" , $aheaders[ columns[x] ]
}
print ""
}
' $*
$ ./a file2 file1 | column -t
gene s4 s3 s8 s7
a 2 1 N/A N/A
b 3 2 N/A N/A
c 1 1 N/A N/A
d 2 2 N/A N/A
以上内容将助您一臂之力。这是一个非常乐观的程序,没有进行负面测试。
Awk是一种工具,它将一组命令应用于与表达式匹配的每个文件的每一行。通常,awk脚本的格式为:
<pattern> <command>
上面有三对这样的对。每个人都需要一点解释:
NR == FNR {
columns[ NR ] = $0
printf "%s\t", $0
next
}
NR == FNR是一种怪异的行为。 NR是记录号,FNR是当前文件中的记录号。 NR始终在增加,但是当awk解析下一个文件时,FNR重置为1。 NR == FNR是一个惯用法,仅在解析第一个文件时才适用。
我设计了awk程序来首先读取column文件(您正在调用此file2)。 File2具有要输出的列的列表。如您所见,我们将第一个文件(file2)中的每一行存储到称为列的数组中。阅读时,我们还将列打印出来。为了避免在每个列名后添加换行符(因为我们希望所有列标题都在同一行上),我们使用printf,它不输出换行符(与之相对的print则不行)。
该节末尾的“下一个”告诉awk读取文件中的下一行,而不处理其他任何节。毕竟,我们只想读取第一个文件。
总而言之,第一个节会记住列名(和顺序),并将它们打印在一行上(没有换行符)。
第二个“节”:
FNR == 1 {
print ""
split( $0, headers )
for (x = 1 ; x <= length(headers) ; x++ )
{
aheaders[ headers[x]] = x
}
next
}
FNR == 1将在任何文件的第一行匹配。由于上一个节中的下一个节,我们只有在第二个文件(file1)的第一行时才打此节。第一个打印的“”语句添加了第一个节中缺少的换行符。现在,带有列标题的行已完成。
split命令采用第一个参数$ 0,即当前行,并根据空格对其进行分割。我们知道当前行是第一行,并且其中包含列标题。 split命令将写入第二个参数标题中命名的数组。现在headers [1] =“ gene”和headers [2] =“ s4”,headers [3] =“ s3”,等等。
我们需要将列名映射到列号。代码的下一位采用每个标头值并创建一个超前条目。 aheders是一个关联数组,它将列标题名称映射到列号。
aheaders["gene"] = 1
aheaders["s1"] = 2
aheaders["s2"] = 3
aheaders["s3"] = 4
aheaders["s4"] = 5
aheaders["s5"] = 6
当我们完成了转发数组的创建后,下一条命令告诉awk跳至输入的下一行。从这一点开始,只有第三个节将具有真实条件。
{
for ( x = 1 ; x <= length( columns ) ; x++ )
{
if (length( aheaders[ columns[x] ] ) == 0 )
printf "N/A\t"
else
printf "%s\t" , $aheaders[ columns[x] ]
}
print ""
}
第三个节没有明确的。 Awk将一如既往地处理此问题。因此,对第二个文件的每一行都执行最后一个。
在这一点上,我们想打印在columns数组中指定的列。我们按顺序遍历数组的每个元素。第一次通过循环,columns [1] =“ gene_symbol”。这给了我们:
printf "%s\t" , $aheaders[ "gene" ]
而且由于agosters [“ gene”] = 1,我们得到了:
printf "%s\t" , $1
awk将$ 1理解为输入行中的第一个字段(或列)。因此,第一列传递给printf,后者输出带有附加制表符(\ t)的值。
然后,循环使用x = 2和column [2] =“ s4”再次执行一次。这将导致以下打印执行:
printf "%s\t" , $5
这将打印第五列,后跟一个选项卡。下一次迭代:
columns[3] = "s3"
aheaders["s3"] = 4
这将导致:
printf "%s\t" , $4
即,输出第四个字段。
下一次迭代我们遇到了失败情况:
columns[4] = "s8"
aheaders["s8"] = ''
在这种情况下,length(fronters [columns [x]])== 0为true,所以我们只打印出一个占位符-告诉操作员输入的内容可能无效:
printf "N/A\t"
当处理最后一列[x]值“ s7”时,输出相同。
现在,由于列中没有更多条目,因此存在循环,我们点击了最终打印:
print ""
提供空字符串以进行打印,因为print本身默认情况下将打印$ 0-整行。
这时,awk再次从file1中读取下一行,命中了第三个块(仅)。因此,awk一直持续到完全读取第二个文件为止。