【问题标题】:Why using awk in bash like vlookup in Excel give empty output file?为什么在 bash 中使用 awk,如 Excel 中的 vlookup 会给出空输出文件?
【发布时间】:2019-05-10 00:11:06
【问题描述】:

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-8CRLF。还有,

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

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

【问题讨论】:

  • 您提到您的 2 个文件以 CRLF 结尾。在使用 UNIX 工具(例如 awk 或加入它们)之前,您应该运行 dos2unix 或类似工具以避免任何问题。

标签: join awk vlookup


【解决方案1】:

Awk 经典。将 数千行 基因文件散列到一个哈希 (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

【讨论】:

  • 感谢您的回答!不幸的是,我的输出仍然是空白的。我在 2 个文件之后添加了一个“> output.txt”。也许这是有问题的?或者我可能需要在“打印”之后添加一些东西?
  • 不,这就是你所需要的。如果输出为空白,则第二个文件中的第一个字段都不存在于第一个文件中。检查任一文件中是否有空格,而不仅仅是字段之间的制表符。
  • 感谢您在我的帖子@EdMorton 中的评论,现在这项工作很好!谢谢 !另外,我看到这个 awk 命令删除了重复的“基因”,我没有例外,但没关系!
  • 哪个文件有重复?如果重复丢失,可能是genes 文件?但这不会有任何区别吗?
  • 修复换行符的另一种方法是将{sub(/\r$/,"")} 添加到脚本的开头。这从\r\n 行尾中删除了\r。使用 GNU awk 我通常设置RS="\r?\n"
【解决方案2】:

你的代码:

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}的简写

【讨论】:

    【解决方案3】:

    盒子里有一个比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 中的行的第一列出现在其中。在我的测试中,这比其他方法快很多。

    【讨论】:

    • 不知道为什么你会认为 join + cut + 2 sorts + bashisms + 管道会比简单的awk -F, 'FNR==NR{a[$1]; next} $1 in a' 更好。那个 grep 需要更多的工作才能变得健壮,正如你所写的那样,你会得到错误的匹配,例如一个以i17 结尾的键与一个以i1 结尾的键匹配。
    • @EdMorton 如果 OP 问题中的示例输入是典型的,那么 grep 方法不会出现问题。如果这不是问题,它几乎可以立即在我的测试数据上运行,所选文件中有 2000 行,转录文件中有 400 万行。 join 和 awk 方法彼此相差几秒钟(有趣的是,提前对文件进行排序不会产生任何重大影响;排序和连接可以在不同的核心上并行完成)。在这两者中,我将使用一个工具来完成它的设计目的。
    • 至于“bashisms”...好吧,OP 标记了问题bash。它们是公平的游戏。
    • 它们都是公平的游戏,但是有很多移动部件,它是不可移植的,而且你正在运行sort,可能不太重要的是,join 在一个文件上运行OP 告诉我们有数十亿行,而不仅仅是 400 万行!
    猜你喜欢
    • 2020-12-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-24
    相关资源
    最近更新 更多