为什么在bash中使用awk(如Excel中的vlookup)会给出空的输出文件?

时间:2019-05-09 13:26:33

标签: join awk vlookup

awk的良好用法对我来说还不清楚,但是我知道它将对我想要的东西有用。

我有两个文件,两个文件都用制表符分隔:

transcriptome.txt(十亿行):

 >TRINITY_DN261_c0_g1_i1    GATATTTATCCGAATATTCAATATGAT
 >TRINITY_DN299_c0_g1_i1    GGACACGGGCCTCAAGCCAAGTCAAAACCACCACAAAG
 >TRINITY_DN216_c0_g1_i1    GTTCAATATTCAATGACTGAAGGGCCCGCTGATTTTCCCCTATAAA
 >TRINITY_DN220_c0_g1_i1    GGGAGATAATAACAATGATAACACACAAAATTCCAATG

selected_genes.txt(数千行):

 >TRINITY_DN261_c0_g1_i1    1
 >TRINITY_DN220_c0_g1_i1    0

我想要此输出(selected_genes.txt的第一列和transcriptome.txt的第二列):

 >TRINITY_DN261_c0_g1_i1    GATATTTATCCGAATATTCAATATGAT
 >TRINITY_DN220_c0_g1_i1    GGGAGATAATAACAATGATAACACACAAAATTCCAATG

通常,我在Excel中使用vlookup函数。 我尝试使用awk来获得结果,就像在许多线程(stackexchange1stackexchange2stackoverflow1stackoverflow2stackoverflow3等中一样..)

因此,我尝试使用这些线程中的建议,但是我的输出要么为空,要么仅为我的selected_genes.txt文件的副本。

我检查了一下,我的2个文件都位于UTF-8中,并带有CRLF。还有,

awk '{print $1}' `transcriptome.txt`
awk '{print $1}' `selected_genes.txt`

请把文件的第一栏给我,所以问题不是出在他们身上。

这是我尝试过的:

awk -F, 'FNR==NR {a[$1]=$1; next}; $1 in a {print a[$2]}' selected_genes.txt transcriptome.txt > output.txt
# Blank result

awk -F 'FNR==NR{var[$1]=$1;next;}{print var[$1]FS$2}' selected_genes.txt transcriptome.txt > output.txt
# Blank result

awk 'NR == FNR{a[$1] = $2;next}; {print $1, $1 in a?a[$1]: "NA"}' selected_genes.txt transcriptome.txt > output.txt
# Print only transcriptome.txt with first column and NAs

awk -F, 'FNR==NR{var[$1]=$1}FNR!=NR{print(var[$2]","$1)}' selected_genes.txt transcriptome.txt > output.txt
# Print only selected_genes.txt

我没有产生想要的输出。 任何解释我的代码问题的建议将不胜感激。

3 个答案:

答案 0 :(得分:2)

经典之作。将数千行基因文件散列为散列(a),以免浪费数十亿行转录组文件中的所有内存和查找$1

$ awk '
    # { sub(/\r$/,"") }    # uncomment to remove Windows style line-endings.
    NR==FNR{a[$1]          # hash $1 of genes file to a
    next
}
($1 in a) {                # lookup from transcriptome
    print
}' genes transcriptome     # mind the order
>TRINITY_DN261_c0_g1_i1    GATATTTATCCGAATATTCAATATGAT
>TRINITY_DN220_c0_g1_i1    GGGAGATAATAACAATGATAACACACAAAATTCCAATG

答案 1 :(得分:0)

在框中,有一种比awk更好的工具,可以在公共字段上进行这种文件合并,尤其是对于大文件:join(1)

$ join -t $'\t' -11 -21 -o 0,2.2 \
   <(sort -t $'\t' -k1,1 selected_genes.txt) \
   <(sort -t $'\t' -k1,1 transcriptome.txt)
>TRINITY_DN220_c0_g1_i1 GGGAGATAATAACAATGATAACACACAAAATTCCAATG
>TRINITY_DN261_c0_g1_i1 GATATTTATCCGAATATTCAATATGAT

唯一的警告是要连接的文件必须在连接列上排序,因此使用sort

就数据库而言,它对两个文件执行INNER JOIN -对于第一个文件的每一行,第二个文件的每一行具有匹配的连接列,将产生一行输出。 -o 0,2.2使这些行成为第二个文件的连接列和第二列。


另一个有趣的选择:

$ grep -F -f <(sed -e 's/[^\t]*$//' selected_genes.txt) transcriptome.txt 
>TRINITY_DN261_c0_g1_i1 GATATTTATCCGAATATTCAATATGAT
>TRINITY_DN220_c0_g1_i1 GGGAGATAATAACAATGATAACACACAAAATTCCAATG

very efficiently仅显示transcriptome.txt中一行中第一行出现的selected_genes.txt中的行。在我的测试中,这比其他方法要快得多。

答案 2 :(得分:0)

您的代码:

awk -F, 'FNR==NR{a[$1]=$1; next}; $1 in a {print a[$2]}' 

将不起作用,因为您正在尝试打印不存在的a[$2]

更改为

awk -F, 'FNR==NR{a[$1]; next} $1 in a' selected_genes.txt transcriptome.txt 

应该会给您预期的输出

第二个表达式是($1 in a) {print $0}的缩写